From 4484bd3108d420d77893a5305336852b71d10676 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:01:11 +0100 Subject: [PATCH 1/6] Add new c++ guideline --- docs/source/development.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/development.rst b/docs/source/development.rst index c405e3db40..446a64cfc8 100644 --- a/docs/source/development.rst +++ b/docs/source/development.rst @@ -38,8 +38,9 @@ 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 ``HasMyAwesomeFunciton``. - 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: @@ -47,7 +48,7 @@ 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&`` + - 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. @@ -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 From 99a83ff79d1e5806a567b970d7741fcf5bf963d3 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:01:44 +0100 Subject: [PATCH 2/6] introduce concepts to compartments dir --- .../compartments/compartmental_model.h | 84 +++++-------------- cpp/memilio/compartments/flow_model.h | 46 +++------- cpp/memilio/compartments/flow_simulation.h | 3 +- .../compartments/flow_simulation_base.h | 3 +- cpp/memilio/compartments/simulation.h | 4 +- cpp/memilio/compartments/simulation_base.h | 3 +- cpp/memilio/compartments/stochastic_model.h | 29 +++---- .../compartments/stochastic_simulation.h | 4 +- 8 files changed, 48 insertions(+), 128 deletions(-) diff --git a/cpp/memilio/compartments/compartmental_model.h b/cpp/memilio/compartments/compartmental_model.h index 5173923af4..3753c7d982 100644 --- a/cpp/memilio/compartments/compartmental_model.h +++ b/cpp/memilio/compartments/compartmental_model.h @@ -22,46 +22,25 @@ #include "memilio/config.h" #include "memilio/math/eigen.h" -#include "memilio/utils/custom_index_array.h" -#include "memilio/utils/metaprogramming.h" -#include -#include -#include -#include +#include namespace mio { -namespace details -{ -//helpers for check_constraint -template -using check_constraints_expr_t = decltype(std::declval().check_constraints()); - -//helpers for apply_constraints -template -using apply_constraints_expr_t = decltype(std::declval().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 -using has_check_constraints_member_function = is_expression_valid; +concept HasCheckConstraints = requires(T t) { + { t.check_constraints() } -> std::same_as; +}; -/** - * @brief Check whether a apply_constraints function exists. - * @tparam The type to check for the existence of the member function. - */ template -using has_apply_constraints_member_function = is_expression_valid; +concept HasApplyConstraints = requires(T t) { + { t.apply_constraints() } -> std::same_as; +}; /** * @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 @@ -148,7 +127,7 @@ struct CompartmentalModel { */ bool apply_constraints() { - if constexpr (has_apply_constraints_member_function::value) { + if constexpr (HasApplyConstraints) { // use bitwise instead of logical or, so that both apply_constraint functions are called return ((int)parameters.apply_constraints() | (int)populations.apply_constraints()); } @@ -163,7 +142,7 @@ struct CompartmentalModel { */ bool check_constraints() const { - if constexpr (has_check_constraints_member_function::value) { + if constexpr (HasCheckConstraints) { return (parameters.check_constraints() || populations.check_constraints()); } else { @@ -176,42 +155,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 -using eval_right_hand_side_expr_t = decltype(std::declval().eval_right_hand_side( - std::declval>>(), std::declval>>(), - std::declval(), std::declval>>())); - -/** - * 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 -using get_initial_values_expr_t = - decltype(std::declval&>() = std::declval().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. + * @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, floating point type, e.g., double. - * @tparam M a type that may or may not be a compartment model. */ -template -using is_compartment_model = - std::integral_constant::value && - is_expression_valid::value)>; +template +concept IsCompartmentalModel = + requires(Model m, Eigen::Ref> pop_y, Eigen::Ref> dydt, FP t) { + { m.get_initial_values() } -> std::convertible_to>; + m.eval_right_hand_side(pop_y, pop_y, t, dydt); + }; } // namespace mio diff --git a/cpp/memilio/compartments/flow_model.h b/cpp/memilio/compartments/flow_model.h index 614d21cda4..e6b90a750b 100644 --- a/cpp/memilio/compartments/flow_model.h +++ b/cpp/memilio/compartments/flow_model.h @@ -235,41 +235,19 @@ class FlowModel : public CompartmentalModel }; /** - * 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. - * @{ + * @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, floating point type, e.g., double. */ -template -using get_derivatives_expr_t = decltype(std::declval().get_derivatives( - std::declval>>(), std::declval>>())); - -template -using get_flows_expr_t = - decltype(std::declval().get_flows(std::declval>>(), - std::declval>>(), - std::declval(), std::declval>>())); - -template -using get_initial_flows_expr_t = - decltype(std::declval>() = std::declval().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. - * @tparam Model A type that may or may not be a flow model. - */ -template -using is_flow_model = std::integral_constant::value && - is_expression_valid::value && - is_expression_valid::value && - is_compartment_model::value)>; +template +concept IsFlowModel = + requires(Model m, Eigen::Ref> const_vref, Eigen::Ref> vref, FP t) { + IsCompartmentalModel; + { m.get_initial_flows() } -> std::convertible_to>; + m.get_flows(const_vref, const_vref, t, vref); + m.get_derivatives(const_vref, vref); + }; } // namespace mio diff --git a/cpp/memilio/compartments/flow_simulation.h b/cpp/memilio/compartments/flow_simulation.h index 3e4c5bed18..5107ef7d7e 100755 --- a/cpp/memilio/compartments/flow_simulation.h +++ b/cpp/memilio/compartments/flow_simulation.h @@ -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" @@ -32,7 +33,7 @@ namespace mio * @tparam FP A floating point type, e.g. double. * @tparam M An implementation of a FlowModel. */ -template +template M> class FlowSimulation : public details::FlowSimulationBase> { public: diff --git a/cpp/memilio/compartments/flow_simulation_base.h b/cpp/memilio/compartments/flow_simulation_base.h index a93a759c3d..74749a4338 100755 --- a/cpp/memilio/compartments/flow_simulation_base.h +++ b/cpp/memilio/compartments/flow_simulation_base.h @@ -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 +template M, class... Integrands> class FlowSimulationBase : public SimulationBase { - static_assert(is_flow_model::value, "Template parameter must be a flow model."); public: using Base = SimulationBase; diff --git a/cpp/memilio/compartments/simulation.h b/cpp/memilio/compartments/simulation.h index cec0fb0392..5cf3beb579 100755 --- a/cpp/memilio/compartments/simulation.h +++ b/cpp/memilio/compartments/simulation.h @@ -39,7 +39,7 @@ using DefaultIntegratorCore = mio::ControlledStepperWrapper +template M> class Simulation : public details::SimulationBase> { public: @@ -95,7 +95,7 @@ using advance_expr_t = decltype(std::declval().advance(std::declval())) template using is_compartment_model_simulation = std::integral_constant::value && - is_compartment_model::value)>; + IsCompartmentalModel)>; /** * @brief Run a Simulation of a CompartmentalModel. diff --git a/cpp/memilio/compartments/simulation_base.h b/cpp/memilio/compartments/simulation_base.h index 3c9056b7de..c551962d34 100755 --- a/cpp/memilio/compartments/simulation_base.h +++ b/cpp/memilio/compartments/simulation_base.h @@ -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 +template M, class... Integrands> class SimulationBase { - static_assert(is_compartment_model::value, "Template parameter must be a compartment model."); public: using Model = M; diff --git a/cpp/memilio/compartments/stochastic_model.h b/cpp/memilio/compartments/stochastic_model.h index 40ebdc18ee..5d1f67dff3 100644 --- a/cpp/memilio/compartments/stochastic_model.h +++ b/cpp/memilio/compartments/stochastic_model.h @@ -92,27 +92,18 @@ class StochasticModel mutable RandomNumberGenerator m_rng; ///< RNG for generating white noise in the get_noise implementation. }; -namespace details -{ -template -using get_noise_expr_t = - decltype(std::declval().get_noise(std::declval>>(), - std::declval>>(), - std::declval(), std::declval>>())); -} - /** - * 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. - * @tparam Model A type that may or may not be a stochastic model. + * @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, floating point type, e.g., double. */ -template -using is_stochastic_model = - std::integral_constant::value && - is_compartment_model::value)>; +template +concept IsStochasticModel = + requires(Model m, Eigen::Ref> const_vref, Eigen::Ref> vref, FP t) { + IsCompartmentalModel; + m.get_noise(const_vref, const_vref, t, vref); + }; } // namespace mio diff --git a/cpp/memilio/compartments/stochastic_simulation.h b/cpp/memilio/compartments/stochastic_simulation.h index 9600b7b460..b9a2616d27 100755 --- a/cpp/memilio/compartments/stochastic_simulation.h +++ b/cpp/memilio/compartments/stochastic_simulation.h @@ -34,11 +34,9 @@ namespace mio * @tparam FP A floating point type, e.g. double. * @tparam M An implementation of a StochasticModel. */ -template +template M> class StochasticSimulation : public details::SimulationBase> { - static_assert(is_stochastic_model::value, "Template parameter must be a stochastic model."); - public: using Base = details::SimulationBase>; using Model = M; From 165a7a269af28ec68644f924bf3a3f9c1498bded Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:03:34 +0100 Subject: [PATCH 3/6] apply new naming scheme --- cpp/memilio/geography/rtree.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/memilio/geography/rtree.h b/cpp/memilio/geography/rtree.h index f8c022c438..740cf49878 100644 --- a/cpp/memilio/geography/rtree.h +++ b/cpp/memilio/geography/rtree.h @@ -42,7 +42,7 @@ namespace geo * The location type needs to provide get_latitude() and get_longitude() methods returning floating point types. */ template -concept SphericalLocationType = requires(const Location& loc) { +concept IsSphericalLocation = requires(const Location& loc) { std::is_floating_point_v && std::is_floating_point_v; }; @@ -69,7 +69,7 @@ class RTree * * @param locations A vector of geographical locations, they need to provide get_latitude() and get_longitude(). */ - template + template RTree(const std::vector& locations) : rtree{} { @@ -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 nearest_neighbor_indices(const SphericalLocationType auto& location, size_t number) const + std::vector nearest_neighbor_indices(const IsSphericalLocation auto& location, size_t number) const { using boost::geometry::index::nearest; using boost::geometry::index::query; @@ -114,7 +114,7 @@ class RTree * @param radius The radius of the query. * @return Vector with indices of the points found. */ - std::vector in_range_indices_approximate(const SphericalLocationType auto& location, Distance radius) const + std::vector in_range_indices_approximate(const IsSphericalLocation auto& location, Distance radius) const { using boost::geometry::index::covered_by; using boost::geometry::index::query; @@ -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> in_range_indices_query(const SphericalLocationType auto& location, + std::vector> in_range_indices_query(const IsSphericalLocation auto& location, const std::vector& radii) const { using boost::geometry::distance; @@ -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 in_range_indices(const SphericalLocationType auto& location, Distance radius) const + std::vector in_range_indices(const IsSphericalLocation auto& location, Distance radius) const { using boost::geometry::distance; using boost::geometry::index::covered_by; @@ -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; From 76506a97a25aee93a81c87e17e8697e120639cc4 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:21:11 +0100 Subject: [PATCH 4/6] add docstrings --- cpp/memilio/compartments/compartmental_model.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/memilio/compartments/compartmental_model.h b/cpp/memilio/compartments/compartmental_model.h index 3753c7d982..ebc4db8521 100644 --- a/cpp/memilio/compartments/compartmental_model.h +++ b/cpp/memilio/compartments/compartmental_model.h @@ -27,11 +27,13 @@ namespace mio { +/// @brief Check that the given type has a check_constraints member function. template concept HasCheckConstraints = requires(T t) { { t.check_constraints() } -> std::same_as; }; +/// @brief Check that the given type has a apply_constraints member function. template concept HasApplyConstraints = requires(T t) { { t.apply_constraints() } -> std::same_as; From fd4fdf115e33cbddccfc437fd1bed307a6df5143 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 10 Nov 2025 17:24:05 +0100 Subject: [PATCH 5/6] add requires --- cpp/memilio/compartments/flow_model.h | 2 +- cpp/memilio/compartments/stochastic_model.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/memilio/compartments/flow_model.h b/cpp/memilio/compartments/flow_model.h index e6b90a750b..a6177b50c8 100644 --- a/cpp/memilio/compartments/flow_model.h +++ b/cpp/memilio/compartments/flow_model.h @@ -243,7 +243,7 @@ class FlowModel : public CompartmentalModel template concept IsFlowModel = requires(Model m, Eigen::Ref> const_vref, Eigen::Ref> vref, FP t) { - IsCompartmentalModel; + requires IsCompartmentalModel; { m.get_initial_flows() } -> std::convertible_to>; m.get_flows(const_vref, const_vref, t, vref); m.get_derivatives(const_vref, vref); diff --git a/cpp/memilio/compartments/stochastic_model.h b/cpp/memilio/compartments/stochastic_model.h index 5d1f67dff3..9e50654e75 100644 --- a/cpp/memilio/compartments/stochastic_model.h +++ b/cpp/memilio/compartments/stochastic_model.h @@ -101,7 +101,7 @@ class StochasticModel template concept IsStochasticModel = requires(Model m, Eigen::Ref> const_vref, Eigen::Ref> vref, FP t) { - IsCompartmentalModel; + requires IsCompartmentalModel; m.get_noise(const_vref, const_vref, t, vref); }; From 809b8a2d7e94d37f20c6ccfba3dc073fd5c30c22 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:48:55 +0100 Subject: [PATCH 6/6] [ci skip] review: fix typos --- cpp/memilio/compartments/compartmental_model.h | 6 +++--- cpp/memilio/compartments/flow_model.h | 4 ++-- cpp/memilio/compartments/stochastic_model.h | 4 ++-- docs/source/development.rst | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/memilio/compartments/compartmental_model.h b/cpp/memilio/compartments/compartmental_model.h index ebc4db8521..c7b9b0cbde 100644 --- a/cpp/memilio/compartments/compartmental_model.h +++ b/cpp/memilio/compartments/compartmental_model.h @@ -33,7 +33,7 @@ concept HasCheckConstraints = requires(T t) { { t.check_constraints() } -> std::same_as; }; -/// @brief Check that the given type has a apply_constraints member function. +/// @brief Check that the given type has an apply_constraints member function. template concept HasApplyConstraints = requires(T t) { { t.apply_constraints() } -> std::same_as; @@ -159,8 +159,8 @@ struct CompartmentalModel { /** * @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, floating point type, e.g., double. + * @tparam Model A type that may or may not be a compartment model. + * @tparam FP A floating point type, e.g. double. */ template concept IsCompartmentalModel = diff --git a/cpp/memilio/compartments/flow_model.h b/cpp/memilio/compartments/flow_model.h index a6177b50c8..0ea41155f6 100644 --- a/cpp/memilio/compartments/flow_model.h +++ b/cpp/memilio/compartments/flow_model.h @@ -237,8 +237,8 @@ class FlowModel : public CompartmentalModel /** * @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, floating point type, e.g., double. + * @tparam Model A type that may or may not be a flow model. + * @tparam FP A floating point type, e.g. double. */ template concept IsFlowModel = diff --git a/cpp/memilio/compartments/stochastic_model.h b/cpp/memilio/compartments/stochastic_model.h index 9e50654e75..b54fa03f2f 100644 --- a/cpp/memilio/compartments/stochastic_model.h +++ b/cpp/memilio/compartments/stochastic_model.h @@ -95,8 +95,8 @@ class StochasticModel /** * @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, floating point type, e.g., double. + * @tparam Model A type that may or may not be a stochastic model. + * @tparam FP A floating point type, e.g. double. */ template concept IsStochasticModel = diff --git a/docs/source/development.rst b/docs/source/development.rst index 446a64cfc8..930e124aa7 100644 --- a/docs/source/development.rst +++ b/docs/source/development.rst @@ -40,7 +40,7 @@ Naming rules: - 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 ``HasMyAwesomeFunciton``. + - 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: