SNOWPACK 20240414.bd1af1d
Adding extra models

Various processes can already be simulated using different models as configured by the user. This result is achieved by providing a specific model of the process of interest, together with the proper entry in a std::map container that links a model keyword with its implementation. In order to look at the required steps, we will take as an example the hand hardness implementation in the StabilityAlgorithms class. Please keep in mind that when adding a new model to a process that already has multiple available choices, only the first and the third steps are required, the other one being already done.

Model implementation

A method has to be implemented in the class with the same prototype as the original method. In our example, the original method (setHandHardnessMONTI) has the following prototype:

double setHandHardnessMONTI(const ElementData& Edata, const double& buried_hoar_density);
ELEMENT DATA used as a pointer in the SnowStation structure NOTE on M below: this is the mass of an e...
Definition: DataClasses.h:285

so any alternative implementation must use the same prototype. If some parameters would be ignored by some implementation, simply comment out the unused variable:

double my_method(const double& param1, const double /*unused_param*/);

Function pointer typedef

All these methods sharing the same prototype, a generic function pointer type can be defined in the Stability class:

typedef double (*StabMemFn)(const ElementData&, const double&);
double(* StabMemFn)(const ElementData &, const double &)
Definition: Stability.h:31

Model map

Once an alternative implementation has been written (and properly declared in the "StabilityAlgorithms.h" header file), it must be "registered" in the model map. In our exmaple, this map is defined in the "Stability.h" header file:

static std::map<std::string, StabMemFn> mapHandHardness;

and statically filled in the initStaticData() method as following:

const bool Stability::__init = Stability::initStaticData();
bool Stability::initStaticData() {
mapHandHardness["MONTI"] = &StabilityAlgorithms::setHandHardnessMONTI;
mapHandHardness["BELLAIRE"] = &StabilityAlgorithms::setHandHardnessBELLAIRE;
mapHandHardness["ASARC"] = &StabilityAlgorithms::setHandHardnessASARC;
return true;

This way of fillinf the map ensures that it will be initialized only once and for all, making it consistent and efficient.

User model configuration

The user selection of model must be connected with the proper model implementation. The user selects the model he wants through a key in his configuration file. We need to read this key and activate the proper implementation, knowing that the proper key <-> implementation matching is done through the map:

cfg.getValue("HARDNESS_PARAMETERIZATION", "SnowpackAdvanced", hardness_parameterization);
map<string, StabMemFn>::const_iterator it1 = mapHandHardness.find(hardness_parameterization);
if (it1 == mapHandHardness.end()) throw InvalidArgumentException("Unknown hardness parameterization: "+hardness_parameterization, AT);

This means that in the section "SnowpackAdvanced" of his ini file, the key "HARDNESS_PARAMETERIZATION" must contain one of the strings given in the mapHandHardness above (ie. either "DEFAULT" or "MONTI" or "ASARC").

Model call

Finally, the process model has to be called where needed, so each time the hand hardness has to be computed, the call becomes:

hardness = (mapHandHardness[hardness_parameterization]))(EMS[e], hoar_density_buried);