Program Listing for File module_base.hpp

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