From 72f3481f99e30ed474a2f7dd749fe123ef923930 Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Fri, 4 Aug 2023 14:13:22 +0000 Subject: [PATCH 01/11] Null/Constant Forcing Provider --- ...example_bmi_realization_config_output.json | 62 ++++ include/forcing/NullForcingProvider.hpp | 271 ++++++++++++++++++ .../catchment/Bmi_Multi_Formulation.hpp | 1 + .../catchment/Formulation_Constructors.hpp | 4 + 4 files changed, 338 insertions(+) create mode 100644 data/example_bmi_realization_config_output.json create mode 100644 include/forcing/NullForcingProvider.hpp diff --git a/data/example_bmi_realization_config_output.json b/data/example_bmi_realization_config_output.json new file mode 100644 index 0000000000..4512c24f43 --- /dev/null +++ b/data/example_bmi_realization_config_output.json @@ -0,0 +1,62 @@ +{ + "global": { + "formulations": [ + { + "name": "bmi_multi", + "params": { + "model_type_name": "bmi_jinja_source", + "forcing_file": "", + "init_config": "", + "allow_exceed_end_time": true, + "main_output_variable": "TMP_2maboveground", + "output_bbox" : [4199,2099,3,3,17644200,5,2], + "modules": [ + { + "name": "bmi_python", + "params": { + "model_type_name": "jinjabmi", + "python_type": "jinjabmi.Jinja", + "init_config": "./jinja_grid_source.yml", + "allow_exceed_end_time": true, + "main_output_variable": "dummy_output_scalar", + "uses_forcing_file": false + } + }, + { + "name": "bmi_python", + "params": { + "python_type": "AORC_bmi_model.AORC_bmi_model", + "model_type_name": "AORC_bmi_model", + "init_config": "./config.yml", + "main_output_variable": "APCP_surface", + "uses_forcing_file": false, + "output_bbox" : [4199,2099,3,3,17644200,5,2], + "variables_names_map_disable" : { + "APCP_surface": "atmosphere_water__liquid_equivalent_precipitation_rate", + "TMP_2maboveground": "land_surface_radiation~incoming~longwave__energy_flux", + "SPFH_2maboveground": "atmosphere_air_water~vapor__relative_saturation", + "UGRD_10maboveground": "land_surface_wind__x_component_of_velocity", + "VGRD_10maboveground": "land_surface_wind__y_component_of_velocity", + "PRES_surface": "land_surface_air__pressure", + "DSWRF_surface": "land_surface_radiation~incoming~shortwave__energy_flux", + "DLWRF_surface": "land_surface_radiation~incoming~longwave__energy_flux" + } + } + } + ], + "uses_forcing_file": false + } + } + ], + "forcing": { + "file_pattern": ".*{{id}}.*..csv", + "path": "../data/forcing/", + "provider": "NullForcingProvider" + } + }, + "time": { + "start_time": "2015-12-01 00:00:00", + "end_time": "2015-12-01 04:00:00", + "output_interval": 3600 + } +} diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp new file mode 100644 index 0000000000..01e8f563e4 --- /dev/null +++ b/include/forcing/NullForcingProvider.hpp @@ -0,0 +1,271 @@ +#ifndef NGEN_NULLFORCING_H +#define NGEN_NULLFORCING_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CSV_Reader.h" +#include +#include +#include +#include "AorcForcing.hpp" +#include "GenericDataProvider.hpp" +#include "DataProviderSelectors.hpp" +#include +#include + +/** + * @brief Forcing class providing time-series precipiation forcing data to the model. + */ +class NullForcingProvider : public data_access::GenericDataProvider +{ + public: + + typedef struct tm time_type; + + + NullForcingProvider(forcing_params forcing_config):start_date_time_epoch(forcing_config.simulation_start_t), + end_date_time_epoch(forcing_config.simulation_end_t), + current_date_time_epoch(forcing_config.simulation_start_t), + forcing_vector_index(-1) + { + read_csv(); + } + + // BEGIN DataProvider interface methods + + /** + * @brief the inclusive beginning of the period of time over which this instance can provide data for this forcing. + * + * @return The inclusive beginning of the period of time over which this instance can provide this data. + */ + long get_data_start_time() override { + //FIXME: Trace this back and you will find that it is the simulation start time, not having anything to do with the forcing at all. + // Apparently this "worked", but at a minimum the description above is false. + //return start_date_time_epoch; + // LONG_MIN is a large negative number + return 0; + } + + /** + * @brief the exclusive ending of the period of time over which this instance can provide data for this forcing. + * + * @return The exclusive ending of the period of time over which this instance can provide this data. + */ + long get_data_stop_time() override { + //return end_date_time_epoch; + return LONG_MAX; + } + + /** + * @brief the duration of one record of this forcing source + * + * @return The duration of one record of this forcing source + */ + long record_duration() override { + //return time_epoch_vector[1] - time_epoch_vector[0]; + //TODO find a more general way to set it + long timestep_size = 3600; + return timestep_size; + } + + /** + * Get the index of the forcing time step that contains the given point in time. + * + * An @ref std::out_of_range exception should be thrown if the time is not in any time step. + * + * @param epoch_time The point in time, as a seconds-based epoch time. + * @return The index of the forcing time step that contains the given point in time. + * @throws std::out_of_range If the given point is not in any time step. + */ + size_t get_ts_index_for_time(const time_t &epoch_time) override { + return 0; + } + + /** + * Get the value of a forcing property for an arbitrary time period, converting units if needed. + * + * An @ref std::out_of_range exception should be thrown if the data for the time period is not available. + * + * @param selector Object storing information about the data to be queried + * @param m methode to resample data if needed + * @return The value of the forcing property for the described time period, with units converted if needed. + * @throws std::out_of_range If data for the time period is not available. + */ + double get_value(const CatchmentAggrDataSelector& selector, data_access::ReSampleMethod m) override + { + throw std::runtime_error("Called get_value function in NullDataProvider"); + } + + virtual std::vector get_values(const CatchmentAggrDataSelector& selector, data_access::ReSampleMethod m) override + { + throw std::runtime_error("Called get_values function in NullDataProvider"); + } + + + /** + * Get whether a param's value is an aggregate sum over the entire time step. + * + * Certain params, like rain fall, are aggregated sums over an entire time step. Others, such as pressure, are not + * such sums and instead something else like an instantaneous reading or an average value over the time step. + * + * It may be the case that forcing data is needed for some discretization different than the forcing time step. + * These values can be calculated (or at least approximated), but doing so requires knowing which values are summed + * versus not. + * + * @param name The name of the forcing param for which the current value is desired. + * @return Whether the param's value is an aggregate sum. + */ + //TODO this function doesn't seem being used + inline bool is_param_sum_over_time_step(const std::string& name) { + if (name == CSDMS_STD_NAME_RAIN_VOLUME_FLUX) { + return true; + } + if (name == CSDMS_STD_NAME_SOLAR_SHORTWAVE) { + return true; + } + if (name == CSDMS_STD_NAME_SOLAR_LONGWAVE) { + return true; + } + if (name == CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE) { + return true; + } + return false; + } + + /** + * Get whether a property's per-time-step values are each an aggregate sum over the entire time step. + * + * Certain properties, like rain fall, are aggregated sums over an entire time step. Others, such as pressure, + * are not such sums and instead something else like an instantaneous reading or an average value. + * + * It may be the case that forcing data is needed for some discretization different than the forcing time step. + * This aspect must be known in such cases to perform the appropriate value interpolation. + * + * @param name The name of the forcing property for which the current value is desired. + * @return Whether the property's value is an aggregate sum. + */ + //TODO this one used in Bmi_Module_Formulation.hpp and Bmi_Multi_Formulation.hpp + inline bool is_property_sum_over_time_step(const std::string& name) override { + return is_param_sum_over_time_step(name); + } + + const std::vector &get_available_variable_names() override { + return available_forcings; + } + + private: + + /** + * @brief Checks forcing vector index bounds and adjusts index if out of vector bounds + * /// \todo: Bounds checking is based on precipitation vector. Consider potential for vectors of different sizes and indices. + */ + inline void check_forcing_vector_index_bounds() + { + //Check if forcing index is less than zero and if so, set to zero. + if (forcing_vector_index < 0) + { + forcing_vector_index = 0; + /// \todo: Return appropriate warning + std::cout << "WARNING: Forcing vector index is less than zero. Therefore, setting index to zero." << std::endl; + } + + //Check if forcing index is greater than or equal to the size of the size of the time vector and if so, set to zero. + else if (forcing_vector_index >= time_epoch_vector.size()) + { + forcing_vector_index = time_epoch_vector.size() - 1; + /// \todo: Return appropriate warning + std::cout << "WARNING: Reached beyond the size of the forcing vector. Therefore, setting index to last value of the vector." << std::endl; + } + + return; + } + + /** + * Get the current value of a forcing param identified by its name. + * + * @param name The name of the forcing param for which the current value is desired. + * @param index The index of the desired forcing time step from which to obtain the value. + * @return The particular param's value at the given forcing time step. + */ + inline double get_value_for_param_name(const std::string& name, int index) { + if (index >= time_epoch_vector.size() ) { + throw std::out_of_range("Forcing had bad index " + std::to_string(index) + " for value lookup of " + name); + } + + std::string can_name = name; + if(data_access::WellKnownFields.count(can_name) > 0){ + auto t = data_access::WellKnownFields.find(can_name)->second; + can_name = std::get<0>(t); + } + + if (forcing_vectors.count(can_name) > 0) { + return forcing_vectors[can_name].at(index); + } + else { + throw std::runtime_error("Cannot get forcing value for unrecognized parameter name '" + name + "'."); + } + } + + /** + * @brief Read Forcing Data from CSV + * Reads only data within the specified model start and end date-times. + * @param file_name Forcing file name + */ + // No file to read + void read_csv() + { + //std::string var_name = CSDMS_STD_NAME_RAIN_VOLUME_FLUX; + available_forcings.push_back("time"); + std::string var_name = "precip"; + std::string units = ""; + auto wkf = data_access::WellKnownFields.find(var_name); + if(wkf != data_access::WellKnownFields.end()){ + units = units.empty() ? std::get<1>(wkf->second) : units; + available_forcings.push_back(var_name); // Allow lookup by non-canonical name + available_forcings_units[var_name] = units; // Allow lookup of units by non-canonical name + var_name = std::get<0>(wkf->second); // Use the CSDMS name from here on + } + } + + std::vector available_forcings; + std::unordered_map available_forcings_units; + + /// \todo: Look into aggregation of data, relevant libraries, and storing frequency information + std::unordered_map> forcing_vectors; + + /// \todo: Consider making epoch time the iterator + std::vector time_epoch_vector; + int forcing_vector_index; + + /// \todo: Are these used? + double precipitation_rate_meters_per_second; + double air_temperature_fahrenheit; + + double latitude; //latitude (degrees_north) + double longitude; //longitude (degrees_east) + int catchment_id; + int day_of_year; + std::string forcing_file_name; + + std::shared_ptr start_date_time; + std::shared_ptr end_date_time; + + time_t start_date_time_epoch; + time_t end_date_time_epoch; + time_t current_date_time_epoch; +}; + +/// \todo Consider aggregating precipiation data +/// \todo Make CSV forcing a subclass +/// \todo Consider passing grid to class +/// \todo Consider following GDAL API functionality + +#endif // NGEN_NULLFORCING_H diff --git a/include/realizations/catchment/Bmi_Multi_Formulation.hpp b/include/realizations/catchment/Bmi_Multi_Formulation.hpp index e7d056f874..f4dfa561d0 100644 --- a/include/realizations/catchment/Bmi_Multi_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Multi_Formulation.hpp @@ -285,6 +285,7 @@ namespace realization { for(std::map>::iterator iter = availableData.begin(); iter != availableData.end(); ++iter) { var_name = iter->first; + //std::cout << "availableData: var_name = " << var_name << std::endl; //TODO: Find a probably more performant way than trial and exception here. try { time_t rv = availableData[var_name]->get_data_stop_time(); diff --git a/include/realizations/catchment/Formulation_Constructors.hpp b/include/realizations/catchment/Formulation_Constructors.hpp index dfd34ada31..7e6d46ea36 100644 --- a/include/realizations/catchment/Formulation_Constructors.hpp +++ b/include/realizations/catchment/Formulation_Constructors.hpp @@ -16,6 +16,7 @@ #include "Bmi_Py_Formulation.hpp" #include #include "CsvPerFeatureForcingProvider.hpp" +#include "NullForcingProvider.hpp" #ifdef NETCDF_ACTIVE #include "NetCDFPerFeatureDataProvider.hpp" #endif @@ -64,6 +65,9 @@ namespace realization { fp = data_access::NetCDFPerFeatureDataProvider::get_shared_provider(forcing_config.path, forcing_config.simulation_start_t, forcing_config.simulation_end_t, output_stream); } #endif + else if (forcing_config.provider == "NullForcingProvider"){ + fp = std::make_shared(forcing_config); + } else { // Some unknown string in the provider field? throw std::runtime_error( "Invalid formulation forcing provider configuration! identifier: \"" + identifier + From 11035bf81239b7b2cd5f8799ce1f98c1bd5c5e81 Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Fri, 4 Aug 2023 19:24:44 +0000 Subject: [PATCH 02/11] Revise config jason to be force-less. --- data/example_bmi_realization_config_output.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/example_bmi_realization_config_output.json b/data/example_bmi_realization_config_output.json index 4512c24f43..0848a9c77c 100644 --- a/data/example_bmi_realization_config_output.json +++ b/data/example_bmi_realization_config_output.json @@ -49,8 +49,7 @@ } ], "forcing": { - "file_pattern": ".*{{id}}.*..csv", - "path": "../data/forcing/", + "path": "", "provider": "NullForcingProvider" } }, From b08063273e13cc2757c278af2424d9899507f345 Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Mon, 7 Aug 2023 19:54:45 +0000 Subject: [PATCH 03/11] Revised NullForcingProvider incorporating Matt's comments --- include/forcing/NullForcingProvider.hpp | 122 ++---------------------- 1 file changed, 9 insertions(+), 113 deletions(-) diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp index 01e8f563e4..b08ab6cb65 100644 --- a/include/forcing/NullForcingProvider.hpp +++ b/include/forcing/NullForcingProvider.hpp @@ -11,7 +11,6 @@ #include #include #include -#include "CSV_Reader.h" #include #include #include @@ -36,7 +35,7 @@ class NullForcingProvider : public data_access::GenericDataProvider current_date_time_epoch(forcing_config.simulation_start_t), forcing_vector_index(-1) { - read_csv(); + set_available_forcings(); } // BEGIN DataProvider interface methods @@ -50,7 +49,7 @@ class NullForcingProvider : public data_access::GenericDataProvider //FIXME: Trace this back and you will find that it is the simulation start time, not having anything to do with the forcing at all. // Apparently this "worked", but at a minimum the description above is false. //return start_date_time_epoch; - // LONG_MIN is a large negative number + // LONG_MIN is a large negative number, so we return 0 as the starting time return 0; } @@ -70,10 +69,7 @@ class NullForcingProvider : public data_access::GenericDataProvider * @return The duration of one record of this forcing source */ long record_duration() override { - //return time_epoch_vector[1] - time_epoch_vector[0]; - //TODO find a more general way to set it - long timestep_size = 3600; - return timestep_size; + return 1; } /** @@ -96,8 +92,7 @@ class NullForcingProvider : public data_access::GenericDataProvider * * @param selector Object storing information about the data to be queried * @param m methode to resample data if needed - * @return The value of the forcing property for the described time period, with units converted if needed. - * @throws std::out_of_range If data for the time period is not available. + * @throws std::runtime_error as this provider does not provide forcing value/values */ double get_value(const CatchmentAggrDataSelector& selector, data_access::ReSampleMethod m) override { @@ -121,23 +116,11 @@ class NullForcingProvider : public data_access::GenericDataProvider * versus not. * * @param name The name of the forcing param for which the current value is desired. - * @return Whether the param's value is an aggregate sum. + * @throw std::runtime_error as this provider does not knowledge of the forcing variables */ //TODO this function doesn't seem being used inline bool is_param_sum_over_time_step(const std::string& name) { - if (name == CSDMS_STD_NAME_RAIN_VOLUME_FLUX) { - return true; - } - if (name == CSDMS_STD_NAME_SOLAR_SHORTWAVE) { - return true; - } - if (name == CSDMS_STD_NAME_SOLAR_LONGWAVE) { - return true; - } - if (name == CSDMS_STD_NAME_LIQUID_EQ_PRECIP_RATE) { - return true; - } - return false; + throw std::runtime_error("Got request for variable " + name + " but no such variable is provided NullForcingProvider." + SOURCE_LOC); } /** @@ -163,109 +146,22 @@ class NullForcingProvider : public data_access::GenericDataProvider private: - /** - * @brief Checks forcing vector index bounds and adjusts index if out of vector bounds - * /// \todo: Bounds checking is based on precipitation vector. Consider potential for vectors of different sizes and indices. - */ - inline void check_forcing_vector_index_bounds() - { - //Check if forcing index is less than zero and if so, set to zero. - if (forcing_vector_index < 0) - { - forcing_vector_index = 0; - /// \todo: Return appropriate warning - std::cout << "WARNING: Forcing vector index is less than zero. Therefore, setting index to zero." << std::endl; - } - - //Check if forcing index is greater than or equal to the size of the size of the time vector and if so, set to zero. - else if (forcing_vector_index >= time_epoch_vector.size()) - { - forcing_vector_index = time_epoch_vector.size() - 1; - /// \todo: Return appropriate warning - std::cout << "WARNING: Reached beyond the size of the forcing vector. Therefore, setting index to last value of the vector." << std::endl; - } - - return; - } /** - * Get the current value of a forcing param identified by its name. - * - * @param name The name of the forcing param for which the current value is desired. - * @param index The index of the desired forcing time step from which to obtain the value. - * @return The particular param's value at the given forcing time step. + * @brief set_available_forcings so that Formulation_Manager has a non-empty variable name to access simulation time */ - inline double get_value_for_param_name(const std::string& name, int index) { - if (index >= time_epoch_vector.size() ) { - throw std::out_of_range("Forcing had bad index " + std::to_string(index) + " for value lookup of " + name); - } - - std::string can_name = name; - if(data_access::WellKnownFields.count(can_name) > 0){ - auto t = data_access::WellKnownFields.find(can_name)->second; - can_name = std::get<0>(t); - } - - if (forcing_vectors.count(can_name) > 0) { - return forcing_vectors[can_name].at(index); - } - else { - throw std::runtime_error("Cannot get forcing value for unrecognized parameter name '" + name + "'."); - } - } - - /** - * @brief Read Forcing Data from CSV - * Reads only data within the specified model start and end date-times. - * @param file_name Forcing file name - */ - // No file to read - void read_csv() + void set_available_forcings() { - //std::string var_name = CSDMS_STD_NAME_RAIN_VOLUME_FLUX; - available_forcings.push_back("time"); - std::string var_name = "precip"; - std::string units = ""; - auto wkf = data_access::WellKnownFields.find(var_name); - if(wkf != data_access::WellKnownFields.end()){ - units = units.empty() ? std::get<1>(wkf->second) : units; - available_forcings.push_back(var_name); // Allow lookup by non-canonical name - available_forcings_units[var_name] = units; // Allow lookup of units by non-canonical name - var_name = std::get<0>(wkf->second); // Use the CSDMS name from here on - } + available_forcings.push_back("dummy"); } std::vector available_forcings; - std::unordered_map available_forcings_units; - - /// \todo: Look into aggregation of data, relevant libraries, and storing frequency information - std::unordered_map> forcing_vectors; - /// \todo: Consider making epoch time the iterator - std::vector time_epoch_vector; int forcing_vector_index; - /// \todo: Are these used? - double precipitation_rate_meters_per_second; - double air_temperature_fahrenheit; - - double latitude; //latitude (degrees_north) - double longitude; //longitude (degrees_east) - int catchment_id; - int day_of_year; - std::string forcing_file_name; - - std::shared_ptr start_date_time; - std::shared_ptr end_date_time; - time_t start_date_time_epoch; time_t end_date_time_epoch; time_t current_date_time_epoch; }; -/// \todo Consider aggregating precipiation data -/// \todo Make CSV forcing a subclass -/// \todo Consider passing grid to class -/// \todo Consider following GDAL API functionality - #endif // NGEN_NULLFORCING_H From 24bb61ba863b04cf21cadbd693c762a1d06e40b2 Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Tue, 8 Aug 2023 19:29:38 +0000 Subject: [PATCH 04/11] Remove is_param_sum_over_time_step function. --- include/forcing/NullForcingProvider.hpp | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp index b08ab6cb65..a154a5b2ef 100644 --- a/include/forcing/NullForcingProvider.hpp +++ b/include/forcing/NullForcingProvider.hpp @@ -104,25 +104,6 @@ class NullForcingProvider : public data_access::GenericDataProvider throw std::runtime_error("Called get_values function in NullDataProvider"); } - - /** - * Get whether a param's value is an aggregate sum over the entire time step. - * - * Certain params, like rain fall, are aggregated sums over an entire time step. Others, such as pressure, are not - * such sums and instead something else like an instantaneous reading or an average value over the time step. - * - * It may be the case that forcing data is needed for some discretization different than the forcing time step. - * These values can be calculated (or at least approximated), but doing so requires knowing which values are summed - * versus not. - * - * @param name The name of the forcing param for which the current value is desired. - * @throw std::runtime_error as this provider does not knowledge of the forcing variables - */ - //TODO this function doesn't seem being used - inline bool is_param_sum_over_time_step(const std::string& name) { - throw std::runtime_error("Got request for variable " + name + " but no such variable is provided NullForcingProvider." + SOURCE_LOC); - } - /** * Get whether a property's per-time-step values are each an aggregate sum over the entire time step. * @@ -137,7 +118,7 @@ class NullForcingProvider : public data_access::GenericDataProvider */ //TODO this one used in Bmi_Module_Formulation.hpp and Bmi_Multi_Formulation.hpp inline bool is_property_sum_over_time_step(const std::string& name) override { - return is_param_sum_over_time_step(name); + throw std::runtime_error("Got request for variable " + name + " but no such variable is provided by NullForcingProvider." + SOURCE_LOC); } const std::vector &get_available_variable_names() override { From e557654171af68bb221e6bb87d9f5a42cc1747ee Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Tue, 5 Sep 2023 20:34:19 +0000 Subject: [PATCH 05/11] Remove presetting available forcings --- include/forcing/NullForcingProvider.hpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp index a154a5b2ef..2aedc14040 100644 --- a/include/forcing/NullForcingProvider.hpp +++ b/include/forcing/NullForcingProvider.hpp @@ -34,9 +34,7 @@ class NullForcingProvider : public data_access::GenericDataProvider end_date_time_epoch(forcing_config.simulation_end_t), current_date_time_epoch(forcing_config.simulation_start_t), forcing_vector_index(-1) - { - set_available_forcings(); - } + {} // BEGIN DataProvider interface methods @@ -127,15 +125,6 @@ class NullForcingProvider : public data_access::GenericDataProvider private: - - /** - * @brief set_available_forcings so that Formulation_Manager has a non-empty variable name to access simulation time - */ - void set_available_forcings() - { - available_forcings.push_back("dummy"); - } - std::vector available_forcings; int forcing_vector_index; From 57849f97f0cfafefb8cae4228f2cae0b9e6669e9 Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Fri, 8 Sep 2023 16:28:48 +0000 Subject: [PATCH 06/11] Remove unused include header names --- include/forcing/NullForcingProvider.hpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp index 2aedc14040..45b71eac83 100644 --- a/include/forcing/NullForcingProvider.hpp +++ b/include/forcing/NullForcingProvider.hpp @@ -2,15 +2,11 @@ #define NGEN_NULLFORCING_H #include -#include #include #include #include #include #include -#include -#include -#include #include #include #include @@ -18,10 +14,9 @@ #include "GenericDataProvider.hpp" #include "DataProviderSelectors.hpp" #include -#include /** - * @brief Forcing class providing time-series precipiation forcing data to the model. + * @brief This forcing class does not, in fact, provide any forcing data! Thus the name NullForcingProvider. */ class NullForcingProvider : public data_access::GenericDataProvider { From b5322d7d8100489ef0f36a4bb3ddf95ed27c4994 Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Fri, 15 Sep 2023 14:59:38 +0000 Subject: [PATCH 07/11] Revise Bmi_Multi_Formulation and remove some include headers from NullForcingProvider --- include/forcing/NullForcingProvider.hpp | 6 +----- .../catchment/Bmi_Multi_Formulation.hpp | 20 +++---------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp index 45b71eac83..9d49bb6346 100644 --- a/include/forcing/NullForcingProvider.hpp +++ b/include/forcing/NullForcingProvider.hpp @@ -2,21 +2,17 @@ #define NGEN_NULLFORCING_H #include -#include -#include #include -#include #include #include #include -#include #include "AorcForcing.hpp" #include "GenericDataProvider.hpp" #include "DataProviderSelectors.hpp" #include /** - * @brief This forcing class does not, in fact, provide any forcing data! Thus the name NullForcingProvider. + * @brief Forcing class providing time-series precipiation forcing data to the model. */ class NullForcingProvider : public data_access::GenericDataProvider { diff --git a/include/realizations/catchment/Bmi_Multi_Formulation.hpp b/include/realizations/catchment/Bmi_Multi_Formulation.hpp index f4dfa561d0..a9b5498bd8 100644 --- a/include/realizations/catchment/Bmi_Multi_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Multi_Formulation.hpp @@ -277,25 +277,11 @@ namespace realization { */ //time_t get_forcing_output_time_end(const std::string &forcing_name) { time_t get_variable_time_end(const std::string &variable_name) { - // If not found ... + // when unspecified, assume all data is available for the same range. + // If no var_name, use forcing ... std::string var_name = variable_name; if(var_name == "*" || var_name == ""){ - // when unspecified, assume all data is available for the same range. - // Find one that successfully returns... - for(std::map>::iterator iter = availableData.begin(); iter != availableData.end(); ++iter) - { - var_name = iter->first; - //std::cout << "availableData: var_name = " << var_name << std::endl; - //TODO: Find a probably more performant way than trial and exception here. - try { - time_t rv = availableData[var_name]->get_data_stop_time(); - return rv; - } - catch (...){ - continue; - } - break; - } + return forcing->get_data_stop_time(); } // If not found ... if (availableData.empty() || availableData.find(var_name) == availableData.end()) { From d0d52c44491ba9723c1d660b79252283582adeae Mon Sep 17 00:00:00 2001 From: Shengting Cui Date: Tue, 19 Sep 2023 19:00:42 +0000 Subject: [PATCH 08/11] Delete a redundant code line --- include/forcing/NullForcingProvider.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp index 9d49bb6346..91f524e1be 100644 --- a/include/forcing/NullForcingProvider.hpp +++ b/include/forcing/NullForcingProvider.hpp @@ -18,9 +18,6 @@ class NullForcingProvider : public data_access::GenericDataProvider { public: - typedef struct tm time_type; - - NullForcingProvider(forcing_params forcing_config):start_date_time_epoch(forcing_config.simulation_start_t), end_date_time_epoch(forcing_config.simulation_end_t), current_date_time_epoch(forcing_config.simulation_start_t), From ffb493dc70d0a0852e7b160f9d8fd49e8717f889 Mon Sep 17 00:00:00 2001 From: Matt Williamson <87771120+mattw-nws@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:14:09 +0000 Subject: [PATCH 09/11] Adjusted example file to use no new models or capabilities --- ...le_bmi_multi_realization_config_w_nfp.json | 97 +++++++++++++++++++ ...example_bmi_realization_config_output.json | 61 ------------ 2 files changed, 97 insertions(+), 61 deletions(-) create mode 100644 data/example_bmi_multi_realization_config_w_nfp.json delete mode 100644 data/example_bmi_realization_config_output.json diff --git a/data/example_bmi_multi_realization_config_w_nfp.json b/data/example_bmi_multi_realization_config_w_nfp.json new file mode 100644 index 0000000000..4f8754a170 --- /dev/null +++ b/data/example_bmi_multi_realization_config_w_nfp.json @@ -0,0 +1,97 @@ +{ + "global": { + "formulations": [ + { + "name": "bmi_multi", + "params": { + "model_type_name": "bmi_multi_noahowp_cfe", + "forcing_file": "", + "init_config": "", + "allow_exceed_end_time": true, + "main_output_variable": "Q_OUT", + "modules": [ + { + "name": "bmi_c++", + "params": { + "model_type_name": "bmi_c++_sloth", + "library_file": "./extern/sloth/cmake_build/libslothmodel.so", + "init_config": "/dev/null", + "allow_exceed_end_time": true, + "main_output_variable": "z", + "uses_forcing_file": false, + "model_params": { + "ice_fraction_schaake(1,double,m,node)": 0.0, + "ice_fraction_xinan(1,double,1,node)": 0.0, + "soil_moisture_profile(1,double,1,node)": 0.0, + "land_surface_air__temperature(1,double,K,node)": 285.8000183105469, + "atmosphere_water__liquid_equivalent_precipitation_rate(1,double,mm/s,node)": 0.1, + "atmosphere_air_water~vapor__relative_saturation(1,double,1,node)": 0.00930000003427267, + "land_surface_wind__x_component_of_velocity(1,double,m/s,node)": -2.299999952316284, + "land_surface_wind__y_component_of_velocity(1,double,m/s,node)": 0.10000000149011612, + "land_surface_air__pressure(1,double,Pa,node)": 100310.0, + "land_surface_radiation~incoming~shortwave__energy_flux(1,double,W/m2,node)": 0.0, + "land_surface_radiation~incoming~longwave__energy_flux(1,double,W/m2,node)": 361.3000183105469 + } + } + }, + { + "name": "bmi_fortran", + "params": { + "model_type_name": "bmi_fortran_noahowp", + "library_file": "./extern/noah-owp-modular/cmake_build/libsurfacebmi.so", + "forcing_file": "", + "init_config": "./data/bmi/fortran/noah-owp-modular-init-{{id}}.namelist.input", + "allow_exceed_end_time": true, + "main_output_variable": "QINSUR", + "variables_names_map": { + "PRCPNONC": "atmosphere_water__liquid_equivalent_precipitation_rate", + "Q2": "atmosphere_air_water~vapor__relative_saturation", + "SFCTMP": "land_surface_air__temperature", + "UU": "land_surface_wind__x_component_of_velocity", + "VV": "land_surface_wind__y_component_of_velocity", + "LWDN": "land_surface_radiation~incoming~longwave__energy_flux", + "SOLDN": "land_surface_radiation~incoming~shortwave__energy_flux", + "SFCPRS": "land_surface_air__pressure" + }, + "uses_forcing_file": false + } + }, + { + "name": "bmi_c", + "params": { + "model_type_name": "bmi_c_cfe", + "library_file": "./extern/cfe/cmake_build/libcfebmi.so", + "forcing_file": "", + "init_config": "./data/bmi/c/cfe/{{id}}_bmi_config.ini", + "allow_exceed_end_time": true, + "main_output_variable": "Q_OUT", + "registration_function": "register_bmi_cfe", + "variables_names_map": { + "water_potential_evaporation_flux": "ETRAN", + "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 + } + } + ], + "uses_forcing_file": false + } + } + ], + "forcing": { + "provider": "NullForcingProvider", + "path": "" + } + }, + "time": { + "start_time": "2015-12-01 00:00:00", + "end_time": "2015-12-30 23:00:00", + "output_interval": 3600 + } +} diff --git a/data/example_bmi_realization_config_output.json b/data/example_bmi_realization_config_output.json deleted file mode 100644 index 0848a9c77c..0000000000 --- a/data/example_bmi_realization_config_output.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "global": { - "formulations": [ - { - "name": "bmi_multi", - "params": { - "model_type_name": "bmi_jinja_source", - "forcing_file": "", - "init_config": "", - "allow_exceed_end_time": true, - "main_output_variable": "TMP_2maboveground", - "output_bbox" : [4199,2099,3,3,17644200,5,2], - "modules": [ - { - "name": "bmi_python", - "params": { - "model_type_name": "jinjabmi", - "python_type": "jinjabmi.Jinja", - "init_config": "./jinja_grid_source.yml", - "allow_exceed_end_time": true, - "main_output_variable": "dummy_output_scalar", - "uses_forcing_file": false - } - }, - { - "name": "bmi_python", - "params": { - "python_type": "AORC_bmi_model.AORC_bmi_model", - "model_type_name": "AORC_bmi_model", - "init_config": "./config.yml", - "main_output_variable": "APCP_surface", - "uses_forcing_file": false, - "output_bbox" : [4199,2099,3,3,17644200,5,2], - "variables_names_map_disable" : { - "APCP_surface": "atmosphere_water__liquid_equivalent_precipitation_rate", - "TMP_2maboveground": "land_surface_radiation~incoming~longwave__energy_flux", - "SPFH_2maboveground": "atmosphere_air_water~vapor__relative_saturation", - "UGRD_10maboveground": "land_surface_wind__x_component_of_velocity", - "VGRD_10maboveground": "land_surface_wind__y_component_of_velocity", - "PRES_surface": "land_surface_air__pressure", - "DSWRF_surface": "land_surface_radiation~incoming~shortwave__energy_flux", - "DLWRF_surface": "land_surface_radiation~incoming~longwave__energy_flux" - } - } - } - ], - "uses_forcing_file": false - } - } - ], - "forcing": { - "path": "", - "provider": "NullForcingProvider" - } - }, - "time": { - "start_time": "2015-12-01 00:00:00", - "end_time": "2015-12-01 04:00:00", - "output_interval": 3600 - } -} From 63ccc9ec04d961ff459f171743352fa54241cffa Mon Sep 17 00:00:00 2001 From: Matt Williamson <87771120+mattw-nws@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:40:40 +0000 Subject: [PATCH 10/11] Removed requirement for `"path"` in forcing config --- data/example_bmi_multi_realization_config_w_nfp.json | 3 +-- include/realizations/catchment/Formulation_Manager.hpp | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/data/example_bmi_multi_realization_config_w_nfp.json b/data/example_bmi_multi_realization_config_w_nfp.json index 4f8754a170..61b26f25c0 100644 --- a/data/example_bmi_multi_realization_config_w_nfp.json +++ b/data/example_bmi_multi_realization_config_w_nfp.json @@ -85,8 +85,7 @@ } ], "forcing": { - "provider": "NullForcingProvider", - "path": "" + "provider": "NullForcingProvider" } }, "time": { diff --git a/include/realizations/catchment/Formulation_Manager.hpp b/include/realizations/catchment/Formulation_Manager.hpp index 45b7e55c2d..1ad5d8c4d7 100644 --- a/include/realizations/catchment/Formulation_Manager.hpp +++ b/include/realizations/catchment/Formulation_Manager.hpp @@ -414,7 +414,10 @@ namespace realization { } forcing_params get_forcing_params(geojson::PropertyMap &forcing_prop_map, std::string identifier, simulation_time_params &simulation_time_config) { - std::string path = forcing_prop_map.at("path").as_string(); + std::string path = ""; + if(forcing_prop_map.count("path") != 0){ + path = forcing_prop_map.at("path").as_string(); + } std::string provider = ""; if(forcing_prop_map.count("provider") != 0){ provider = forcing_prop_map.at("provider").as_string(); From 04853cf50cc2f645f27728279ac4905b9f10d821 Mon Sep 17 00:00:00 2001 From: Matt Williamson <87771120+mattw-nws@users.noreply.github.com> Date: Thu, 21 Sep 2023 15:29:17 +0000 Subject: [PATCH 11/11] Cleanup from review feedback --- include/forcing/NullForcingProvider.hpp | 74 ++----------------- .../catchment/Formulation_Constructors.hpp | 2 +- 2 files changed, 6 insertions(+), 70 deletions(-) diff --git a/include/forcing/NullForcingProvider.hpp b/include/forcing/NullForcingProvider.hpp index 91f524e1be..185392684c 100644 --- a/include/forcing/NullForcingProvider.hpp +++ b/include/forcing/NullForcingProvider.hpp @@ -3,83 +3,37 @@ #include #include -#include -#include -#include -#include "AorcForcing.hpp" +#include +#include #include "GenericDataProvider.hpp" -#include "DataProviderSelectors.hpp" -#include /** - * @brief Forcing class providing time-series precipiation forcing data to the model. + * @brief Forcing class that returns no variables to the simulation--use this e.g. if a BMI model provides forcing data. */ class NullForcingProvider : public data_access::GenericDataProvider { public: - NullForcingProvider(forcing_params forcing_config):start_date_time_epoch(forcing_config.simulation_start_t), - end_date_time_epoch(forcing_config.simulation_end_t), - current_date_time_epoch(forcing_config.simulation_start_t), - forcing_vector_index(-1) - {} + NullForcingProvider(){} // BEGIN DataProvider interface methods - /** - * @brief the inclusive beginning of the period of time over which this instance can provide data for this forcing. - * - * @return The inclusive beginning of the period of time over which this instance can provide this data. - */ long get_data_start_time() override { - //FIXME: Trace this back and you will find that it is the simulation start time, not having anything to do with the forcing at all. - // Apparently this "worked", but at a minimum the description above is false. - //return start_date_time_epoch; - // LONG_MIN is a large negative number, so we return 0 as the starting time return 0; } - /** - * @brief the exclusive ending of the period of time over which this instance can provide data for this forcing. - * - * @return The exclusive ending of the period of time over which this instance can provide this data. - */ long get_data_stop_time() override { - //return end_date_time_epoch; return LONG_MAX; } - /** - * @brief the duration of one record of this forcing source - * - * @return The duration of one record of this forcing source - */ long record_duration() override { return 1; } - /** - * Get the index of the forcing time step that contains the given point in time. - * - * An @ref std::out_of_range exception should be thrown if the time is not in any time step. - * - * @param epoch_time The point in time, as a seconds-based epoch time. - * @return The index of the forcing time step that contains the given point in time. - * @throws std::out_of_range If the given point is not in any time step. - */ size_t get_ts_index_for_time(const time_t &epoch_time) override { return 0; } - /** - * Get the value of a forcing property for an arbitrary time period, converting units if needed. - * - * An @ref std::out_of_range exception should be thrown if the data for the time period is not available. - * - * @param selector Object storing information about the data to be queried - * @param m methode to resample data if needed - * @throws std::runtime_error as this provider does not provide forcing value/values - */ double get_value(const CatchmentAggrDataSelector& selector, data_access::ReSampleMethod m) override { throw std::runtime_error("Called get_value function in NullDataProvider"); @@ -90,19 +44,6 @@ class NullForcingProvider : public data_access::GenericDataProvider throw std::runtime_error("Called get_values function in NullDataProvider"); } - /** - * Get whether a property's per-time-step values are each an aggregate sum over the entire time step. - * - * Certain properties, like rain fall, are aggregated sums over an entire time step. Others, such as pressure, - * are not such sums and instead something else like an instantaneous reading or an average value. - * - * It may be the case that forcing data is needed for some discretization different than the forcing time step. - * This aspect must be known in such cases to perform the appropriate value interpolation. - * - * @param name The name of the forcing property for which the current value is desired. - * @return Whether the property's value is an aggregate sum. - */ - //TODO this one used in Bmi_Module_Formulation.hpp and Bmi_Multi_Formulation.hpp inline bool is_property_sum_over_time_step(const std::string& name) override { throw std::runtime_error("Got request for variable " + name + " but no such variable is provided by NullForcingProvider." + SOURCE_LOC); } @@ -112,14 +53,9 @@ class NullForcingProvider : public data_access::GenericDataProvider } private: - + std::vector available_forcings; - int forcing_vector_index; - - time_t start_date_time_epoch; - time_t end_date_time_epoch; - time_t current_date_time_epoch; }; #endif // NGEN_NULLFORCING_H diff --git a/include/realizations/catchment/Formulation_Constructors.hpp b/include/realizations/catchment/Formulation_Constructors.hpp index 7e6d46ea36..a5cf348f04 100644 --- a/include/realizations/catchment/Formulation_Constructors.hpp +++ b/include/realizations/catchment/Formulation_Constructors.hpp @@ -66,7 +66,7 @@ namespace realization { } #endif else if (forcing_config.provider == "NullForcingProvider"){ - fp = std::make_shared(forcing_config); + fp = std::make_shared(); } else { // Some unknown string in the provider field? throw std::runtime_error(