Program Listing for File module_base.hpp#
↰ Return to documentation for file (src/modules/module_base.hpp)
//
// Canadian Hydrological Model - The Canadian Hydrological Model (CHM) is a novel
// modular unstructured mesh based approach for hydrological modelling
// Copyright (C) 2018 Christopher Marsh
//
// This file is part of Canadian Hydrological Model.
//
// Canadian Hydrological Model is free software: you can redistribute it and/or
// modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Canadian Hydrological Model is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Canadian Hydrological Model. If not, see
// <http://www.gnu.org/licenses/>.
//
#pragma once
#include <string>
#include <memory>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
namespace pt = boost::property_tree;
#include "triangulation.hpp"
#include "global.hpp"
#include "timeseries/netcdf.hpp"
#include "factory.hpp"
//Create a process modules group in the doxygen docs to add individual modules to
typedef pt::ptree config_file;
enum class SpatialType
{
local,
neighbor,
distance
};
struct variable_info
{
bool is_distance_set;
SpatialType spatial_type;
double spatial_distance;
std::string name;
// no need for default constructor
variable_info() = delete;
// constructing by name only assumes local
variable_info(std::string name) : spatial_type{SpatialType::local}, name{name} {}
// constructing by spatial type explicitly
// - if type is distance, need to make sure distance gets set before usage (useful for setting via options)
variable_info(std::string name, SpatialType st) : spatial_type{st}, name{name}
{
switch (st)
{
case SpatialType::distance:
is_distance_set = false;
default:
is_distance_set = false;
}
}
// explicit construction with distance intended for modules with a fixed distance
variable_info(std::string name, SpatialType st, double distance) : spatial_type{st}, name{name}
{
switch (st)
{
case SpatialType::distance:
is_distance_set = true;
spatial_distance = distance;
default:
BOOST_THROW_EXCEPTION(
module_error() << errstr_info(
"Distance specified with local or neighbor SpatialType. This is not allowed."));
}
}
};
class module_base
{
public:
enum class parallel
{
data,
domain
};
std::string ID;
pt::ptree cfg;
int IDnum;
std::shared_ptr<global> global_param;
module_base(){};
module_base(std::string name = "",
parallel type = parallel::data,
config_file input_cfg = pt::basic_ptree<std::string,std::string>())
: ID(name), cfg(input_cfg), IDnum(0),_parallel_type(type)
{
_provides = std::make_shared<std::vector<variable_info>>();
_provides_parameters = std::make_shared<std::vector<std::string>>();
_vectors = std::make_shared<std::vector<std::string>>();
_depends = std::make_shared<std::vector<variable_info>>();
_depends_from_met = std::make_shared<std::vector<std::string>>();
_optional = std::make_shared<std::vector<std::string>>();
_conflicts = std::make_shared<std::vector<std::string>>(); // modules that we explicitly cannot be run
// alongside. Use sparingly
global_param = nullptr;
//nothing
};
virtual ~module_base()
{
//nothing
};
virtual void checkpoint(mesh& domain, netcdf& chkpt)
{
//TODO: Add default check for the assumption that module does not support serialization
};
virtual void load_checkpoint(mesh& domain, netcdf& chkpt)
{
//TODO: Add default check for the assumption that module does not support serialization
};
virtual void run(mesh_elem&face)
{
};
/*
* Needs to be implemented by each domain parallel module. This will be called and executed for each timestep. Unique to domain parallel modules.
* \param domain The entier terrain mesh
* \param global_parama A pointer to the shared global paramter space with domain-wide paramters
*/
virtual void run(mesh& domain)
{
};
/*
* Optional function to run after the dependency constructor call, but before the run function is called. Used to perform any initalization.
* \param domain The entire terrain mesh
*/
virtual void init(mesh& domain)
{
};
/*
* Returns the module's parallel type
* \return the parallel type
*/
parallel parallel_type()
{
return _parallel_type;
}
std::shared_ptr<std::vector<variable_info> > provides()
{
return _provides;
}
std::shared_ptr<std::vector<std::string> > provides_vector()
{
return _vectors;
}
std::shared_ptr<std::vector<std::string> > provides_parameter()
{
return _provides_parameters;
}
void provides_vector(const std::string& name)
{
if(name.find_first_of("\t ") != std::string::npos)
BOOST_THROW_EXCEPTION(module_error() << errstr_info ("Variable " + name +" has a space. This is not allowed."));
_vectors->push_back(name);
}
void provides(const std::string& name)
{
if(name.find_first_of("\t ") != std::string::npos)
BOOST_THROW_EXCEPTION(module_error() << errstr_info ("Variable " + name +" has a space. This is not allowed."));
_provides->push_back(variable_info(name,SpatialType::neighbor));
}
void provides(const std::string& name, SpatialType st)
{
if(name.find_first_of("\t ") != std::string::npos)
BOOST_THROW_EXCEPTION(module_error() << errstr_info ("Variable " + name +" has a space. This is not allowed."));
_provides->push_back(variable_info(name, st));
}
void provides(const std::string& name, SpatialType st, double distance)
{
if(name.find_first_of("\t ") != std::string::npos)
BOOST_THROW_EXCEPTION(module_error() << errstr_info ("Variable " + name +" has a space. This is not allowed."));
_provides->push_back(variable_info(name, st, distance));
}
void provides_parameter(const std::string& variable)
{
if(variable.find_first_of("\t ") != std::string::npos)
BOOST_THROW_EXCEPTION(module_error() << errstr_info ("Variable " + variable +" has a space. This is not allowed."));
_provides_parameters->push_back(variable);
}
std::shared_ptr<std::vector<variable_info>> depends() { return _depends; }
void conflicts(const std::string& variable)
{
if(variable.find_first_of("\t ") != std::string::npos)
BOOST_THROW_EXCEPTION(module_error() << errstr_info ("Variable " + variable +" has a space. This is not allowed."));
_conflicts->push_back(variable);
}
std::shared_ptr<std::vector<std::string> > conflicts()
{
return _conflicts;
}
std::shared_ptr<std::vector<std::string> > optionals()
{
return _optional;
}
void depends(const std::string& name) { _depends->push_back(variable_info(name)); }
void depends(const std::string& name, SpatialType st) { _depends->push_back(variable_info(name,st)); }
void depends(const std::string& name, SpatialType st, double distance) { _depends->push_back(variable_info(name,st,distance)); }
std::shared_ptr<std::vector<std::string> > depends_from_met()
{
return _depends_from_met;
}
void depends_from_met(const std::string& variable)
{
_depends_from_met->push_back(variable);
}
void optional(const std::string& variable)
{
_optional->push_back(variable);
_optional_found.insert( std::pair<std::string,bool>(variable,false));
}
bool has_optional(const std::string& variable)
{
auto it = _optional_found.find(variable);
//asked for a variable that isn't optional, just return false
if(it ==_optional_found.end())
BOOST_THROW_EXCEPTION(module_error() << errstr_info ("Requested a non-optional variable"));
// return false;
return it->second; //return if we found it
}
/*
* Get a vector of only the variable names for a given collection type of variables
*/
std::vector<std::string> get_variable_names_from_collection(std::vector<variable_info> collection)
{
std::vector<std::string> names;
std::transform(collection.begin(), collection.end(), std::back_inserter(names),
[](variable_info const& x) { return x.name; });
return names;
}
void set_all_nan_on_skip(mesh_elem& face)
{
for(auto& itr: *_provides)
{
(*face)[itr.name]=-9999.;
}
}
void set_optional_found(const std::string& variable)
{
_optional_found[variable]=true;
}
bool is_nan(const double& variable)
{
if( std::fabs(variable - -9999.0) < 1e-5)
return true;
if( std::isnan(variable) )
return true;
return false;
}
bool is_water(mesh_elem& face)
{
bool is = false;
if(face->has_parameter("landcover"_s))
{
int LC = face->parameter("landcover"_s);
is = global_param->parameters.get<bool>("landcover." + std::to_string(LC) + ".is_water",false);
}
return is;
}
bool is_glacier(mesh_elem& face)
{
bool is = false;
if(face->has_parameter("landcover"_s))
{
int LC = face->parameter("landcover"_s);
is = global_param->parameters.get<bool>("landcover." + std::to_string(LC) + ".is_glacier",false);
}
return is;
}
protected:
parallel _parallel_type;
std::shared_ptr<std::vector<variable_info>> _provides;
std::shared_ptr<std::vector<std::string>> _provides_parameters;
std::shared_ptr<std::vector<variable_info>> _depends;
std::shared_ptr<std::vector<std::string>> _depends_from_met;
std::shared_ptr<std::vector<std::string>> _optional;
std::shared_ptr<std::vector<std::string>> _conflicts;
// Currently not used to resolve dependencies
// This is a list of variables that are stored as x,y,z vectors, such as wind velocities
// Vector_3 so magnitude needs to be stored in a normal _provides variable
std::shared_ptr<std::vector<std::string>> _vectors;
// lists the options that were found
std::map<std::string, bool> _optional_found;
};
typedef std::shared_ptr<module_base> module;
// Macros for easier registration of Module implementations
// single argument ctor
typedef factory<module_base, config_file> module_factory;
#define REGISTER_MODULE_HPP(Implementation) \
private: \
static const registration_helper<module_base,Implementation,config_file> registrar;
#define STR_EXPAND(x) #x // ensure x gets evaluated as a string,
#define STR(x) STR_EXPAND(x) // two-stage macro
#define REGISTER_MODULE_CPP(Implementation) \
const registration_helper<module_base,Implementation,config_file> Implementation::registrar(STR(Implementation));