Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null/Constant Forcing Provider #601

Merged
merged 11 commits into from
Sep 21, 2023
61 changes: 61 additions & 0 deletions data/example_bmi_realization_config_output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"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
}
}
137 changes: 137 additions & 0 deletions include/forcing/NullForcingProvider.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#ifndef NGEN_NULLFORCING_H
#define NGEN_NULLFORCING_H

#include <vector>
PhilMiller marked this conversation as resolved.
Show resolved Hide resolved
#include <set>
#include <cmath>
#include <algorithm>
#include <string>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <ctime>
#include <time.h>
#include <memory>
#include "AorcForcing.hpp"
#include "GenericDataProvider.hpp"
#include "DataProviderSelectors.hpp"
#include <exception>
#include <UnitsHelper.hpp>

/**
* @brief Forcing class providing time-series precipiation forcing data to the model.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update this doc string to reflect that this forcing provider does not, in fact, provide any forcing data!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am on it.

*/
class NullForcingProvider : public data_access::GenericDataProvider
{
public:

typedef struct tm time_type;

PhilMiller marked this conversation as resolved.
Show resolved Hide resolved

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)
{}

// 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.
mattw-nws marked this conversation as resolved.
Show resolved Hide resolved
// 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");
}

virtual std::vector<double> get_values(const CatchmentAggrDataSelector& selector, data_access::ReSampleMethod m) override
{
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);
}

const std::vector<std::string> &get_available_variable_names() override {
PhilMiller marked this conversation as resolved.
Show resolved Hide resolved
return available_forcings;
}

private:

std::vector<std::string> 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
1 change: 1 addition & 0 deletions include/realizations/catchment/Bmi_Multi_Formulation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ namespace realization {
for(std::map<std::string,std::shared_ptr<data_access::GenericDataProvider>>::iterator iter = availableData.begin(); iter != availableData.end(); ++iter)
{
var_name = iter->first;
//std::cout << "availableData: var_name = " << var_name << std::endl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this function in Bmi_Multi_Formulation almost certainly needs the same change as was done in #624 for the NullForcingProvider to not break things. Once this provider gets used, if Bmi_Multi_Formulation::get_variable_time_end is called, it will probably throw on line 302 here because no forcing variables will be found to return an end time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, agree.
There is also the same codes in record_duration() function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point, I thought I deleted many of the header lines. Don't know why they are still there. Will do.

//TODO: Find a probably more performant way than trial and exception here.
try {
time_t rv = availableData[var_name]->get_data_stop_time();
Expand Down
4 changes: 4 additions & 0 deletions include/realizations/catchment/Formulation_Constructors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Bmi_Py_Formulation.hpp"
#include <GenericDataProvider.hpp>
#include "CsvPerFeatureForcingProvider.hpp"
#include "NullForcingProvider.hpp"
#ifdef NETCDF_ACTIVE
#include "NetCDFPerFeatureDataProvider.hpp"
#endif
Expand Down Expand Up @@ -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<NullForcingProvider>(forcing_config);
}
else { // Some unknown string in the provider field?
throw std::runtime_error(
"Invalid formulation forcing provider configuration! identifier: \"" + identifier +
Expand Down