Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 47 additions & 28 deletions cpp/memilio/compartments/compartmentalmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,32 @@ using apply_constraints_expr_t = decltype(std::declval<T>().apply_constraints())
} //namespace details

/**
* @brief check whether a check_constraints function exists
* @tparam The type to check for the existence of the member function
* @brief Check whether a check_constraints function exists.
* @tparam The type to check for the existence of the member function.
*/
template <class T>
using has_check_constraints_member_function = is_expression_valid<details::check_constraints_expr_t, T>;

/**
* @brief check whether a apply_constraints function exists
* @tparam The type to check for the existence of the member function
* @brief Check whether a apply_constraints function exists.
* @tparam The type to check for the existence of the member function.
*/
template <class T>
using has_apply_constraints_member_function = is_expression_valid<details::apply_constraints_expr_t, T>;

/**
* @brief CompartmentalModel is a template for a compartmental model for an
* array of initial populations and a parameter set
* @tparam FP floating point type, e.g., double
* array of initial populations and a parameter set.
* @tparam FP floating point type, e.g., double.
*
* The Populations must be a concrete class derived from the Populations template,
* i.e. a multi-dimensional array of compartment populations where each dimension
* corresponds to a category
* corresponds to a category.
*
* The ParameterSet must be a concrete class derived form the ParameterSet template,
* i.e. a compile-time map of parameters used by the model. These can be referenced
* when defining the flows between compartments and they can be used for parameter
* studies
* studies.
*
*/
template <typename FP, class Comp, class Pop, class Params>
Expand All @@ -80,7 +80,7 @@ struct CompartmentalModel {
using Populations = Pop;
using ParameterSet = Params;
/**
* @brief CompartmentalModel default constructor
* @brief CompartmentalModel default constructor.
*/
CompartmentalModel(Populations const& po, ParameterSet const& pa)
: populations{std::move(po)}
Expand All @@ -94,29 +94,29 @@ struct CompartmentalModel {
CompartmentalModel& operator=(CompartmentalModel&&) = default;
virtual ~CompartmentalModel() = default;

//REMARK: Not pure virtual for easier java/python bindings
// REMARK: Not pure virtual for easier java/python bindings.
virtual void get_derivatives(Eigen::Ref<const Vector<FP>>, Eigen::Ref<const Vector<FP>> /*y*/, FP /*t*/,
Eigen::Ref<Vector<FP>> /*dydt*/) const {};

/**
* @brief eval_right_hand_side evaluates the right-hand-side f of the ODE dydt = f(y, t)
* @brief This function evaluates the right-hand-side f of the ODE dydt = f(y, t).
*
* The heart of the compartmental model is a first order ODE dydt = f(y,t), where y is a flat
* representation of all the compartmental populations at time t. This function evaluates the
* right-hand-side f of the ODE from the intercompartmental flows. It can be used in an ODE
* solver
* solver.
*
* The distinction between pop and y is only for the case of mobility.
* If we have mobility, we want to evaluate the evolution of infection states for a small group of travellers (y)
* while they are in any population (pop). It is important that pop > y always applies.
*
* If we consider a simulation without mobility, the function is called with
* model.eval_right_hand_side(y, y, t, dydt)
* model.eval_right_hand_side(y, y, t, dydt).
*
* @param pop the current state of the population in the geographic unit we are considering
* @param y the current state of the model (or a subpopulation) as a flat array
* @param t the current time
* @param dydt a reference to the calculated output
* @param[in] pop The current state of the population in the geographic unit we are considering.
* @param[in] y The current state of the model (or a subpopulation) as a flat array.
* @param[in] t The current time.
* @param[out] dydt A reference to the calculated output.
*/
void eval_right_hand_side(Eigen::Ref<const Vector<FP>> pop, Eigen::Ref<const Vector<FP>> y, FP t,
Eigen::Ref<Vector<FP>> dydt) const
Expand All @@ -126,28 +126,47 @@ struct CompartmentalModel {
}

/**
* @brief get_initial_values returns the initial values for the compartmental populations.
* This can be used as initial conditions in an ODE solver
* @return the initial populatoins
* @brief Get the initial conditions for the ODE dydt = f(y, t).
* See eval_right_hand_side for more detail.
* @return Current value of model populations as a flat vector.
*/
Vector<FP> get_initial_values() const
{
return populations.get_compartments();
}

void apply_constraints()
/**
* @brief Checks whether the model satisfies all constraints. If not, it changes values to suffice their constraints.
*
* Attention: This function should be used with care. It can not and will not set model parameters and
* compartments to meaningful values. In most cases it is preferable to use check_constraints,
* and correct values manually before proceeding with the simulation.
* The main usage for apply_constraints is in automated tests using random values for initialization.
*
* @return Returns true if one or more constraints were corrected, false otherwise.
*/
bool apply_constraints()
{
populations.apply_constraints();
if constexpr (has_apply_constraints_member_function<ParameterSet>::value) {
parameters.apply_constraints();
// use bitwise instead of logical or, so that both apply_constraint functions are called
return ((int)parameters.apply_constraints() | (int)populations.apply_constraints());
}
else {
return populations.check_constraints();
}
}

void check_constraints() const
/**
* @brief Checks that the model satisfies all constraints (e.g. parameter or population constraints).
* @return Returns true if one or more constraints are not satisfied, false otherwise.
*/
bool check_constraints() const
{
populations.check_constraints();
if constexpr (has_check_constraints_member_function<ParameterSet>::value) {
parameters.check_constraints();
return (parameters.check_constraints() || populations.check_constraints());
}
else {
return populations.check_constraints();
}
}

Expand All @@ -156,7 +175,7 @@ struct CompartmentalModel {
};

/**
* detect the eval_right_hand_side member function of a compartment model.
* Detect the eval_right_hand_side member function of a compartment model.
* If the eval_right_hand_side member function exists in the type M, this template when instatiated
* will be equal to the return type of the function.
* Otherwise the template is invalid.
Expand All @@ -170,7 +189,7 @@ using eval_right_hand_side_expr_t =
std::declval<FP>(), std::declval<Eigen::Ref<Vector<FP>>>()));

/**
* detect the get_initial_values member function of a compartment model.
* Detect the get_initial_values member function of a compartment model.
* If the detect_initial_values member function exists in the type M, this template when instatiated
* will be equal to the return type of the function.
* Otherwise the template is invalid.
Expand Down
24 changes: 15 additions & 9 deletions cpp/memilio/epidemiology/populations.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,35 +228,41 @@ class Populations : public CustomIndexArray<UncertainValue<FP>, Categories...>
* This function can be used to prevent slighly negative function values in compartment sizes that came out
* due to roundoff errors if, e.g., population sizes were computed in a complex way.
*
* Attention: This function should be used with care. It is necessary for some test problems to run through quickly,
* but in a manual execution of an example, check_constraints() may be preferred. Note that the apply_constraints()
* function can and will not set compartments to meaningful values in a sense of a particular scenario,
* it only sets negative values to zero.
* Attention: This function should be used with care. It can not and will not set model parameters and
* compartments to meaningful values. In most cases it is preferable to use check_constraints,
* and correct values manually before proceeding with the simulation.
* The main usage for apply_constraints is in automated tests using random values for initialization.
*
* @return Returns true if one ore more constraint were corrected, false otherwise.
*/
void apply_constraints()
* @return Returns true if one (or more) constraint(s) were corrected, otherwise false.
*/
bool apply_constraints()
{
bool corrected = false;
for (int i = 0; i < this->array().size(); i++) {
if (this->array()[i] < 0) {
log_warning("Constraint check: Compartment size {:d} changed from {:.4f} to {:d}", i, this->array()[i],
0);
this->array()[i] = 0;
corrected = true;
}
}
return corrected;
}

/**
* @brief checks whether the population Parameters satisfy their corresponding constraints
* @brief Checks whether all compartments have non-negative values and logs an error if constraint is not satisfied.
* @return Returns true if one or more constraints are not satisfied, false otherwise.
*/
void check_constraints() const
bool check_constraints() const
{
for (int i = 0; i < this->array().size(); i++) {
FP value = this->array()[i];
if (value < 0.) {
log_error("Constraint check: Compartment size {} is {} and smaller {}", i, value, 0);
return true;
}
}
return false;
}

/**
Expand Down
3 changes: 1 addition & 2 deletions cpp/models/lct_secir/initializer_flows.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ class Initializer
}

// Check if model is valid and the calculated initial value vector is valid.
m_model.check_constraints();
return false;
return m_model.check_constraints();
}

/**
Expand Down
Loading