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
88 changes: 22 additions & 66 deletions cpp/memilio/compartments/compartmental_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,27 @@

#include "memilio/config.h"
#include "memilio/math/eigen.h"
#include "memilio/utils/custom_index_array.h"
#include "memilio/utils/metaprogramming.h"
#include <cstddef>
#include <type_traits>
#include <vector>
#include <functional>
#include <concepts>

namespace mio
{

namespace details
{
//helpers for check_constraint
template <class T>
using check_constraints_expr_t = decltype(std::declval<T>().check_constraints());

//helpers for apply_constraints
/// @brief Check that the given type has a check_constraints member function.
template <class T>
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.
*/
template <class T>
using has_check_constraints_member_function = is_expression_valid<details::check_constraints_expr_t, T>;
concept HasCheckConstraints = requires(T t) {
{ t.check_constraints() } -> std::same_as<bool>;
};

/**
* @brief Check whether a apply_constraints function exists.
* @tparam The type to check for the existence of the member function.
*/
/// @brief Check that the given type has an apply_constraints member function.
template <class T>
using has_apply_constraints_member_function = is_expression_valid<details::apply_constraints_expr_t, T>;
concept HasApplyConstraints = requires(T t) {
{ t.apply_constraints() } -> std::same_as<bool>;
};

/**
* @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.
* @tparam FP A 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
Expand Down Expand Up @@ -148,7 +129,7 @@ struct CompartmentalModel {
*/
bool apply_constraints()
{
if constexpr (has_apply_constraints_member_function<ParameterSet>::value) {
if constexpr (HasApplyConstraints<ParameterSet>) {
// use bitwise instead of logical or, so that both apply_constraint functions are called
return ((int)parameters.apply_constraints() | (int)populations.apply_constraints());
}
Expand All @@ -163,7 +144,7 @@ struct CompartmentalModel {
*/
bool check_constraints() const
{
if constexpr (has_check_constraints_member_function<ParameterSet>::value) {
if constexpr (HasCheckConstraints<ParameterSet>) {
return (parameters.check_constraints() || populations.check_constraints());
}
else {
Expand All @@ -176,42 +157,17 @@ struct CompartmentalModel {
};

/**
* 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.
* @tparam FP, floating point type, e.g., double.
* @tparam M a type that has a eval_right_hand_side member function, e.g. a compartment model type.
*/
template <typename FP, class M>
using eval_right_hand_side_expr_t = decltype(std::declval<const M&>().eval_right_hand_side(
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(), std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
std::declval<FP>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));

/**
* 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.
* @tparam FP, floating point type, e.g., double.
* @tparam M a type that has a get_initial_values member function, e.g. a compartment model type.
*/
template <typename FP, class M>
using get_initial_values_expr_t =
decltype(std::declval<Eigen::VectorX<FP>&>() = std::declval<const M&>().get_initial_values());

/**
* Template meta function to check if a type is a valid compartment model.
* Defines a static constant of name `value`.
* The constant `value` will be equal to true if M is a valid compartment model type.
* Otherwise, `value` will be equal to false.
* @tparam FP, floating point type, e.g., double.
* @tparam M a type that may or may not be a compartment model.
* @brief Concept to check if a type is a valid compartment model.
* Note that Model must be the first template argument
* @tparam Model A type that may or may not be a compartment model.
* @tparam FP A floating point type, e.g. double.
*/
template <typename FP, class M>
using is_compartment_model =
std::integral_constant<bool, (is_expression_valid<eval_right_hand_side_expr_t, FP, M>::value &&
is_expression_valid<get_initial_values_expr_t, FP, M>::value)>;
template <class Model, typename FP>
concept IsCompartmentalModel =
requires(Model m, Eigen::Ref<const Eigen::VectorX<FP>> pop_y, Eigen::Ref<Eigen::VectorX<FP>> dydt, FP t) {
{ m.get_initial_values() } -> std::convertible_to<Eigen::VectorX<FP>>;
m.eval_right_hand_side(pop_y, pop_y, t, dydt);
};

} // namespace mio

Expand Down
44 changes: 11 additions & 33 deletions cpp/memilio/compartments/flow_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,41 +235,19 @@ class FlowModel : public CompartmentalModel<FP, Comp, Pop, Params>
};

/**
* Detect whether certain member functions (the name before '_expr_t') exist.
* If the 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.
* @tparam FP floating point type, e.g. double.
* @tparam M Any class, e.g. FlowModel.
* @{
*/
template <typename FP, class M>
using get_derivatives_expr_t = decltype(std::declval<const M&>().get_derivatives(
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));

template <typename FP, class M>
using get_flows_expr_t =
decltype(std::declval<const M&>().get_flows(std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
std::declval<FP>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));

template <typename FP, class M>
using get_initial_flows_expr_t =
decltype(std::declval<Eigen::VectorX<FP>>() = std::declval<const M&>().get_initial_flows());
/** @} */

/**
* Template meta function to check if a type is a valid flow model.
* Defines a static constant of name `value`.
* The constant `value` will be equal to true if M is a valid flow model type.
* Otherwise, `value` will be equal to false.
* @tparam FP floating point type, e.g. double.
* @brief Concept to check if a type is a valid flow model.
* Note that Model must be the first template argument
* @tparam Model A type that may or may not be a flow model.
* @tparam FP A floating point type, e.g. double.
*/
template <typename FP, class Model>
using is_flow_model = std::integral_constant<bool, (is_expression_valid<get_derivatives_expr_t, FP, Model>::value &&
is_expression_valid<get_flows_expr_t, FP, Model>::value &&
is_expression_valid<get_initial_flows_expr_t, FP, Model>::value &&
is_compartment_model<FP, Model>::value)>;
template <class Model, typename FP>
concept IsFlowModel =
requires(Model m, Eigen::Ref<const Eigen::VectorX<FP>> const_vref, Eigen::Ref<Eigen::VectorX<FP>> vref, FP t) {
requires IsCompartmentalModel<Model, FP>;
{ m.get_initial_flows() } -> std::convertible_to<Eigen::VectorX<FP>>;
m.get_flows(const_vref, const_vref, t, vref);
m.get_derivatives(const_vref, vref);
};

} // namespace mio

Expand Down
3 changes: 2 additions & 1 deletion cpp/memilio/compartments/flow_simulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#ifndef MIO_COMPARTMENTS_FLOW_SIMULATION_H
#define MIO_COMPARTMENTS_FLOW_SIMULATION_H

#include "memilio/compartments/flow_model.h"
#include "memilio/compartments/flow_simulation_base.h"
#include "memilio/compartments/simulation.h"
#include "memilio/math/integrator.h"
Expand All @@ -32,7 +33,7 @@ namespace mio
* @tparam FP A floating point type, e.g. double.
* @tparam M An implementation of a FlowModel.
*/
template <typename FP, class M>
template <typename FP, IsFlowModel<FP> M>
class FlowSimulation : public details::FlowSimulationBase<FP, M, OdeIntegrator<FP>>
{
public:
Expand Down
3 changes: 1 addition & 2 deletions cpp/memilio/compartments/flow_simulation_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ namespace details
* @tparam M An implementation of FlowModel.
* @tparam Integrands One or more function types used for defining the right hand side of a system of equations.
*/
template <typename FP, class M, class... Integrands>
template <typename FP, IsFlowModel<FP> M, class... Integrands>
class FlowSimulationBase : public SimulationBase<FP, M, Integrands...>
{
static_assert(is_flow_model<FP, M>::value, "Template parameter must be a flow model.");

public:
using Base = SimulationBase<FP, M, Integrands...>;
Expand Down
4 changes: 2 additions & 2 deletions cpp/memilio/compartments/simulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ using DefaultIntegratorCore = mio::ControlledStepperWrapper<FP, boost::numeric::
* @tparam FP A floating point type, e.g. double.
* @tparam M An implementation of a CompartmentalModel.
*/
template <typename FP, class M>
template <typename FP, IsCompartmentalModel<FP> M>
class Simulation : public details::SimulationBase<FP, M, OdeIntegrator<FP>>
{
public:
Expand Down Expand Up @@ -95,7 +95,7 @@ using advance_expr_t = decltype(std::declval<Sim>().advance(std::declval<FP>()))
template <typename FP, class Sim>
using is_compartment_model_simulation =
std::integral_constant<bool, (is_expression_valid<advance_expr_t, FP, Sim>::value &&
is_compartment_model<FP, typename Sim::Model>::value)>;
IsCompartmentalModel<typename Sim::Model, FP>)>;

/**
* @brief Run a Simulation of a CompartmentalModel.
Expand Down
3 changes: 1 addition & 2 deletions cpp/memilio/compartments/simulation_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ namespace details
* @tparam M An implementation of CompartmentalModel.
* @tparam Integrands One or more function types used for defining the right hand side of a system of equations.
*/
template <typename FP, class M, class... Integrands>
template <typename FP, IsCompartmentalModel<FP> M, class... Integrands>
class SimulationBase
{
static_assert(is_compartment_model<FP, M>::value, "Template parameter must be a compartment model.");

public:
using Model = M;
Expand Down
27 changes: 9 additions & 18 deletions cpp/memilio/compartments/stochastic_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,27 +92,18 @@ class StochasticModel
mutable RandomNumberGenerator m_rng; ///< RNG for generating white noise in the get_noise implementation.
};

namespace details
{
template <typename FP, class M>
using get_noise_expr_t =
decltype(std::declval<const M&>().get_noise(std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
std::declval<FP>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));
}

/**
* Template meta function to check if a type is a valid stochastic model.
* Defines a static constant of name `value`.
* The constant `value` will be equal to true if M is a valid stochastic model type.
* Otherwise, `value` will be equal to false.
* @tparam FP A floating point type, e.g. double.
* @brief Concept to check if a type is a valid stochastic model.
* Note that Model must be the first template argument
* @tparam Model A type that may or may not be a stochastic model.
* @tparam FP A floating point type, e.g. double.
*/
template <typename FP, class Model>
using is_stochastic_model =
std::integral_constant<bool, (is_expression_valid<details::get_noise_expr_t, FP, Model>::value &&
is_compartment_model<FP, Model>::value)>;
template <class Model, typename FP>
concept IsStochasticModel =
requires(Model m, Eigen::Ref<const Eigen::VectorX<FP>> const_vref, Eigen::Ref<Eigen::VectorX<FP>> vref, FP t) {
requires IsCompartmentalModel<Model, FP>;
m.get_noise(const_vref, const_vref, t, vref);
};

} // namespace mio

Expand Down
4 changes: 1 addition & 3 deletions cpp/memilio/compartments/stochastic_simulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,9 @@ namespace mio
* @tparam FP A floating point type, e.g. double.
* @tparam M An implementation of a StochasticModel.
*/
template <typename FP, class M>
template <typename FP, IsStochasticModel<FP> M>
class StochasticSimulation : public details::SimulationBase<FP, M, SdeIntegrator<FP>>
{
static_assert(is_stochastic_model<FP, M>::value, "Template parameter must be a stochastic model.");

public:
using Base = details::SimulationBase<FP, M, SdeIntegrator<FP>>;
using Model = M;
Expand Down
14 changes: 7 additions & 7 deletions cpp/memilio/geography/rtree.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace geo
* The location type needs to provide get_latitude() and get_longitude() methods returning floating point types.
*/
template <class Location>
concept SphericalLocationType = requires(const Location& loc) {
concept IsSphericalLocation = requires(const Location& loc) {
std::is_floating_point_v<decltype(loc.get_latitude())> && std::is_floating_point_v<decltype(loc.get_longitude())>;
};

Expand All @@ -69,7 +69,7 @@ class RTree
*
* @param locations A vector of geographical locations, they need to provide get_latitude() and get_longitude().
*/
template <SphericalLocationType Location>
template <IsSphericalLocation Location>
RTree(const std::vector<Location>& locations)
: rtree{}
{
Expand All @@ -96,7 +96,7 @@ class RTree
* @param number The number of nearest neighbours to find.
* @return Vector with indices of the nearest neighbours.
*/
std::vector<size_t> nearest_neighbor_indices(const SphericalLocationType auto& location, size_t number) const
std::vector<size_t> nearest_neighbor_indices(const IsSphericalLocation auto& location, size_t number) const
{
using boost::geometry::index::nearest;
using boost::geometry::index::query;
Expand All @@ -114,7 +114,7 @@ class RTree
* @param radius The radius of the query.
* @return Vector with indices of the points found.
*/
std::vector<size_t> in_range_indices_approximate(const SphericalLocationType auto& location, Distance radius) const
std::vector<size_t> in_range_indices_approximate(const IsSphericalLocation auto& location, Distance radius) const
{
using boost::geometry::index::covered_by;
using boost::geometry::index::query;
Expand All @@ -132,7 +132,7 @@ class RTree
* @param radii Vector containing the radii of the query.
* @return Vector of vectors with indices of the points found.
*/
std::vector<std::vector<size_t>> in_range_indices_query(const SphericalLocationType auto& location,
std::vector<std::vector<size_t>> in_range_indices_query(const IsSphericalLocation auto& location,
const std::vector<Distance>& radii) const
{
using boost::geometry::distance;
Expand Down Expand Up @@ -168,7 +168,7 @@ class RTree
*
* Basically the same as \ref in_range_indices_approximate, but filters the result to make sure the points are within the radius.
*/
std::vector<size_t> in_range_indices(const SphericalLocationType auto& location, Distance radius) const
std::vector<size_t> in_range_indices(const IsSphericalLocation auto& location, Distance radius) const
{
using boost::geometry::distance;
using boost::geometry::index::covered_by;
Expand Down Expand Up @@ -204,7 +204,7 @@ class RTree
* @param approximation_points Number of points used to approximate the circle. Default is 36, i.e. we build a 36-gon.
* @return multi_polygon.
*/
MultiPolygon create_circle_approximation(const SphericalLocationType auto& location, Distance radius,
MultiPolygon create_circle_approximation(const IsSphericalLocation auto& location, Distance radius,
size_t approximation_points = 36) const
{
using namespace boost::geometry::strategy::buffer;
Expand Down
7 changes: 4 additions & 3 deletions docs/source/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@ Namespaces:

Naming rules:

- Classes begin with large Letters , e.g. ``class MyClass``.
- classes begin with large letters , e.g. ``class MyClass``.
- functions, methods, variables use small letters + underscore, e.g. ``my_awesome_function``.
- concepts begin with a boolean prefix (like is, has, can) and use large letters, e.g. ``IsMyClass`` or ``HasMyAwesomeFunction``.
- member variables should be generally private (we allow exceptions from this rule) and should be named with a leading ``m_``, e.g. ``m_my_member``.

Return Values:

- If only one object is output, use return, for multiple objects, pass by reference (we still have to check ``std::expected``).
- The semantics of return value arguments have to make clear, how the ownership is handled.

- If the function creates an object (allocates), pass it as ``std::unique_ptr<T>&``
- If the function creates an object (allocates), pass it as ``T``
- If the function simply changes an object, pass is as ``T&``

- Avoid producing unnecessarily long outputs. Ensure that all output is concise and limited to relevant information.
Expand All @@ -64,7 +65,7 @@ Logging:

Includes:

- Please use include guards with capitalized name of the header file (``test.h -> #ifndefine TEST_H``).
- Please use include guards with capitalized name of the header file (``memilio/utils/test.h -> #ifndefine MIO_UTILS_TEST_H``).
- Sort includes according to

1. own header
Expand Down