diff --git a/.github/workflows/build_and_run_tests.yml b/.github/workflows/build_and_run_tests.yml index 9ec8a69..3055a00 100644 --- a/.github/workflows/build_and_run_tests.yml +++ b/.github/workflows/build_and_run_tests.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout the commit uses: actions/checkout@v4 - + # - name: check gcc version # run: gcc --version @@ -45,13 +45,17 @@ jobs: fi sed -i 's/\${CXX}/g++/' run_unittest.sh ./run_unittest.sh - - - name: Build and Run Standalone + + - name: Build and Run CFE run: | + git clone https://github.com/NOAA-OWP/cfe extern/cfe + if [ -d build ]; then + rm -rf build + fi mkdir build && cd build - cmake ../ -DSTANDALONE=ON + cmake ../ -DCFE=ON make && cd .. - ./run_smp.sh STANDALONE + ./run_smp.sh CFE - name: Build and Run with Topmodel run: | @@ -60,6 +64,6 @@ jobs: rm -rf build fi mkdir build && cd build - cmake ../ -DWITHTOPMODEL=ON + cmake ../ -DTOPMODEL=ON make && cd .. - ./run_smp.sh WITHTOPMODEL + ./run_smp.sh TOPMODEL diff --git a/.github/workflows/ngen_integration.yaml b/.github/workflows/ngen_integration.yaml index 85184dc..eedf2ec 100644 --- a/.github/workflows/ngen_integration.yaml +++ b/.github/workflows/ngen_integration.yaml @@ -51,22 +51,32 @@ jobs: with: repository: noaa-owp/ngen + - name: Build CFE + uses: actions/checkout@v4 + with: + repository: noaa-owp/cfe + + - name: Build CFE Library + run: | + cmake -B cmake_build -S . -DNGEN=ON + make -C cmake_build + - name: Build Topmodel - id: submod_build_4 + id: submod_build_2 uses: ./.github/actions/ngen-submod-build with: mod-dir: "extern/topmodel/" targets: "topmodelbmi" - name: Build PET - id: submod_build_5 + id: submod_build_3 uses: ./.github/actions/ngen-submod-build with: mod-dir: "extern/evapotranspiration/evapotranspiration" targets: "petbmi" - name: Build SLoTH - id: submod_build_6 + id: submod_build_4 uses: ./.github/actions/ngen-submod-build with: mod-dir: "extern/sloth/" @@ -91,7 +101,7 @@ jobs: - name: Run Ngen Test for SMP run: | mv ${{ steps.ngen_id1.outputs.build-dir }} ./ngen-build/ - inputfile='extern/SoilMoistureProfiles/SoilMoistureProfiles/realizations/realization_config_smp.json' + inputfile='extern/SoilMoistureProfiles/SoilMoistureProfiles/realizations/realization_config_smp_cfe.json' ./ngen-build/ngen ./data/catchment_data.geojson "cat-27" ./data/nexus_data.geojson "nex-26" $inputfile - name: Run Ngen Test with Topmodel diff --git a/CMakeLists.txt b/CMakeLists.txt index 87f8f92..c0eb8cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,26 +10,23 @@ set(CMAKE_BUILD_TYPE Debug) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_C_COMPILER $ENV{CC}) -set(CMAKE_CXX_COMPILER $ENV{CXX}) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") project(smpbmi VERSION 1.0.0 DESCRIPTION "OWP SMP BMI Module Shared Library") option(NGEN "NGEN" OFF) -option(STANDALONE "STANDALONE" OFF) -option(WITHTOPMODEL "WITHTOPMODEL" OFF) +option(CFE "CFE" OFF) +option(TOPMODEL "TOPMODEL" OFF) if(NGEN) add_definitions(-DNGEN) endif() -if(STANDALONE) -message("${Red} Standalone Soil moisture profile (using conceptual/layered reservoir)! ${ColourReset}") -set(exe_name "smp_standalone") -elseif(WITHTOPMODEL) +if(CFE) +message("${Red} Soil moisture profile for CFE! ${ColourReset}") +set(exe_name "smp_cfe") +elseif(TOPMODEL) message("${Red} Soil moisture profile for topmodel (watertable-based)! ${ColourReset}") set(exe_name "smp_topmodel") endif() @@ -43,19 +40,22 @@ set(SMP_LIB_DESC_CMAKE "OWP SMP BMI Module Shared Library") add_compile_definitions(BMI_ACTIVE) # add the executable -if(STANDALONE) -add_executable(${exe_name} ./src/main_soil_moisture.cxx ./src/bmi_soil_moisture_profile.cxx src/soil_moisture_profile.cxx) -elseif(WITHTOPMODEL) -add_executable(${exe_name} ./src/main_smp_topmodel.cxx ./extern/topmodel/src/topmodel.c ./extern/topmodel/src/bmi_topmodel.c) - -add_library(smplib ./src/bmi_soil_moisture_profile.cxx ./src/soil_moisture_profile.cxx ./include/bmi_soil_moisture_profile.hxx ./include/soil_moisture_profile.hxx) -target_link_libraries(${exe_name} LINK_PUBLIC smplib) -target_include_directories(${exe_name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extern/) +if(CFE) + add_executable(${exe_name} ./src/main_smp_cfe.cxx ./extern/cfe/src/cfe.c ./extern/cfe/src/bmi_cfe.c + ./extern/cfe/src/giuh.c ./extern/cfe/src/conceptual_reservoir.c ./extern/cfe/src/nash_cascade.c) + target_include_directories(${exe_name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extern/cfe/include) +elseif(TOPMODEL) + add_executable(${exe_name} ./src/main_smp_topmodel.cxx ./extern/topmodel/src/topmodel.c ./extern/topmodel/src/bmi_topmodel.c) + target_include_directories(${exe_name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extern/topmodel/include) endif() -if(WITHTOPMODEL OR STANDALONE) -target_link_libraries(${exe_name} PRIVATE m) +if(TOPMODEL OR CFE) + add_library(smplib ./src/bmi_soil_moisture_profile.cxx ./src/soil_moisture_profile.cxx ./include/bmi_soil_moisture_profile.hxx + ./include/soil_moisture_profile.hxx) + target_link_libraries(${exe_name} LINK_PUBLIC smplib) + target_include_directories(${exe_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + target_link_libraries(${exe_name} PRIVATE m) endif() diff --git a/INSTALL.md b/INSTALL.md index ced76db..1fa3f5b 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,40 +1,41 @@ # Installation instructions -Detailed instructions on how to build and run SoilMoistureProfiles for different setups/modes (standalone, with topmodel, and nextgen framework) are provided below. The schemes assume you have [GCC](https://gcc.gnu.org) and [CMAKE](https://cmake.org/) on your machine. These are simple examples providing a one-way coupling (model --> SoilMoistureProfiles) and are only for demonstration purposes. However, in real examples when coupled to other models, the soil moisture profiles effect freeze-thaw and other hydrological processes (such as infiltration). +Detailed instructions on how to build and run SoilMoistureProfiles for different setups/modes (pseudo framework with a conceptual soil reservoir, pseudo framework with soil moisture deficit, and nextgen framework) are provided below. The schemes assume you have [GCC](https://gcc.gnu.org) and [CMAKE](https://cmake.org/) on your machine. These are simple examples providing a one-way coupling (model --> SoilMoistureProfiles) and are only for demonstration purposes. However, in real examples when coupled to other models, the soil moisture profiles effect freeze-thaw and other hydrological processes (such as infiltration). **Note**: Before running the following examples, it is recommended to run the unittests [tests](https://github.com/NOAA-OWP/SoilMoistureProfiles/tree/ajk/doc_update/tests). -## Standalone example -The example runs SMP for a hypothetical conceptual soil reservoir (with prescribed soil_storage and soil_storage_change SMP BMI input variables) to compute `watertable` and `soil_moisture_profile` and compare the results against benchmark results. The hypothetical conceptual soil reservoir mimics CFE. +## Pseudo framework example (CFE) +This example couples SMP to the [Conceptual Functional Equivalent (CFE)](https://github.com/NOAA-OWP/cfe) model which uses a conceptual soil reservoir. SMP is used to compute `watertable` and `soil_moisture_profile`. ### Build ``` git clone https://github.com/NOAA-OWP/SoilMoistureProfiles && cd SoilMoistureProfiles + git clone https://github.com/NOAA-OWP/cfe extern/cfe mkdir build && cd build - cmake ../ -DSTANDALONE=ON + cmake ../ -DCFE=ON make && cd .. ``` ### Run
-Run: ./run_smp.sh STANDALONE (from SoilMoistureProfiles directory)    
+Run: ./run_smp.sh CFE (from SoilMoistureProfiles directory)
 
-## Pseudo framework example -An example coupling TopModel to SMP to compute `watertable` and `soil_moisture_profile` from soil moisture deficit. +## Pseudo framework example (TopModel) +This example couples [TopModel](https://github.com/NOAA-OWP/topmodel), which tracks soil moisture deficit, to SMP. SMP is used to compute `watertable` and the `soil_moisture_profile` from the soil moisture deficit. ### Build ``` - git clone https://github.com/NOAA-OWP/SoilMoistureProfiles && cd SoilMoistureProfiles + git clone https://github.com/NOAA-OWP/SoilMoistureProfiles && cd SoilMoistureProfiles git clone https://github.com/NOAA-OWP/topmodel extern/topmodel mkdir build && cd build - cmake ../ -DWITHTOPMODEL=ON + cmake ../ -DTOPMODEL=ON make && cd .. ``` ### Run
-Run:  ./run_smp.sh WITHTOPMODEL (from SoilMoistureProfiles directory)    
+Run:  ./run_smp.sh TOPMODEL (from SoilMoistureProfiles directory)
 
## Nextgen framework example -Detailed instructions for running and building SoilMoistureProfiles coupled to other models (for instance, CFE or SFT) in the nextgen framework are provided at [instructions](https://github.com/NOAA-OWP/SoilFreezeThaw/blob/master/INSTALL.md). +The [nextgen framework](https://github.com/NOAA-OWP/ngen) allows the user to easily couple different BMI enabled models, including SMP. Detailed instructions for running and building SoilMoistureProfiles coupled to other models (for instance, CFE or [SoilFreezeThaw](https://github.com/NOAA-OWP/SoilFreezeThaw)) in the nextgen framework are provided at [instructions](https://github.com/NOAA-OWP/SoilFreezeThaw/blob/master/INSTALL.md). Once nextgen is built, refer to the commands below to run the SMP examples in nextgen. ### Build ``` mkdir smp && cd smp (in the nextgen directory) @@ -44,12 +45,12 @@ ln -s extern/SoilMoistureProfiles/SoilMoistureProfiles/realizations cp extern/topmodel/topmodel/data/* data ``` ### Run - - Standalone example + - SMP with CFE example ``` ../cmake_build/ngen data/catchment_data.geojson cat-27 data/nexus_data.geojson nex-26 realizations/realization_config_smp.json ``` - - With Topmodel example + - SMP with Topmodel example ``` cp ./extern/topmodel/topmodel/data/* data ../cmake_build/ngen data/catchment_data.geojson cat-27 data/nexus_data.geojson nex-26 realizations/realization_config_smp_topmodel.json - ``` + ``` diff --git a/README.md b/README.md index 3c33f52..086d3bf 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,35 @@ # SoilMoistureProfiles -The soil moisture profiles schemes provide soil moisture distributed over a one-dimensional vertical column. These schemes facilitate coupling among different models such as (CFE and SFT or LASAM and SFT). The following three schemes are provided here to compute vertical soil moisture profiles. - * Scheme for conceptual soil reservoirs (e.g., **[CFE](https://github.com/NOAA-OWP/cfe))** +The soil moisture profiles schemes provide soil moisture distributed over a one-dimensional vertical column and depth to water table. These schemes facilitate coupling among hydrological and thermal models such as (CFE and SFT or LASAM and SFT). It can also be coupled with conceptual hydrologic models to compute rootzone-based actual evapotranspiration. The following three schemes are provided here to compute vertical soil moisture profiles and water table depth. + * Scheme for conceptual soil reservoirs (e.g., **[CFE](https://github.com/NOAA-OWP/cfe))** * Schemes for layered soil reservoirs (e.g., **[LGAR](https://github.com/NOAA-OWP/LGAR-C))** * Schemes for topmodel (details are provided below; **[TopModel](https://github.com/NOAA-OWP/topmodel))** - + ## Conceptual soil reservoir -For conceptual reservoirs, see the following schematic and algorithm. We use the Clap-Hornberger soil moisture characteristic function here, and **soil moisture storage** is the main input passed through a BMI. - +For conceptual reservoirs, see the following schematic and algorithm. We use the Clap-Hornberger soil moisture characteristic function here, and **soil_storage** and **soil_storage_chage** are the two inputs passed through BMI. + ![smp_schematic](https://user-images.githubusercontent.com/15165757/164322224-479477d7-2275-4ce3-a00b-9270cc0d3201.png) - + ## Layered soil reservoir -For layered soil reservoirs, the two options include +For layered soil reservoirs, the two options include * constant by layer, and Clap-Horngerger soil moisture characteristic function for the profile below the depth of the last layer * linearly interpolated profile between consecutive layers, and Clap-Horngerger soil moisture characteristic function for the profile below the depth of the last layer - + ## Topmodel based soil reservoir * (flux-based method) A method using an iterative scheme to first compute watertable depth and then soil moisture profile ([Blazkova et al. (2002)](https://agupubs.onlinelibrary.wiley.com/doi/full/10.1029/2001WR000912)) * (deficit-based method) A method using catchment deficit to first compute watertable depth and then soil moisture profile ([Franchini et al. (1996)](https://www.sciencedirect.com/science/article/abs/pii/S0022169496800151) ## Build and Run Instructions -Detailed instructions on how to build and run SoilMoistureProfiels (SMP) can be found here [INSTALL](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/ajk/doc_update/INSTALL.md). +Detailed instructions on how to build and run SoilMoistureProfiels (SMP) can be found here [INSTALL](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/main/INSTALL.md). - Test examples highlights - - Unittest: (see [tests](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/ajk/doc_update/tests/README.md)) - - Standalone: An example computing `watertable` and `soil_moisture_profile` using a soil conceptual reservoir (see [build/run](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/ajk/doc_update/INSTALL.md#standalone-example)) - - With topmodel: An example coupling TopModel to SMP (Soil Moisture Profiles) to compute `watertable` and `soil_moisture_profile` (see [build/run](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/ajk/doc_update/INSTALL.md#pseudo-framework-example)) - - Nextgen examples: Realization files for the two above examples (Standalone and with topmodel) are provided [here](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/ajk/doc_update/realizations) (see [build/run](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/ajk/doc_update/INSTALL.md#nextgen-framework-example)). + - Unittest: (see [tests](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/main/tests/README.md)) + - With CFE: An example coupling CFE with SMP (Soil Moisture Profiles) to compute `watertable` and `soil_moisture_profile` (see [build/run](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/main/INSTALL.mdd#pseudo-framework-example-1)) + - With topmodel: An example coupling TopModel to SMP (Soil Moisture Profiles) to compute `watertable` and `soil_moisture_profile` (see [build/run](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/main/INSTALL.md#pseudo-framework-example-2)) + - Nextgen examples: Realization files for the two above examples (Standalone and with topmodel) are provided [here](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/main/realizations) (see [build/run](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/main/INSTALL.md#nextgen-framework-example)). ## Model Configuration File -Detailed description of the parameters for model configuration is provided ([here](https://github.com/NOAA-OWP/SoilMoistureProfiles/tree/ajk/doc_update/configs/README.md)) - +Detailed description of the parameters for model configuration is provided ([here](https://github.com/NOAA-OWP/SoilMoistureProfiles/tree/main/configs/README.md)) + ## Getting help For questions, please contact Ahmad Jan (ahmad.jan(at)noaa.gov), the main developer/maintainer of the repository. @@ -37,10 +37,4 @@ For questions, please contact Ahmad Jan (ahmad.jan(at)noaa.gov), the main develo We are constantly looking to improve the model and/or fix bugs as they arise. Please see the Git Issues for known issues or if you want to suggest adding a capability or to report a bug, please open an issue. ## Getting involved -See general instructions to contribute to the model development ([instructions](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/ajk/doc_update/CONTRIBUTING.md)) or simply fork the repository and submit a pull request. - - - - - - +See general instructions to contribute to the model development ([instructions](https://github.com/NOAA-OWP/SoilMoistureProfiles/blob/main/CONTRIBUTING.md)) or simply fork the repository and submit a pull request. diff --git a/configs/config_cfe.txt b/configs/config_cfe.txt new file mode 100644 index 0000000..ff5c14c --- /dev/null +++ b/configs/config_cfe.txt @@ -0,0 +1,32 @@ +forcing_file=./extern/cfe/forcings/cat87_01Dec2015.csv +soil_params.depth=2.0[m] +soil_params.b=4.05[] +soil_params.satdk=0.00000338[m s-1] +soil_params.satpsi=0.355[m] +soil_params.slop=0.01[m/m] +soil_params.smcmax=0.439[m/m] +soil_params.wltsmc=0.066[m/m] +soil_params.expon=1.0[] +soil_params.expon_secondary=1.0[] +max_gw_storage=0.25[m] +Cgw=1.8e-05[m h-1] +expon=6.0[] +gw_storage=0.125[m/m] +alpha_fc=0.33[] +soil_storage=0.585626[m/m] +K_nash=0.03[] +K_lf=0.01[] +nash_storage=0.0,0.0 +giuh_ordinates=0.06,0.51,0.28,0.12,0.03 +num_timesteps=1 +verbosity=1 +surface_runoff_scheme=GIUH +surface_partitioning_scheme=Xinanjiang +a_Xinanjiang_inflection_point_parameter=1 +b_Xinanjiang_shape_parameter=1 +x_Xinanjiang_shape_parameter=1 +urban_decimal_fraction=0.0 +DEBUG=0 +#surface_partitioning_scheme=Schaake +#ice_fraction=0 +#ice_content_threshold=0.15 diff --git a/realizations/realization_config_smp.json b/realizations/realization_config_smp.json deleted file mode 100644 index bce2dc9..0000000 --- a/realizations/realization_config_smp.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "time": { - "start_time": "2015-12-01 00:00:00", - "end_time": "2015-12-01 03:00:00", - "output_interval": 3600 - }, - "catchments": { - "cat-27": { - "formulations": [ - { - "name": "bmi_multi", - "params": { - "model_type_name": "bmi_multi_sloth_smp", - "forcing_file": "", - "init_config": "", - "allow_exceed_end_time": true, - "main_output_variable": "soil_storage", - "output_variables" : [ - "soil_water_table" - ], - "modules": [ - { - "name": "bmi_c++", - "params": { - "model_type_name": "bmi_c++_sloth", - "library_file": "./extern/sloth/cmake_build/libslothmodel", - "init_config": "/dev/null", - "allow_exceed_end_time": true, - "main_output_variable": "z", - "uses_forcing_file": false, - "model_params": { - "sloth_SOIL_STORAGE(1,double,m,node)": 0.8, - "sloth_SOIL_STORAGE_CHANGE(1,double,m,node)": -0.000472, - "soil_moisture_wetting_fronts(1,double,1,node)": 0.0, - "soil_depth_wetting_fronts(1,double,1,node)": 0.0, - "num_wetting_fronts(1,int,1,node)": 1, - "Qb_topmodel(1,double,1,node)": 0.0, - "Qv_topmodel(1,double,1,node)": 0.0, - "global_deficit(1,double,1,node)": 0.0 - } - } - }, - { - "name": "bmi_c++", - "params": { - "model_type_name": "bmi_smp", - "library_file": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/cmake_build/libsmpbmi", - "init_config": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/configs/config_conceptual.txt", - "allow_exceed_end_time": true, - "main_output_variable": "soil_storage", - "variables_names_map" : { - "soil_storage" : "sloth_SOIL_STORAGE", - "soil_storage_change" : "sloth_SOIL_STORAGE_CHANGE" - }, - "output_variables" : [ - "soil_storage", - "soil_water_table" - ], - "uses_forcing_file": false - } - } - ], - "uses_forcing_file": false - } - } - ], - "forcing": { - "path": "./extern/cfe/cfe/forcings/cat87_01Dec2015-.csv", - "provider": "CsvPerFeature" - } - } - } -} diff --git a/realizations/realization_config_smp_cfe.json b/realizations/realization_config_smp_cfe.json new file mode 100644 index 0000000..005a47e --- /dev/null +++ b/realizations/realization_config_smp_cfe.json @@ -0,0 +1,120 @@ +{ + "time": { + "start_time": "2015-12-01 00:00:00", + "end_time": "2015-12-01 03:00:00", + "output_interval": 3600 + }, + "catchments": { + "cat-27": { + "formulations": [ + { + "name": "bmi_multi", + "params": { + "model_type_name": "bmi_multi_sloth_smp", + "forcing_file": "", + "init_config": "", + "allow_exceed_end_time": true, + "main_output_variable": "soil_storage", + "output_variables" : [ + "soil_water_table" + ], + "modules": [ + { + "name": "bmi_c++", + "params": { + "model_type_name": "bmi_c++_sloth", + "library_file": "./extern/sloth/cmake_build/libslothmodel", + "init_config": "/dev/null", + "allow_exceed_end_time": true, + "main_output_variable": "z", + "uses_forcing_file": false, + "model_params": { + "sloth_ice_fraction_schaake(1,double,m,node)": 0.0, + "sloth_ice_fraction_xinanjiang(1,double,1,node)": 0.0, + "sloth_smp(1,double,1,node)": 0.0, + "sloth_soil_moisture_wetting_fronts(1,double,1,node)": 0.0, + "sloth_soil_depth_wetting_fronts(1,double,1,node)": 0.0, + "sloth_num_wetting_fronts(1,int,1,node)": 1, + "sloth_Qb_topmodel(1,double,1,node)": 0.0, + "sloth_Qv_topmodel(1,double,1,node)": 0.0, + "sloth_global_deficit(1,double,1,node)": 0.0 + } + } + }, + { + "name": "bmi_c", + "params": { + "model_type_name": "bmi_c_pet", + "library_file": "./extern/evapotranspiration/evapotranspiration/cmake_build/libpetbmi", + "forcing_file": "", + "init_config": "./extern/cfe/cfe/configs/cat_87_bmi_config_pet_pass.txt", + "allow_exceed_end_time": true, + "main_output_variable": "water_potential_evaporation_flux", + "registration_function":"register_bmi_pet", + "uses_forcing_file": false + } + }, + { + "name": "bmi_c", + "params": { + "model_type_name": "bmi_c_cfe", + "library_file": "./extern/cfe/cfe/cmake_build/libcfebmi", + "forcing_file": "", + "init_config": "./extern/cfe/cfe/configs/cat_87_bmi_config_cfe_pass.txt", + "allow_exceed_end_time": true, + "main_output_variable": "Q_OUT", + "registration_function": "register_bmi_cfe", + "variables_names_map": { + "water_potential_evaporation_flux" : "water_potential_evaporation_flux", + "atmosphere_water__liquid_equivalent_precipitation_rate" : "APCP_surface", + "atmosphere_air_water~vapor__relative_saturation" : "SPFH_2maboveground", + "land_surface_air__temperature" : "TMP_2maboveground", + "land_surface_wind__x_component_of_velocity" : "UGRD_10maboveground", + "land_surface_wind__y_component_of_velocity" : "VGRD_10maboveground", + "land_surface_radiation~incoming~longwave__energy_flux" : "DLWRF_surface", + "land_surface_radiation~incoming~shortwave__energy_flux" : "DSWRF_surface", + "land_surface_air__pressure" : "PRES_surface", + "ice_fraction_schaake" : "sloth_ice_fraction_schaake", + "ice_fraction_xinanjiang" : "sloth_ice_fraction_xinanjiang", + "soil_moisture_profile" : "sloth_smp" + }, + "uses_forcing_file": false + } + }, + { + "name": "bmi_c++", + "params": { + "model_type_name": "bmi_smp", + "library_file": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/cmake_build/libsmpbmi", + "init_config": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/configs/config_conceptual.txt", + "allow_exceed_end_time": true, + "main_output_variable": "soil_storage", + "variables_names_map" : { + "soil_storage" : "SOIL_STORAGE", + "soil_storage_change" : "SOIL_STORAGE_CHANGE", + "Qb_topmodel": "sloth_Qb_topmodel", + "Qv_topmodel": "sloth_Qv_topmodel", + "global_deficit" : "sloth_global_deficit", + "sloth_soil_moisture_wetting_fronts": "soil_moisture_wetting_fronts", + "soil_depth_wetting_fronts" : "sloth_soil_depth_wetting_fronts", + "num_wetting_fronts" : "sloth_num_wetting_fronts" + }, + "output_variables" : [ + "soil_storage", + "soil_water_table" + ], + "uses_forcing_file": false + } + } + ], + "uses_forcing_file": false + } + } + ], + "forcing": { + "path": "./extern/cfe/cfe/forcings/cat87_01Dec2015-.csv", + "provider": "CsvPerFeature" + } + } + } +} diff --git a/realizations/realization_config_smp_topmodel.json b/realizations/realization_config_smp_topmodel.json index 57febaf..3e87f6f 100644 --- a/realizations/realization_config_smp_topmodel.json +++ b/realizations/realization_config_smp_topmodel.json @@ -6,22 +6,22 @@ }, "catchments": { "cat-27": { - "formulations": [ - { + "formulations": [ + { "name": "bmi_multi", "params": { - "model_type_name": "bmi_multi_sloth_smp", - "forcing_file": "", - "init_config": "", - "allow_exceed_end_time": true, - "main_output_variable": "soil_storage", - "output_variables" : [ - "soil_water_table" - ], - "modules": [ - { - "name": "bmi_c++", - "params": { + "model_type_name": "bmi_multi_sloth_smp", + "forcing_file": "", + "init_config": "", + "allow_exceed_end_time": true, + "main_output_variable": "soil_storage", + "output_variables" : [ + "soil_water_table" + ], + "modules": [ + { + "name": "bmi_c++", + "params": { "model_type_name": "bmi_c++_sloth", "library_file": "./extern/sloth/cmake_build/libslothmodel", "init_config": "/dev/null", @@ -29,81 +29,81 @@ "main_output_variable": "z", "uses_forcing_file": false, "model_params": { - "sloth_SOIL_STORAGE(1,double,m,node)": 0.8, - "sloth_SOIL_STORAGE_CHANGE(1,double,m,node)": -0.000472, - "soil_moisture_wetting_fronts(1,double,1,node)": 0.0, - "soil_depth_wetting_fronts(1,double,1,node)": 0.0, - "num_wetting_fronts(1,int,1,node)": 1 + "sloth_SOIL_STORAGE(1,double,m,node)": 0.8, + "sloth_SOIL_STORAGE_CHANGE(1,double,m,node)": -0.000472, + "soil_moisture_wetting_fronts(1,double,1,node)": 0.0, + "soil_depth_wetting_fronts(1,double,1,node)": 0.0, + "num_wetting_fronts(1,int,1,node)": 1 } - } + } }, - { - "name": "bmi_c", - "params": { - "model_type_name": "bmi_c_pet", - "library_file": "./extern/evapotranspiration/evapotranspiration/cmake_build/libpetbmi", - "forcing_file": "", - "init_config": "./extern/cfe/cfe/configs/cat_87_bmi_config_pet_pass.txt", - "allow_exceed_end_time": true, - "main_output_variable": "water_potential_evaporation_flux", - "registration_function":"register_bmi_pet", - "uses_forcing_file": false - } - }, - { - "name": "bmi_c", - "params": { - "model_type_name": "bmi_c_topmodel", - "library_file": "./extern/topmodel/cmake_build/libtopmodelbmi", - "init_config": "./extern/topmodel/topmodel/data/topmod.run", - "allow_exceed_end_time": true, - "main_output_variable": "Qout", - "registration_function": "register_bmi_topmodel", - "variables_names_map" : { - "water_potential_evaporation_flux" : "water_potential_evaporation_flux", - "atmosphere_water__liquid_equivalent_precipitation_rate" : "APCP_surface", - "atmosphere_air_water~vapor__relative_saturation" : "SPFH_2maboveground", - "land_surface_air__temperature" : "TMP_2maboveground", - "land_surface_wind__x_component_of_velocity" : "UGRD_10maboveground", - "land_surface_wind__y_component_of_velocity" : "VGRD_10maboveground", - "land_surface_radiation~incoming~longwave__energy_flux" : "DLWRF_surface", - "land_surface_radiation~incoming~shortwave__energy_flux" : "DSWRF_surface", - "land_surface_air__pressure" : "PRES_surface" - }, - "uses_forcing_file": false - } - }, - { - "name": "bmi_c++", - "params": { - "model_type_name": "bmi_smp", - "library_file": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/cmake_build/libsmpbmi", - "init_config": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/configs/config_topmodel.txt", - "allow_exceed_end_time": true, - "main_output_variable": "soil_storage", - "variables_names_map" : { - "soil_storage" : "sloth_SOIL_STORAGE", - "soil_storage_change" : "sloth_SOIL_STORAGE_CHANGE", - "Qb_topmodel" : "land_surface_water__baseflow_volume_flux", - "Qv_topmodel" : "soil_water_root-zone_unsat-zone_top__recharge_volume_flux", - "global_deficit" : "soil_water__domain_volume_deficit" - }, - "output_variables" : [ - "soil_storage", - "soil_water_table" - ], - "uses_forcing_file": false - } - } - ], - "uses_forcing_file": false + { + "name": "bmi_c", + "params": { + "model_type_name": "bmi_c_pet", + "library_file": "./extern/evapotranspiration/evapotranspiration/cmake_build/libpetbmi", + "forcing_file": "", + "init_config": "./extern/cfe/cfe/configs/cat_87_bmi_config_pet_pass.txt", + "allow_exceed_end_time": true, + "main_output_variable": "water_potential_evaporation_flux", + "registration_function":"register_bmi_pet", + "uses_forcing_file": false + } + }, + { + "name": "bmi_c", + "params": { + "model_type_name": "bmi_c_topmodel", + "library_file": "./extern/topmodel/cmake_build/libtopmodelbmi", + "init_config": "./extern/topmodel/topmodel/data/topmod.run", + "allow_exceed_end_time": true, + "main_output_variable": "Qout", + "registration_function": "register_bmi_topmodel", + "variables_names_map" : { + "water_potential_evaporation_flux" : "water_potential_evaporation_flux", + "atmosphere_water__liquid_equivalent_precipitation_rate" : "APCP_surface", + "atmosphere_air_water~vapor__relative_saturation" : "SPFH_2maboveground", + "land_surface_air__temperature" : "TMP_2maboveground", + "land_surface_wind__x_component_of_velocity" : "UGRD_10maboveground", + "land_surface_wind__y_component_of_velocity" : "VGRD_10maboveground", + "land_surface_radiation~incoming~longwave__energy_flux" : "DLWRF_surface", + "land_surface_radiation~incoming~shortwave__energy_flux" : "DSWRF_surface", + "land_surface_air__pressure" : "PRES_surface" + }, + "uses_forcing_file": false + } + }, + { + "name": "bmi_c++", + "params": { + "model_type_name": "bmi_smp", + "library_file": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/cmake_build/libsmpbmi", + "init_config": "./extern/SoilMoistureProfiles/SoilMoistureProfiles/configs/config_topmodel.txt", + "allow_exceed_end_time": true, + "main_output_variable": "soil_storage", + "variables_names_map" : { + "soil_storage" : "sloth_SOIL_STORAGE", + "soil_storage_change" : "sloth_SOIL_STORAGE_CHANGE", + "Qb_topmodel" : "land_surface_water__baseflow_volume_flux", + "Qv_topmodel" : "soil_water_root-zone_unsat-zone_top__recharge_volume_flux", + "global_deficit" : "soil_water__domain_volume_deficit" + }, + "output_variables" : [ + "soil_storage", + "soil_water_table" + ], + "uses_forcing_file": false + } + } + ], + "uses_forcing_file": false } - } + } ], "forcing": { - "path": "./extern/cfe/cfe/forcings/cat87_01Dec2015-.csv", - "provider": "CsvPerFeature" + "path": "./extern/cfe/cfe/forcings/cat87_01Dec2015-.csv", + "provider": "CsvPerFeature" } - } + } } } diff --git a/run_smp.sh b/run_smp.sh index 6454c31..86b7cf1 100755 --- a/run_smp.sh +++ b/run_smp.sh @@ -7,7 +7,7 @@ if [ ! $# == 1 ]; then exit fi -if [ $flag == "STANDALONE" ] || [ "$flag" == "WITHTOPMODEL" ]; then +if [ $flag == "CFE" ] || [ "$flag" == "TOPMODEL" ]; then echo "SMP running with option $flag" else echo "Invalid option! $flag" @@ -17,10 +17,10 @@ fi args=" " exe_name=" " -if [ $flag == "STANDALONE" ]; then - args='./configs/config_conceptual.txt' - exe_name='smp_standalone' -else if [ $flag == "WITHTOPMODEL" ]; then +if [ $flag == "CFE" ]; then + args="./configs/config_cfe.txt ./configs/config_conceptual.txt" + exe_name='smp_cfe' +else if [ $flag == "TOPMODEL" ]; then args="configs/topmod.run configs/config_topmodel.txt" exe_name='smp_topmodel' fi diff --git a/src/main_smp_cfe.cxx b/src/main_smp_cfe.cxx new file mode 100644 index 0000000..77e4cf4 --- /dev/null +++ b/src/main_smp_cfe.cxx @@ -0,0 +1,123 @@ +/* + author: Ahmad Jan Khattak + email : ahmad.jan@noaa.gov + date : May 2024 +*/ + +#include +#include +#include +#include +#include + +#include "bmi.h" +#include "cfe.h" +#include "bmi_cfe.h" + +#include "../bmi/bmi.hxx" +#include "bmi_soil_moisture_profile.hxx" +#include "soil_moisture_profile.hxx" + +#define SUCCESS 0 + + +/* + This pseudo-framework couples CFE and SoilMoistureProfiles modules to compute water table + and soil moisture profile +*/ + +/***************************************************************** + * Function to pass the parameters from CFE to SoilMoistureProfiles + ****************************************************************/ +void pass_data_from_cfe_to_smp(Bmi *cfe_bmi_model, BmiSoilMoistureProfile *smp_bmi_model) { + + double storage = 0.0; + double storage_change = 0.0; + double *storage_ptr = &storage; + double *storage_change_ptr = &storage_change; + cfe_bmi_model->get_value(cfe_bmi_model, "SOIL_STORAGE", storage_ptr); + cfe_bmi_model->get_value(cfe_bmi_model, "SOIL_STORAGE_CHANGE", storage_change_ptr); + + smp_bmi_model->SetValue("soil_storage",storage_ptr); + smp_bmi_model->SetValue("soil_storage_change",storage_change_ptr); + +} + +int main(int argc, char *argv[]) +{ + BmiSoilMoistureProfile smp_bmi; + + if (argc != 3) { + printf("Usage: ./run_smp.sh CFE \n\n"); + printf("Run soil moisture profile model through its BMI with a configuration file.\n"); + //printf("Output is written to the file `bmi_file.out`.\n"); + return SUCCESS; + } + + /************************************************************************ + * Allocating memory to store the entire CFE BMI structure + ************************************************************************/ + printf("\n Allocating memory to CFE BMI model structure ... \n"); + Bmi *cfe_bmi_model = (Bmi *) malloc(sizeof(Bmi)); + + + /************************************************************************ + * Registering the BMI model for CFE + ************************************************************************/ + printf("Registering BMI CFE model\n"); + register_bmi_cfe(cfe_bmi_model); + + /************************************************************************ + * Initializing the BMI model for CFE and AORC and Freeze-thaw model + ************************************************************************/ + printf("Initializeing BMI CFE %s \n", argv[1]); + const char *cfg_file_cfe = argv[1]; + cfe_bmi_model->initialize(cfe_bmi_model, cfg_file_cfe); + + printf("Initializeing BMI SMP model %s \n", argv[2]); + smp_bmi.Initialize(argv[2]); + + + /************************************************************************ + * Get the information from the configuration here in Main + ************************************************************************/ + printf("Get the information from the configuration here in Main\n"); + cfe_state_struct *cfe; + cfe = (cfe_state_struct *) cfe_bmi_model->data; + + /************************************************************************ + This is the basic process for getting the SoilMoistureProfile and CFE to share data + through BMI interface + 1. Update the CFE + 2. Get data from the CFE and Set variables in the Soil Moisture Profile + 3. Update the Soil Moisture Profile (which computes 1D soil moisture profile and water table depth) + ************************************************************************/ + + int nz = 20; // number of cells for soil discretization + double *smc = new double[nz]; + double water_table; + double soil_moisture_fraction; + + int nstep = 10; + + for (int i = 0; i < nstep; i++) { + cfe_bmi_model->update(cfe_bmi_model); + + pass_data_from_cfe_to_smp(cfe_bmi_model, &smp_bmi); // Get and Set values + + smp_bmi.Update(); + + smp_bmi.GetValue("soil_moisture_profile",&smc[0]); + smp_bmi.GetValue("soil_water_table",&water_table); + smp_bmi.GetValue("soil_moisture_fraction",&soil_moisture_fraction); + + //for (int k = 0; k < 20; k++) + // std::cout <<"soil_moisture ("<< k << ") = "<< smc[k] <<"\n"; + std::cout<<"water table depth [m] = "<finalize(cfe_bmi_model); + + return SUCCESS; +} diff --git a/src/main_smp_topmodel.cxx b/src/main_smp_topmodel.cxx index 7675376..1252ed9 100644 --- a/src/main_smp_topmodel.cxx +++ b/src/main_smp_topmodel.cxx @@ -1,5 +1,5 @@ /* - author: Ahmad Jan + author: Ahmad Jan Khattak email : ahmad.jan@noaa.gov date : March 2023 */ @@ -9,13 +9,13 @@ #include -#include "./topmodel/include/topmodel.h" -#include "./topmodel/include/bmi.h" -#include "./topmodel/include/bmi_topmodel.h" +#include "topmodel.h" +#include "bmi.h" +#include "bmi_topmodel.h" #include "../bmi/bmi.hxx" -#include "../include/bmi_soil_moisture_profile.hxx" -#include "../include/soil_moisture_profile.hxx" +#include "bmi_soil_moisture_profile.hxx" +#include "soil_moisture_profile.hxx" /* @@ -27,11 +27,11 @@ */ /*************************************************************** - Function to pass the parameters from Topmodel to SoilMoistureProfiles + * Function to pass the parameters from Topmodel to SoilMoistureProfiles ***************************************************************/ -void pass_data_from_topmodel_to_smc(Bmi *topmodel_bmi, BmiSoilMoistureProfile *smc_bmi) { - - +void pass_data_from_topmodel_to_smp(Bmi *topmodel_bmi, BmiSoilMoistureProfile *smc_bmi) { + + double Qb; // baseflow in topmodel double Qv; // flow to saturated zone from unsaturated zone double deficit; // catchment soil moisture deficit @@ -46,7 +46,7 @@ void pass_data_from_topmodel_to_smc(Bmi *topmodel_bmi, BmiSoilMoistureProfile *s smc_bmi->SetValue("Qb_topmodel",&Qb); smc_bmi->SetValue("Qv_topmodel",&Qv); smc_bmi->SetValue("global_deficit",&deficit); - + } /************************************************************************ @@ -55,12 +55,12 @@ void pass_data_from_topmodel_to_smc(Bmi *topmodel_bmi, BmiSoilMoistureProfile *s ************************************************************************/ int main(int argc, const char *argv[]) { - + /************************************************************************ A configuration file is required for running this model through BMI ************************************************************************/ if(argc<=1) { - printf("make sure to include a path to config files\n"); + printf("make sure to include path to config files\n"); exit(1); } @@ -68,35 +68,35 @@ main(int argc, const char *argv[]) { allocating memory to store the entire BMI structure for TopModel ************************************************************************/ printf("\n Allocating memory to TOPMODEL BMI model structure ... \n"); - Bmi *model = (Bmi *) malloc(sizeof(Bmi)); - + Bmi *topmodel_bmi = (Bmi *) malloc(sizeof(Bmi)); + BmiSoilMoistureProfile smp_bmi; - + /************************************************************************ Registering the BMI model for Topmodel ************************************************************************/ - register_bmi_topmodel(model); + register_bmi_topmodel(topmodel_bmi); printf("Registering BMI topmodel\n"); - + /************************************************************************ Initializing the BMI for Topmodel ************************************************************************/ - + printf("Initializeing BMI Topmodel. %s \n", argv[1]); const char *cfg_file_topmodel = argv[1]; - model->initialize(model, cfg_file_topmodel); - - printf("Initializeing BMI SMP \n"); - const char *cfg_file_smp = argv[2]; - smp_bmi.Initialize(cfg_file_smp); - + topmodel_bmi->initialize(topmodel_bmi, cfg_file_topmodel); + + printf("Initializeing BMI SMP \n"); + const char *cfg_file_smp = argv[2]; + smp_bmi.Initialize(cfg_file_smp); + /************************************************************************ Get the information from the configuration here in Main ************************************************************************/ printf("Get the information from the configuration here in Main\n"); topmodel_model *topmodel; - topmodel = (topmodel_model *) model->data; - + topmodel = (topmodel_model *) topmodel_bmi->data; + /************************************************************************ This is the basic process for getting the SoilMoistureProfile and Topmodel to share data through BMI interface @@ -106,7 +106,7 @@ main(int argc, const char *argv[]) { ************************************************************************/ int nstep; - + if (topmodel->stand_alone == TRUE) { // Gather number of steps from input file // when in standalone mode. @@ -117,12 +117,12 @@ main(int argc, const char *argv[]) { // Note: this is a pseudo-framework nstep = 720; } - + /************************************************************************ Now loop through time and call the models with the intermediate get/set ************************************************************************/ - printf("looping through and calling updata\n"); + printf("looping through and calling update \n"); // output files -- writing water table depth, soil moisture fraction, and soil moisture profiles to separate files ofstream fout, fout_wt; @@ -134,16 +134,16 @@ main(int argc, const char *argv[]) { double *smc = new double[nz]; double water_table; double soil_moisture_fraction; - + for (int i = 0; i < nstep; i++) { - model->update(model); + topmodel_bmi->update(topmodel_bmi); - pass_data_from_topmodel_to_smc(model, &smp_bmi); // Get and Set values + pass_data_from_topmodel_to_smp(topmodel_bmi, &smp_bmi); // Get and Set values smp_bmi.Update(); - + smp_bmi.GetValue("soil_moisture_profile",&smc[0]); smp_bmi.GetValue("soil_water_table",&water_table); smp_bmi.GetValue("soil_moisture_fraction",&soil_moisture_fraction); @@ -159,13 +159,12 @@ main(int argc, const char *argv[]) { fout.close(); // Run the Mass Balance check - + /************************************************************************ Finalize both the Topmodel bmi ************************************************************************/ printf("\n Finalizing TOPMODEL and SMP BMIs ... \n"); - model->finalize(model); + topmodel_bmi->finalize(topmodel_bmi); return 0; } - diff --git a/src/main_soil_moisture.cxx b/src/main_soil_moisture.cxx deleted file mode 100644 index bb322c3..0000000 --- a/src/main_soil_moisture.cxx +++ /dev/null @@ -1,133 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../bmi/bmi.hxx" -#include "../include/bmi_soil_moisture_profile.hxx" -#include "../include/soil_moisture_profile.hxx" - -#define SUCCESS 0 -int main(int argc, char *argv[]) -{ - BmiSoilMoistureProfile model; - - if (argc != 2) { - printf("Usage: ./make_run_standalone.sh \n\n"); - printf("Run soil moisture profile model through its BMI with a configuration file.\n"); - printf("Output is written to the file `bmi_file.out`.\n"); - return SUCCESS; - } - - FILE *fp = fopen("bmi_file.out", "w"); - fprintf(fp, "Configuration file = %s\n", argv[1]); - fprintf(fp, "Initializing... "); - - model.Initialize(argv[1]); - - fprintf(fp, "done\n"); - - { - std::string model_name; - model_name = model.GetComponentName(); - fprintf(fp, "%s\n", model_name.c_str()); - } - - { - std::string var_name_s = "soil_storage"; - std::string var_name_sc = "soil_storage_change"; - std::string var_name_wt = "soil_water_table"; - std::string var_name_smc = "soil_moisture_profile"; - std::string var_name_smcl = "soil_moisture_wetting_fronts"; - std::string var_name_smc_bmi = "soil_storage_model"; - - int grid, rank, *shape; - double *var_s = NULL; - double *var_sc = NULL; - double *water_table_thickness_bmi = NULL; - - fprintf(fp, "variable = %s\n", var_name_s.c_str()); - fprintf(fp, "variable = %s\n", var_name_sc.c_str()); - fprintf(fp, "variable = %s\n", var_name_wt.c_str()); - fprintf(fp, "variable = %s\n", var_name_smc.c_str()); - fprintf(fp, "variable = %s\n", var_name_smcl.c_str()); - fprintf(fp, "variable = %s\n", var_name_smc_bmi.c_str()); - - grid = model.GetVarGrid(var_name_smc); - - rank = model.GetGridRank(grid); - fprintf(fp, "rank = %d\n", rank); - shape = new int[rank]; - model.GetGridShape(grid, shape); - - fprintf(fp, "shape = %d x %d x %d\n", shape[0],1,1); - - /****************************************************************************/ - // unit test data for conceptual soil reservoir - double soil_moisture_profile[] = {0.3375956134,0.3423608214,0.3475802559,0.3533405627,0.3597547738,0.3669739833, - 0.3752061855,0.3847482243,0.3960434405,0.4097942568,0.4272064334,0.4390000000, - 0.4390000000,0.4390000000,0.4390000000,0.4390000000,0.4390000000,0.4390000000, - 0.4390000000,0.4390000000}; - double water_table_thickness = 1.50956; // in meters - enum option { Conceptual = 1, Layered = 2}; - /****************************************************************************/ - // Set values - double storage_m = 0.8; - double storage_change_m = -0.000472; - double *storage_m_ptr = &storage_m; - double *storage_change_m_ptr = &storage_change_m; - double smc_layers[] = {0.25, 0.15, 0.1, 0.12}; - - int soil_moisture_profile_option; - - model.GetValue(var_name_smc_bmi,&soil_moisture_profile_option); - - model.SetValue(var_name_s,storage_m_ptr); - - model.SetValue(var_name_sc,storage_change_m_ptr); - - model.SetValue(var_name_smcl,&smc_layers[0]); - - var_s = (double *)model.GetValuePtr(var_name_s); - var_sc = (double *)model.GetValuePtr(var_name_sc); - - std::cout<<"soil_storage_model: "<shape[0] = parameters->ncells; // this is used for the size of soil_moisture_profile (bmi output) - + if (parameters->soil_storage_model == Conceptual || parameters->soil_storage_model == Topmodel) parameters->shape[1] = 1; else if (parameters->soil_storage_model == Layered) @@ -77,10 +77,10 @@ SoilMoistureProfile(string config_file, struct soil_profile_parameters* paramete void soil_moisture_profile:: InitFromConfigFile(string config_file, struct soil_profile_parameters* parameters) -{ +{ ifstream fp; fp.open(config_file); - + bool is_soil_z_set = false; bool is_soil_depth_layers_set = false; bool is_smcmax_set = false; @@ -92,14 +92,14 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter bool is_water_table_based_method_set = false; bool is_soil_moisture_fraction_depth_set = false; bool is_soil_moisture_profile_option_set = false; // option for linear or piece-wise constant layered profile - + while (fp) { string line; string param_key, param_value, param_unit; - + getline(fp, line); - + int loc_eq = line.find("=") + 1; int loc_u = line.find("["); param_key = line.substr(0,line.find("=")); @@ -112,15 +112,15 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter param_unit = ""; param_value = line.substr(loc_eq,loc_u - loc_eq); - + if (param_key == "soil_z") { vector vec = ReadVectorData(param_key, param_value); - + parameters->soil_z = new double[vec.size()]; - + for (unsigned int i=0; i < vec.size(); i++) parameters->soil_z[i] = vec[i]; - + parameters->ncells = vec.size(); parameters->soil_depth = parameters->soil_z[parameters->ncells-1]; is_soil_z_set = true; @@ -133,10 +133,10 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter else { vector vec = ReadVectorData(param_key,param_value); parameters->soil_depth_layers = new double[vec.size()]; - + for (unsigned int i=0; i < vec.size(); i++) parameters->soil_depth_layers[i] = vec[i]; - + parameters->num_layers = vec.size(); parameters->num_wetting_fronts = vec.size(); parameters->last_layer_depth = parameters->soil_depth_layers[parameters->num_layers - 1]; @@ -153,7 +153,7 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter else { vector vec = ReadVectorData(param_key, param_value); parameters->smcmax = new double[vec.size()]; - + for (unsigned int i=0; i < vec.size(); i++) { assert (vec[i] > 0); parameters->smcmax[i] = vec[i]; @@ -164,7 +164,7 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter assert (parameters->num_layers > 0); is_smcmax_set = true; } - + continue; } // NOTE: `soil_params.b` may be deprecated in the future in favor of `b` @@ -184,9 +184,9 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter else if (param_key == "soil_storage_model") { if ( param_value == "Conceptual" || param_value == "conceptual") parameters->soil_storage_model = Conceptual; - else if (param_value == "layered" || param_value == "Layered") + else if (param_value == "layered" || param_value == "Layered") parameters->soil_storage_model = Layered; - else if (param_value == "topmodel" || param_value == "TopModel" || param_value == "TOPMODEL") + else if (param_value == "topmodel" || param_value == "TopModel" || param_value == "TOPMODEL") parameters->soil_storage_model = Topmodel; is_soil_storage_model_set = true; @@ -225,20 +225,20 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter continue; } else if (param_key == "verbosity") { - + if (param_value == "high" || param_value == "low") parameters->verbosity = param_value; else parameters->verbosity = "none"; - + continue; } - + } - + fp.close(); - + if (!is_soil_z_set) { stringstream errMsg; errMsg << "soil_z not set in the config file "<< config_file << "\n"; @@ -253,19 +253,19 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter throw runtime_error(errMsg.str()); } } - + if (!is_smcmax_set) { stringstream errMsg; errMsg << "smcmax not set in the config file "<< config_file << "\n"; throw runtime_error(errMsg.str()); } - + if (!is_b_set) { stringstream errMsg; errMsg << "b (Clapp-Hornberger's parameter) not set in the config file "<< config_file << "\n"; throw runtime_error(errMsg.str()); } - + if (!is_satpsi_set) { stringstream errMsg; errMsg << "satpsi not set in the config file "<< config_file << "\n"; @@ -275,7 +275,7 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter if (!is_soil_moisture_fraction_depth_set) { parameters->soil_moisture_fraction_depth = 0.4; // in meters } - + if (!is_soil_storage_model_depth_set && parameters->soil_storage_model == Conceptual) { stringstream errMsg; errMsg << "soil_storage_model_depth not set in the config file "<< config_file << "\n"; @@ -288,7 +288,7 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter throw runtime_error(errMsg.str()); } - + if (parameters->soil_storage_model == Layered) { if (!is_soil_moisture_profile_option_set) { stringstream errMsg; @@ -299,14 +299,14 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter if (!is_water_table_depth_set) { parameters->water_table_depth = 6.0; } - + assert (parameters->num_wetting_fronts > 0); assert (parameters->water_table_depth >= 0); } // check to ensure that options for the topmodel based watertable provided are correct if (parameters->soil_storage_model == Topmodel) { - + if (is_water_table_based_method_set) { if (parameters->water_table_based_method != Flux_based && parameters->water_table_based_method != Deficit_based) { stringstream errMsg; @@ -320,7 +320,7 @@ InitFromConfigFile(string config_file, struct soil_profile_parameters* parameter throw runtime_error(errMsg.str()); } } - + assert (parameters->ncells > 0); } @@ -333,8 +333,8 @@ SoilMoistureProfileUpdate(struct soil_profile_parameters* parameters) double thickness = 0.0; double soil_moisture_fraction_depth = parameters->soil_moisture_fraction_depth; parameters->soil_moisture_fraction = 0.0; // reset to 0 at each timestep - - + + if (parameters->soil_storage_model == Conceptual) { SoilMoistureProfileFromConceptualReservoir(parameters); } @@ -342,7 +342,7 @@ SoilMoistureProfileUpdate(struct soil_profile_parameters* parameters) SoilMoistureProfileFromLayeredReservoir(parameters); } else if (parameters->soil_storage_model == Topmodel) { - SoilMoistureProfileFromWaterTableDepth(parameters); + SoilMoistureProfileFromWaterTableDepth(parameters); } else { stringstream errMsg; @@ -355,7 +355,7 @@ SoilMoistureProfileUpdate(struct soil_profile_parameters* parameters) if (parameters->soil_storage_model == Topmodel || parameters->soil_storage_model == Layered) { double storage_temp = 0.0; // temporary storage for (int i=0; incells; i++) { - if (parameters->soil_z[i] <= parameters->soil_depth_NWM && i == 0) { + if (parameters->soil_z[i] <= parameters->soil_depth_NWM && i == 0) { storage_temp = parameters->soil_moisture_profile[i] * parameters->soil_z[i]; } else if (parameters->soil_z[i] <= parameters->soil_depth_NWM) { @@ -365,15 +365,15 @@ SoilMoistureProfileUpdate(struct soil_profile_parameters* parameters) else { break; } - + } parameters->soil_storage = storage_temp; } - - // compute soil moisture fraction, moisture in the top 40 cm over soil storage in 2 m + + // compute soil moisture fraction, moisture in the top 40 cm over soil storage in 2 m for (int i=0; incells; i++) { - if (parameters->soil_z[i] <= soil_moisture_fraction_depth && i == 0) { + if (parameters->soil_z[i] <= soil_moisture_fraction_depth && i == 0) { parameters->soil_moisture_fraction += parameters->soil_moisture_profile[i] * parameters->soil_z[i]; } else if (parameters->soil_z[i] <= soil_moisture_fraction_depth) { @@ -383,16 +383,16 @@ SoilMoistureProfileUpdate(struct soil_profile_parameters* parameters) else { break; } - + } - + parameters->soil_moisture_fraction = fmin(parameters->soil_moisture_fraction, parameters->soil_storage); - + if (parameters->soil_storage > 0.0) parameters->soil_moisture_fraction /= parameters->soil_storage; else parameters->soil_moisture_fraction = 0.0; - + } /* @@ -431,23 +431,23 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param double tolerance = 1.0E-6; double soil_storage_max = model_depth * parameters->smcmax[0]; - + double soil_storage_change_per_timestep_cm = fabs(parameters->soil_storage_change_per_timestep * 100.0); double soil_storage_current_timestep_cm = 100.0 * parameters->soil_storage; // storage at the current timestep - + assert(parameters->soil_storage > 0.0); /* to ensure that soil storage is non-zero due to unexpected bugs (either in the models or calibration tools) */ - + int count = 0; /* compute a new profile only if sufficient amount of water is added at this timestep. 1.0E-4 corresponds to 0.001 mm of water */ - + if (soil_storage_change_per_timestep_cm > 0.0001 || parameters->init_profile) { - - // turn off the flag for times t > 0 + + // turn off the flag for times t > 0 parameters->init_profile = false; - + // check if the storage is greater than the maximum soil storage. if yes, set it to the maximum storage if(soil_storage_current_timestep_cm >= soil_storage_max) { for(int j=0;jncells;j++) @@ -466,7 +466,7 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param parameters->water_table_depth = 1000.0; // fictitious water table return; } - + double diff=1000.0; // guess for the initial differnce between the roots double f, zi_new, df_dzi; @@ -474,7 +474,7 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param // that means the soil is super dry do { count++; - + // function representing the total amount of soil moisture. 2nd term is the integral of // the Clap-Hornberger function (area under the soil moisture curve) @@ -486,7 +486,7 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param + alpha * parameters->smcmax[0] * ( pow((model_depth-zi),beta) - pow(satpsi_cm,beta) ); double fib = parameters->smcmax[0] * (zi - z0) + parameters->smcmax[0] * satpsi_cm + alpha * parameters->smcmax[0] * ( pow(abs(zb-zi),beta) - pow(satpsi_cm,beta) ); - + fib = zi >= 0.0 ? 0.0 : fib; @@ -498,12 +498,12 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param dfib = zi >= 0.0 ? 0.0 : dfib; df_dzi = dfis - dfib; - + // Newton-Raphson method zi_new = zi - f / fmax(df_dzi,1.e-6); // to avoid division by zero - + diff=zi_new-zi; // difference betweent the previous and new root - + zi=zi_new; // update the previous root z0 = zi >= 0.0 ? zb : zi - satpsi_cm; @@ -513,7 +513,7 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param // may fail to converge in reasonable number of timesteps if (zi_m < -1000) break; - + } while (fabs(diff) > tolerance); // water table thickness can be negative and that would be depth of the water table below the @@ -526,22 +526,22 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param for (int i=0; incells; i++) { double z_temp = parameters->water_table_depth - parameters->soil_z[i]; double theta; - + if (parameters->water_table_depth <= parameters->soil_z[i]) theta = parameters->smcmax[0]; else theta = parameters->smcmax[0] * pow((parameters->satpsi/z_temp),lam); - + parameters->soil_moisture_profile[i] = fmin(theta, parameters->smcmax[0]); - + } - + } if (verbosity.compare("high") == 0) { std::cout<<"Number of iterations = "<< count <<"\nWater table depth (m) = "<< parameters->water_table_depth <<"\n"; PrintSoilMoistureProfile(parameters); - + // check compute water in the model domaian double total_water = parameters->soil_moisture_profile[0] * parameters->soil_z[0]; @@ -565,7 +565,7 @@ SoilMoistureProfileFromConceptualReservoir(struct soil_profile_parameters* param - Two strategies are implemented - Constant strategy: A simple technique to map layered values to grids in the soil discretization. That is, all grid cells in the discretization has a constant value within a layer. - Note cells at the interface take average value of the layers + Note cells at the interface take average value of the layers - Linear strategy: A linear interpolation tehcnique is used to map layered values to grids in the soil discretization. That is, grid cells in the discretization take linearly interpolated value between consecutive layers. @@ -592,12 +592,12 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete } parameters->last_layer_depth = parameters->soil_depth_wetting_fronts[num_wf-1]; - + double lam = 1.0/parameters->b; // pore distribution index - + vector z_layers_n(1,0.0); - + for (int i=0; i < num_wf; i++) z_layers_n.push_back(parameters->soil_depth_wetting_fronts[i]); @@ -606,10 +606,10 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete // call to find water table function for layered reservoirs FindWaterTableLayeredReservoir(parameters); - + // piece-wise constant (vertically) if (parameters->soil_moisture_profile_option == Constant) { - + // loop over all the cells in the discretized column for (int i=0; i < parameters->ncells; i++) { @@ -624,7 +624,7 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete parameters->soil_moisture_profile[i] = parameters->soil_moisture_wetting_fronts[c]; } else { // cell at the interface of layers, so take the mean - + if (parameters->soil_z[i-1] == parameters->soil_depth_wetting_fronts[c] && parameters->soil_z[i] <= parameters->soil_depth_wetting_fronts[c+1]) { parameters->soil_moisture_profile[i] = parameters->soil_moisture_wetting_fronts[c+1]; @@ -634,7 +634,7 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete + parameters->soil_moisture_wetting_fronts[c+1]); } c++; - + } } else { @@ -646,7 +646,7 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete double z_temp = parameters->water_table_depth - parameters->soil_z[i]; theta = parameters->smcmax[num_layers-1] * pow((parameters->satpsi/z_temp),lam); } - + parameters->soil_moisture_profile[i] = theta; } } @@ -655,12 +655,12 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete else if (parameters->soil_moisture_profile_option == Linear ) { double value = 0.0; int c = 0; // the parameter c keeps track of wetting fronts - + for (int i=0; i < parameters->ncells; i++) { if (parameters->soil_z[i] <= parameters->soil_depth_wetting_fronts[0]) { parameters->soil_moisture_profile[i] = parameters->soil_moisture_wetting_fronts[0]; - + if (parameters->soil_z[i+1] > parameters->soil_depth_wetting_fronts[0]) c++; } @@ -669,17 +669,17 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete if (c == num_wf) break; - + value = LinearInterpolation(parameters->soil_z[i], parameters->soil_depth_wetting_fronts[c-1], parameters->soil_depth_wetting_fronts[c], parameters->soil_moisture_wetting_fronts[c-1], parameters->soil_moisture_wetting_fronts[c]); - + parameters->soil_moisture_profile[i] = value; // if true, we reached the domain depth of the soil moisture profile if (i == parameters->ncells-1) break; - + if (parameters->soil_z[i+1] > parameters->soil_depth_wetting_fronts[c]) c++; } @@ -692,19 +692,19 @@ SoilMoistureProfileFromLayeredReservoir(struct soil_profile_parameters* paramete double z_temp = parameters->water_table_depth - parameters->soil_z[i]; theta = parameters->smcmax[num_layers-1] * pow((parameters->satpsi/z_temp),lam); } - + parameters->soil_moisture_profile[i] = theta; - + } } - + } - + if (verbosity.compare("high") == 0) { std::cout<<"Water table depth (m) = "<< parameters->water_table_depth <<"\n"; PrintSoilMoistureProfile(parameters); } - + } void soil_moisture_profile:: @@ -716,20 +716,20 @@ FindWaterTableLayeredReservoir(struct soil_profile_parameters* parameters) double tolerance = 1.0e-3; double lam = 1.0/parameters->b; // pore distribution index bool is_water_table_found = false; - + // find and update water table location if it is within the model domain if (fabs(parameters->soil_moisture_wetting_fronts[num_wf-1] - parameters->smcmax[num_layers-1]) < tolerance) { is_water_table_found = true; int j = num_wf-1; double z1; - + for (int c=num_layers-1; c>=0; c--) { z1 = 0.0; if ( c != 0) z1 = parameters->soil_depth_layers[c-1]; - + // std::cerr<<"layer = "<soil_depth_wetting_fronts[j]<<" , "<soil_depth_layers[c]<<" "<soil_depth_wetting_fronts[j] <= parameters->soil_depth_layers[c] && parameters->soil_depth_wetting_fronts[j] > z1) { bool is_wf_saturated = fabs(parameters->soil_moisture_wetting_fronts[j] - parameters->smcmax[c]) < tolerance; //std::cerr<<"Vx = "<soil_depth_wetting_fronts[j]<<" " @@ -745,11 +745,11 @@ FindWaterTableLayeredReservoir(struct soil_profile_parameters* parameters) } else if (j==0 || !is_wf_saturated) break; - + } } } - + // If the watertable is not within the model domain (i.e., the soil is unsaturated in the column), // then we find watertable depth as below @@ -760,13 +760,13 @@ FindWaterTableLayeredReservoir(struct soil_profile_parameters* parameters) double theta = 0.0; double initial_head = parameters->satpsi; // fully saturated soil while (fabs(theta - target_theta) > tolerance) { - + // extend the profile below the depth of the last layer double z_head = initial_head + dz; theta = pow((parameters->satpsi/z_head),lam) * parameters->smcmax[num_layers-1]; - + assert (theta <= parameters->smcmax[num_layers-1]); - + if (theta <= target_theta) break; @@ -775,7 +775,7 @@ FindWaterTableLayeredReservoir(struct soil_profile_parameters* parameters) dz += 0.05; } - + // watertable depth = domain_depth + depth_to_WT_from_domain_depth + capillary_fringe //parameters->water_table_depth = parameters->soil_z[parameters->ncells-1] + dz + parameters->satpsi; parameters->water_table_depth = parameters->soil_depth_wetting_fronts[num_wf-1] + dz + parameters->satpsi; @@ -790,13 +790,13 @@ ExtendedProfileBelowDomainDepth(struct soil_profile_parameters* parameters) { double theta = 0.0; double initial_head = parameters->satpsi; // fully saturated soil double tolerance = 1.0e-3; - + while (fabs(theta - target_theta) > tolerance) { - + // extend the profile below the depth of the last layer double z_head = initial_head + dz; theta = pow((parameters->satpsi/z_head),lam) * parameters->smcmax[num_layers-1]; - + assert (theta <= parameters->smcmax[num_layers-1]); if (theta <= target_theta) @@ -804,7 +804,7 @@ ExtendedProfileBelowDomainDepth(struct soil_profile_parameters* parameters) { if (dz >= 100.0) break; - + dz += 0.05; } @@ -837,34 +837,34 @@ SoilMoistureProfileFromWaterTableDepth(struct soil_profile_parameters* parameter double theta_fc = parameters->smcmax[0] / 3.0; double delta_theta = (parameters->smcmax[0] - theta_fc); std::string verbosity = parameters->verbosity; - + if (parameters->water_table_based_method == Deficit_based || parameters->init_profile) { parameters->water_table_depth = parameters->global_deficit/delta_theta * to_cm + satpsi_cm; // add saturated head to account for capillary fringe - - parameters->init_profile = false; // turn off the flag for times t > 0 + + parameters->init_profile = false; // turn off the flag for times t > 0 } else if (parameters->water_table_based_method == Flux_based) { // Eq. (15) in M. Franchini et al. Journal of Hydrology 175 (1996) 293-338 parameters->water_table_depth -= (parameters->Qv_topmodel - parameters->Qb_topmodel)/parameters->cat_area * dt * to_cm; } - + // check if the storage is greater than the maximum soil storage. if yes, set it to the maximum storage if(parameters->water_table_depth == 0.0) { for(int j=0;jncells;j++) parameters->soil_moisture_profile[j] = parameters->smcmax[0]; - + return; } - + /*******************************************************************/ // get a high resolution moisture profile that will be mapped on the desired soil discretization - + int z_hres = 1000; double *smct_temp = new double[z_hres]; double *z_temp = new double[z_hres]; double zi = model_depth - parameters->water_table_depth; // thickness of the water table depth (bottom to top) - + // we have the new water table location now, so let's compute the soil moisture curve now double z = zi + satpsi_cm; double dz_v = (model_depth - zi - satpsi_cm)/z_hres; // vertical spacing over the depth (from zi+satpsi to the surface) @@ -877,7 +877,7 @@ SoilMoistureProfileFromWaterTableDepth(struct soil_profile_parameters* parameter z+=dz_v; z_temp[i] = z; } - + // map the high resolution soil moisture curve to the soil discretization depth that is provided in the config file for (int i=0; incells; i++) { for (int j=0; j values(0.0); string z1 = param_value; - + if (z1.find(delimiter) == string::npos) { double v = stod(z1); if (v <= 0.0) { @@ -929,9 +929,9 @@ ReadVectorData(string param_name, string param_value) errMsg << "Input provided in the config file for parameter "<< param_name << " is " << v << ". It should be positive."<< "\n"; throw runtime_error(errMsg.str()); } - + values.push_back(v); - + } else { while (z1.find(delimiter) != string::npos) { @@ -939,13 +939,13 @@ ReadVectorData(string param_name, string param_value) string z_v = z1.substr(0, pos); values.push_back(stod(z_v.c_str())); - + z1.erase(0, pos + delimiter.length()); if (z1.find(delimiter) == string::npos) values.push_back(stod(z1)); } } - + return values; } @@ -955,6 +955,6 @@ PrintSoilMoistureProfile(struct soil_profile_parameters* parameters) for (int i=0; incells; i++) std::cout<<"soil_moisture (z, value) = "<< parameters->soil_z[i]<<", "<soil_moisture_profile[i]<<"\n"; } - + #endif diff --git a/tests/main_unittest.cxx b/tests/main_unittest.cxx index 66032a3..7dceb49 100644 --- a/tests/main_unittest.cxx +++ b/tests/main_unittest.cxx @@ -34,7 +34,7 @@ int main(int argc, char *argv[]) } std::cout<<"\n**************** BEGIN SoilMoistureProfiles BMI UNIT TEST *******************\n"; - + model.Initialize(argv[1]); model_layered.Initialize(argv[2]); @@ -43,17 +43,17 @@ int main(int argc, char *argv[]) bool test_status = true; int num_input_vars = 8; int num_output_vars = 3; - + std::vector bmi_input_vars = {"soil_storage", "soil_storage_change", "num_wetting_fronts", "soil_moisture_wetting_fronts", "soil_depth_wetting_fronts", "Qb_topmodel", "Qv_topmodel", "global_deficit"}; std::vector bmi_output_vars = {"soil_moisture_profile", "soil_water_table","soil_moisture_fraction"}; - + int nbytes_input[] = {sizeof(double), sizeof(double), sizeof(int), sizeof(double), sizeof(double), sizeof(double), sizeof(double), sizeof(double)}; int nbytes_output[] = {int(nz * sizeof(double)), sizeof(double), sizeof(double)}; //double soil_moisture_profile[] = {0.389,0.396,0.397,0.397}; // total_moisture_content - + std::cout<<"Num cells: "< names_in; std::vector names_out; - + // Test get_component_name() model_name = model.GetComponentName(); @@ -86,7 +86,7 @@ int main(int argc, char *argv[]) throw std::runtime_error(errMsg.str()); } - // Test GetInputVarNames + // Test GetInputVarNames names_in = model.GetInputVarNames(); if (VERBOSITY) { std::cout<<"Input variable names \n"; @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) errMsg << "Number of output variables are different. "<< count_out <<" != "<< num_output_vars <<"\n"; throw std::runtime_error(errMsg.str()); } - + // Test BMI: VARIABLE INFORMATION FUNCTIONS std::cout<<"\n**************** TEST BMI VARIABLE INFORMATION FUNCTIONS\n***************************\n"; @@ -125,7 +125,7 @@ int main(int argc, char *argv[]) std::string location; std::string units; std::string vartype; - + // Loop through both input and output variables and call GetVar* functions for (int i=0; i 0 ? "Yes" : "No"; std::cout< 0 ? "Yes" : "No"; - + } - + std::cout< 0 ? "Yes" : "No"; } - + std::cout< 0 ? "Yes" : "No"; } - + std::cout< 0 ? "Yes" : "No"; - + std::cout<<"| Unittest passed : "<< RED << passed << RESET << GREEN <<"\n"; std::cout<<"| *************************************** \n"; std::cout<