
.. _program_listing_file_src_modules_module_base.hpp:

Program Listing for File module_base.hpp
========================================

|exhale_lsh| :ref:`Return to documentation for file <file_src_modules_module_base.hpp>` (``src/modules/module_base.hpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   //
   // 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));
