diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 9f8e1f2f4d..ed60379588 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -71,7 +71,7 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list auto prng = mio::abm::PersonalRandomNumberGenerator(person); //some % of people are infected, large enough to have some infection activity without everyone dying auto pct_infected = 0.05; - if (mio::UniformDistribution::get_instance()(prng, 0.0, 1.0) < pct_infected) { + if (mio::UniformDistribution::get_instance()(prng, 0.0, 1.0) < pct_infected) { auto state = mio::abm::InfectionState( mio::UniformIntDistribution::get_instance()(prng, 1, int(mio::abm::InfectionState::Count) - 1)); auto infection = mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, person.get_age(), @@ -91,7 +91,7 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list //skip homes so persons always have a place to go, simulation might break otherwise auto pct_require_mask = 0.2; if (loc.get_type() != mio::abm::LocationType::Home && - mio::UniformDistribution::get_instance()(model.get_rng()) < pct_require_mask) { + mio::UniformDistribution::get_instance()(model.get_rng()) < pct_require_mask) { loc.set_required_mask(mio::abm::MaskType::Community); } } diff --git a/cpp/benchmarks/flow_simulation_ode_secirvvs.h b/cpp/benchmarks/flow_simulation_ode_secirvvs.h index 1d2f793b93..c482d29664 100644 --- a/cpp/benchmarks/flow_simulation_ode_secirvvs.h +++ b/cpp/benchmarks/flow_simulation_ode_secirvvs.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding, Daniel Abele, Martin J. Kuehn @@ -20,12 +20,14 @@ #include "memilio/compartments/simulation.h" #include "models/ode_secirvvs/model.h" +#include + namespace mio { namespace benchmark { -using FlowModel = osecirvvs::Model<>; +using FlowModel = osecirvvs::Model; // For comparison benchmarks, an old model version that does not provide computation of flows has been reimplemented here. // For more details see the original implementation in: @@ -40,8 +42,8 @@ class FlowlessModel : public CompartmentalModel, - osecirvvs::Parameters>; + mio::Populations, + osecirvvs::Parameters>; public: FlowlessModel(const Populations& pop, const ParameterSet& params) @@ -55,16 +57,17 @@ class FlowlessModel : public CompartmentalModel pop, Eigen::Ref y, double t, - Eigen::Ref dydt) const override + void get_derivatives(Eigen::Ref> pop, + Eigen::Ref> y, ScalarType t, + Eigen::Ref> dydt) const override { auto const& params = this->parameters; AgeGroup n_agegroups = params.get_num_groups(); - ContactMatrixGroup const& contact_matrix = params.get>(); + ContactMatrixGroup const& contact_matrix = params.get>(); - auto icu_occupancy = 0.0; - auto test_and_trace_required = 0.0; + ScalarType icu_occupancy = 0.0; + ScalarType test_and_trace_required = 0.0; for (auto i = AgeGroup(0); i < n_agegroups; ++i) { test_and_trace_required += (1 - params.get>()[i]) / @@ -129,29 +132,31 @@ class FlowlessModel : public CompartmentalModel>()[i]; - double reducExposedImprovedImmunity = params.get>()[i]; - double reducInfectedSymptomsPartialImmunity = + ScalarType reducExposedPartialImmunity = + params.get>()[i]; + ScalarType reducExposedImprovedImmunity = + params.get>()[i]; + ScalarType reducInfectedSymptomsPartialImmunity = params.get>()[i]; - double reducInfectedSymptomsImprovedImmunity = + ScalarType reducInfectedSymptomsImprovedImmunity = params.get>()[i]; - double reducInfectedSevereCriticalDeadPartialImmunity = + ScalarType reducInfectedSevereCriticalDeadPartialImmunity = params.get>()[i]; - double reducInfectedSevereCriticalDeadImprovedImmunity = + ScalarType reducInfectedSevereCriticalDeadImprovedImmunity = params.get>()[i]; - double reducTimeInfectedMild = params.get>()[i]; + ScalarType reducTimeInfectedMild = params.get>()[i]; //symptomatic are less well quarantined when testing and tracing is overwhelmed so they infect more people - auto riskFromInfectedSymptomatic = - smoother_cosine(test_and_trace_required, params.get>(), - params.get>() * 15, - params.get>()[i], - params.get>()[i]); + auto riskFromInfectedSymptomatic = smoother_cosine( + test_and_trace_required, params.get>(), + params.get>() * 15, + params.get>()[i], + params.get>()[i]); - auto riskFromInfectedNoSymptoms = - smoother_cosine(test_and_trace_required, params.get>(), - params.get>() * 2, - params.get>()[i], 1.0); + auto riskFromInfectedNoSymptoms = smoother_cosine( + test_and_trace_required, params.get>(), + params.get>() * 2, + params.get>()[i], 1.0); for (auto j = AgeGroup(0); j < n_agegroups; j++) { size_t SNj = this->populations.get_flat_index({j, InfectionState::SusceptibleNaive}); @@ -191,31 +196,32 @@ class FlowlessModel : public CompartmentalModelpopulations.get_flat_index({j, InfectionState::InfectedSymptomsImprovedImmunityConfirmed}); // effective contact rate by contact rate between groups i and j and damping j - double season_val = + ScalarType season_val = (1 + params.get>() * - sin(3.141592653589793 * ((params.get() + t) / 182.5 + 0.5))); - double cont_freq_eff = - season_val * contact_matrix.get_matrix_at(t)(static_cast((size_t)i), - static_cast((size_t)j)); + sin(std::numbers::pi_v * + ((params.get>() + t) / 182.5 + 0.5))); + ScalarType cont_freq_eff = + season_val * contact_matrix.get_matrix_at(SimulationTime(t))( + static_cast((size_t)i), static_cast((size_t)j)); // without died people - double Nj = pop[SNj] + pop[ENj] + pop[INSNj] + pop[ISyNj] + pop[ISevNj] + pop[ICrNj] + pop[INSNCj] + - pop[ISyNCj] + pop[SPIj] + pop[EPIj] + pop[INSPIj] + pop[ISyPIj] + pop[ISevPIj] + - pop[ICrPIj] + pop[INSPICj] + pop[ISyPICj] + pop[SIIj] + pop[EIIj] + pop[INSIIj] + - pop[ISyIIj] + pop[ISevIIj] + pop[ICrIIj] + pop[INSIICj] + pop[ISyIICj]; + ScalarType Nj = pop[SNj] + pop[ENj] + pop[INSNj] + pop[ISyNj] + pop[ISevNj] + pop[ICrNj] + pop[INSNCj] + + pop[ISyNCj] + pop[SPIj] + pop[EPIj] + pop[INSPIj] + pop[ISyPIj] + pop[ISevPIj] + + pop[ICrPIj] + pop[INSPICj] + pop[ISyPICj] + pop[SIIj] + pop[EIIj] + pop[INSIIj] + + pop[ISyIIj] + pop[ISevIIj] + pop[ICrIIj] + pop[INSIICj] + pop[ISyIICj]; - const double divNj = (Nj < Limits::zero_tolerance()) ? 0.0 : 1.0 / Nj; + const ScalarType divNj = (Nj < Limits::zero_tolerance()) ? 0.0 : 1.0 / Nj; - double ext_inf_force_dummy = + ScalarType ext_inf_force_dummy = cont_freq_eff * divNj * params.template get>()[(AgeGroup)i] * (riskFromInfectedNoSymptoms * (pop[INSNj] + pop[INSPIj] + pop[INSIIj]) + riskFromInfectedSymptomatic * (pop[ISyNj] + pop[ISyPIj] + pop[ISyIIj])); - double dummy_SN = y[SNi] * ext_inf_force_dummy; + ScalarType dummy_SN = y[SNi] * ext_inf_force_dummy; - double dummy_SPI = y[SPIi] * reducExposedPartialImmunity * ext_inf_force_dummy; + ScalarType dummy_SPI = y[SPIi] * reducExposedPartialImmunity * ext_inf_force_dummy; - double dummy_SII = y[SIIi] * reducExposedImprovedImmunity * ext_inf_force_dummy; + ScalarType dummy_SII = y[SIIi] * reducExposedImprovedImmunity * ext_inf_force_dummy; dydt[SNi] -= dummy_SN; dydt[ENi] += dummy_SN; @@ -231,12 +237,12 @@ class FlowlessModel : public CompartmentalModel>(), - params.get>(), - params.get>()[i], 0); + ScalarType criticalPerSevereAdjusted = + smoother_cosine(icu_occupancy, 0.90 * params.get>(), + params.get>(), + params.get>()[i], 0); - double deathsPerSevereAdjusted = + ScalarType deathsPerSevereAdjusted = params.get>()[i] - criticalPerSevereAdjusted; /**** path of immune-naive ***/ @@ -398,7 +404,7 @@ class FlowlessModel : public CompartmentalModel @@ -429,8 +435,8 @@ class FlowlessModel : public CompartmentalModel> -double get_infections_relative(const Simulation& model, double t, - const Eigen::Ref& y); +ScalarType get_infections_relative(const Simulation& model, ScalarType t, + const Eigen::Ref>& y); template class Simulation : public Base @@ -442,23 +448,25 @@ class Simulation : public Base * @param t0 start time * @param dt time steps */ - Simulation(FlowlessModel const& model, double t0 = 0., double dt = 0.1) + Simulation(FlowlessModel const& model, ScalarType t0 = 0.0, ScalarType dt = 0.1) : Base(model, t0, dt) , m_t_last_npi_check(t0) { } - void apply_variant(const double t, const CustomIndexArray, AgeGroup> base_infectiousness) + void apply_variant(const ScalarType t, + const CustomIndexArray, AgeGroup> base_infectiousness) { - auto start_day = this->get_model().parameters.template get(); - auto start_day_new_variant = this->get_model().parameters.template get(); + auto start_day = this->get_model().parameters.template get>(); + auto start_day_new_variant = + this->get_model().parameters.template get>(); if (start_day + t >= start_day_new_variant - 1e-10) { - const double days_variant = t - (start_day_new_variant - start_day); - const double share_new_variant = std::min(1.0, 0.01 * pow(2, (1. / 7) * days_variant)); - const auto num_groups = this->get_model().parameters.get_num_groups(); + const ScalarType days_variant = t - (start_day_new_variant - start_day); + const ScalarType share_new_variant = std::min(1.0, 0.01 * pow(2, (1.0 / 7) * days_variant)); + const auto num_groups = this->get_model().parameters.get_num_groups(); for (auto i = AgeGroup(0); i < num_groups; ++i) { - double new_transmission = + ScalarType new_transmission = (1 - share_new_variant) * base_infectiousness[i] + share_new_variant * base_infectiousness[i] * this->get_model().parameters.template get>()[i]; @@ -469,7 +477,7 @@ class Simulation : public Base } } - void apply_vaccination(double t) + void apply_vaccination(ScalarType t) { auto t_idx = SimulationDay((size_t)t); auto& params = this->get_model().parameters; @@ -483,8 +491,8 @@ class Simulation : public Base for (size_t i = 0; i < num_groups; ++i) { - double first_vacc; - double full_vacc; + ScalarType first_vacc; + ScalarType full_vacc; if (t_idx == SimulationDay(0)) { first_vacc = params.template get>()[{(AgeGroup)i, t_idx}]; @@ -529,7 +537,7 @@ class Simulation : public Base * @param tmax next stopping point of simulation * @return value at tmax */ - Eigen::Ref advance(double tmax) + Eigen::Ref> advance(ScalarType tmax) { auto& t_end_dyn_npis = this->get_model().parameters.get_end_dynamic_npis(); auto& dyn_npis = @@ -540,7 +548,7 @@ class Simulation : public Base auto base_infectiousness = this->get_model().parameters.template get>(); - double delay_npi_implementation; + ScalarType delay_npi_implementation; auto t = Base::get_result().get_last_time(); const auto dt = dyn_npis.get_interval().get(); while (t < tmax) { @@ -576,12 +584,12 @@ class Simulation : public Base auto exceeded_threshold = dyn_npis.get_max_exceeded_threshold(inf_rel); if (exceeded_threshold != dyn_npis.get_thresholds().end() && (exceeded_threshold->first > m_dynamic_npi.first || - t > double(m_dynamic_npi.second))) { //old npi was weaker or is expired + t > ScalarType(m_dynamic_npi.second))) { //old npi was weaker or is expired - auto t_start = SimulationTime(t + delay_npi_implementation); - auto t_end = t_start + SimulationTime(dyn_npis.get_duration()); - this->get_model().parameters.get_start_commuter_detection() = (double)t_start; - this->get_model().parameters.get_end_commuter_detection() = (double)t_end; + auto t_start = SimulationTime(t + delay_npi_implementation); + auto t_end = t_start + SimulationTime(dyn_npis.get_duration()); + this->get_model().parameters.get_start_commuter_detection() = t_start.get(); + this->get_model().parameters.get_end_commuter_detection() = t_end.get(); m_dynamic_npi = std::make_pair(exceeded_threshold->first, t_end); implement_dynamic_npis(contact_patterns.get_cont_freq_mat(), exceeded_threshold->second, t_start, t_end, [](auto& g) { @@ -604,14 +612,16 @@ class Simulation : public Base } private: - double m_t_last_npi_check; - std::pair m_dynamic_npi = {-std::numeric_limits::max(), SimulationTime(0)}; + ScalarType m_t_last_npi_check; + std::pair> m_dynamic_npi = {-std::numeric_limits::max(), + SimulationTime(0)}; }; template -double get_infections_relative(const Simulation& sim, double /*t*/, const Eigen::Ref& y) +ScalarType get_infections_relative(const Simulation& sim, ScalarType /*t*/, + const Eigen::Ref>& y) { - double sum_inf = 0; + ScalarType sum_inf = 0; for (auto i = AgeGroup(0); i < sim.get_model().parameters.get_num_groups(); ++i) { sum_inf += sim.get_model().populations.get_from(y, {i, osecirvvs::InfectionState::InfectedSymptomsNaive}); sum_inf += @@ -677,7 +687,7 @@ void setup_model(Model& model) auto& contact_matrix = contacts.get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(0.5); contact_matrix[0].get_baseline().diagonal().setConstant(5.0); - contact_matrix[0].add_damping(0.3, SimulationTime(5.0)); + contact_matrix[0].add_damping(0.3, SimulationTime(5.0)); //times model.parameters.template get>()[AgeGroup(0)] = 3.33; diff --git a/cpp/benchmarks/flow_simulation_ode_seir.cpp b/cpp/benchmarks/flow_simulation_ode_seir.cpp index 1ed2ba129f..8b1e6a958b 100644 --- a/cpp/benchmarks/flow_simulation_ode_seir.cpp +++ b/cpp/benchmarks/flow_simulation_ode_seir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding, Daniel Abele, Martin J. Kuehn @@ -52,8 +52,9 @@ class FlowlessModel { } - void get_derivatives(Eigen::Ref pop, Eigen::Ref y, double t, - Eigen::Ref dydt) const override + void get_derivatives(Eigen::Ref> pop, + Eigen::Ref> y, ScalarType t, + Eigen::Ref> dydt) const override { auto& params = this->parameters; const Index age_groups = reduce_index>(this->populations.size()); @@ -71,10 +72,10 @@ class FlowlessModel size_t Ij = this->populations.get_flat_index({j, InfectionState::Infected}); size_t Rj = this->populations.get_flat_index({j, InfectionState::Recovered}); - const double Nj_inv = 1.0 / (pop[Sj] + pop[Ej] + pop[Ij] + pop[Rj]); - const double coeffStoE = - params.template get>().get_cont_freq_mat().get_matrix_at(t)(i.get(), - j.get()) * + const ScalarType Nj_inv = 1.0 / (pop[Sj] + pop[Ej] + pop[Ij] + pop[Rj]); + const ScalarType coeffStoE = + params.template get>().get_cont_freq_mat().get_matrix_at( + SimulationTime(t))(i.get(), j.get()) * params.template get>()[i] * Nj_inv; dydt[Si] -= y[Si] * pop[Ij] * coeffStoE; @@ -91,8 +92,8 @@ class FlowlessModel template void setup_model(Model& model) { - const double total_population = 10000.0; - const auto num_groups = model.parameters.get_num_groups(); + const ScalarType total_population = 10000.0; + const auto num_groups = model.parameters.get_num_groups(); for (AgeGroup i = 0; i < num_groups; i++) { model.populations[{i, oseir::InfectionState::Exposed}] = 100.0 / static_cast(num_groups); model.populations[{i, oseir::InfectionState::Infected}] = 100.0 / static_cast(num_groups); @@ -106,8 +107,9 @@ void setup_model(Model& model) model.parameters.template set>(5.2); model.parameters.template set>(6); model.parameters.template set>(0.04); - mio::ContactMatrixGroup& contact_matrix = model.parameters.template get>(); - contact_matrix[0].get_baseline().setConstant(10.); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>(); + contact_matrix[0].get_baseline().setConstant(10.0); } } // namespace benchmark diff --git a/cpp/benchmarks/graph_simulation.cpp b/cpp/benchmarks/graph_simulation.cpp index 17ee57b075..7a2bf413a3 100644 --- a/cpp/benchmarks/graph_simulation.cpp +++ b/cpp/benchmarks/graph_simulation.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker @@ -78,7 +78,7 @@ mio::osecirvvs::Model create_model(size_t num_agegroups, const Scala auto& contact_matrix = contacts.get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(0.5); contact_matrix[0].get_baseline().diagonal().setConstant(5.0); - contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); + contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); for (mio::AgeGroup i = 0; i < (mio::AgeGroup)num_agegroups; i++) { //times @@ -115,9 +115,9 @@ auto create_simulation() { auto cfg = mio::benchmark::GraphConfig::initialize(config_path); - mio::osecirvvs::Model model = create_model(cfg.num_agegroups, cfg.t_max); + mio::osecirvvs::Model model = create_model(cfg.num_agegroups, cfg.t_max); - mio::Graph>>, + mio::Graph>>, mio::MobilityEdge> g; for (size_t county_id = 0; county_id < cfg.num_regions; county_id++) { @@ -130,9 +130,9 @@ auto create_simulation() for (size_t county_idx_j = 0; county_idx_j < g.nodes().size(); ++county_idx_j) { if (county_idx_i == county_idx_j) continue; - g.add_edge( - county_idx_i, county_idx_j, - Eigen::VectorXd::Constant((size_t)mio::osecirvvs::InfectionState::Count * cfg.num_agegroups, 0.01)); + g.add_edge(county_idx_i, county_idx_j, + Eigen::VectorX::Constant( + (size_t)mio::osecirvvs::InfectionState::Count * cfg.num_agegroups, 0.01)); } } diff --git a/cpp/benchmarks/graph_simulation.h b/cpp/benchmarks/graph_simulation.h index 5947109e24..9ddc5dc7e3 100644 --- a/cpp/benchmarks/graph_simulation.h +++ b/cpp/benchmarks/graph_simulation.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker @@ -32,7 +32,7 @@ namespace benchmark /// @brief parameters for simulation benchmark struct GraphConfig { int num_agegroups, num_regions; - double t0, t_max, dt; + ScalarType t0, t_max, dt; /** * @brief creates configuration with default parameters for a secir model * @param num_agegroups number of agegroups @@ -65,9 +65,9 @@ struct GraphConfig { auto obj = io.expect_object("bench_graph_simulation"); auto num_agegroups_io = obj.expect_element("num_agegroups", mio::Tag{}); auto num_regions_io = obj.expect_element("num_regions", mio::Tag{}); - auto t_io = obj.expect_element("t0", mio::Tag{}); - auto t_max_io = obj.expect_element("t_max", mio::Tag{}); - auto dt_io = obj.expect_element("dt", mio::Tag{}); + auto t_io = obj.expect_element("t0", mio::Tag{}); + auto t_max_io = obj.expect_element("t_max", mio::Tag{}); + auto dt_io = obj.expect_element("dt", mio::Tag{}); return mio::apply( io, [](auto&& num_agegroups, auto&& num_regions, auto&& t0, auto&& t_max, auto&& dt) { diff --git a/cpp/benchmarks/integrator_step.cpp b/cpp/benchmarks/integrator_step.cpp index 4050ba5606..bce6833ac8 100644 --- a/cpp/benchmarks/integrator_step.cpp +++ b/cpp/benchmarks/integrator_step.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding @@ -36,13 +36,13 @@ void integrator_step(::benchmark::State& state) //auto cfg = mio::benchmark::IntegratorStepConfig::initialize(); auto model = mio::benchmark::model::SecirAgeres(cfg.num_agegroups); // set deriv function and integrator - mio::DerivFunction f = [model](Eigen::Ref x, double s, - Eigen::Ref dxds) { + mio::DerivFunction f = [model](Eigen::Ref> x, ScalarType s, + Eigen::Ref> dxds) { model.eval_right_hand_side(x, x, s, dxds); }; auto I = Integrator(cfg.abs_tol, cfg.rel_tol, cfg.dt_min, cfg.dt_max); - double t, dt; + ScalarType t, dt; for (auto _ : state) { // This code gets timed t = cfg.t_init; diff --git a/cpp/benchmarks/integrator_step.h b/cpp/benchmarks/integrator_step.h index 35b49c5720..6d98ac087e 100644 --- a/cpp/benchmarks/integrator_step.h +++ b/cpp/benchmarks/integrator_step.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding @@ -32,31 +32,31 @@ namespace benchmark /// @brief parameters for integrator-step benchmark struct IntegratorStepConfig { int num_agegroups; - double t_init, dt_init, abs_tol, rel_tol, dt_min, dt_max; - Eigen::VectorXd yt, ytp1; + ScalarType t_init, dt_init, abs_tol, rel_tol, dt_min, dt_max; + Eigen::VectorX yt, ytp1; /** - * @brief creates configuration with default parameters for a secir model - * @return configuration for integrator-step benchmark - */ + * @brief creates configuration with default parameters for a secir model + * @return configuration for integrator-step benchmark + */ static IntegratorStepConfig initialize() { - const double vals[8] = {6377.873644, 35.249156, 30.029611, 182.145865, - 66.153059, 79.530621, 3069.383604, 159.634440}; + const ScalarType vals[8] = {6377.873644, 35.249156, 30.029611, 182.145865, + 66.153059, 79.530621, 3069.383604, 159.634440}; return IntegratorStepConfig{1, 50, 1, 1e-10, 1e-5, - std::numeric_limits::min(), - std::numeric_limits::max(), - Eigen::Matrix(vals), - Eigen::VectorXd::Zero(8)}; + std::numeric_limits::min(), + std::numeric_limits::max(), + Eigen::Matrix(vals), + Eigen::VectorX::Zero(8)}; } /** - * @brief reads configuration from json file - * @param path the path of the configfile - * @return configuration for integrator-step benchmark - */ + * @brief reads configuration from json file + * @param path the path of the configfile + * @return configuration for integrator-step benchmark + */ static IntegratorStepConfig initialize(std::string path) { auto result = mio::read_json(path, mio::Tag{}); @@ -72,13 +72,13 @@ struct IntegratorStepConfig { { auto obj = io.expect_object("integrator_step"); auto num_agegroups_io = obj.expect_element("num_agegroups", mio::Tag{}); - auto t_init_io = obj.expect_element("t_init", mio::Tag{}); - auto dt_init_io = obj.expect_element("dt_init", mio::Tag{}); - auto abs_tol_io = obj.expect_element("abs_tol", mio::Tag{}); - auto rel_tol_io = obj.expect_element("rel_tol", mio::Tag{}); - auto dt_min_io = obj.expect_element("dt_min", mio::Tag{}); - auto dt_max_io = obj.expect_element("dt_max", mio::Tag{}); - auto yt_io = obj.expect_list("yt", mio::Tag{}); + auto t_init_io = obj.expect_element("t_init", mio::Tag{}); + auto dt_init_io = obj.expect_element("dt_init", mio::Tag{}); + auto abs_tol_io = obj.expect_element("abs_tol", mio::Tag{}); + auto rel_tol_io = obj.expect_element("rel_tol", mio::Tag{}); + auto dt_min_io = obj.expect_element("dt_min", mio::Tag{}); + auto dt_max_io = obj.expect_element("dt_max", mio::Tag{}); + auto yt_io = obj.expect_list("yt", mio::Tag{}); return mio::apply( io, [](auto&& num_agegroups, auto&& t_init, auto&& dt_init, auto&& abs_tol, auto&& rel_tol, auto&& dt_min, @@ -90,8 +90,8 @@ struct IntegratorStepConfig { rel_tol, dt_min, dt_max, - Eigen::VectorXd::Zero(yt.size()), - Eigen::VectorXd::Zero(yt.size())}; + Eigen::VectorX::Zero(yt.size()), + Eigen::VectorX::Zero(yt.size())}; for (size_t i = 0; i < yt.size(); i++) { cfg.yt[i] = yt[i]; } diff --git a/cpp/benchmarks/secir_ageres_setups.h b/cpp/benchmarks/secir_ageres_setups.h index 3f2f6e5096..38f4d5e1b9 100644 --- a/cpp/benchmarks/secir_ageres_setups.h +++ b/cpp/benchmarks/secir_ageres_setups.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding @@ -33,30 +33,30 @@ namespace benchmark namespace detail { /** - * @brief Helper function to create a secir model with consistent setup for use in benchmarking. - */ + * @brief Helper function to create a secir model with consistent setup for use in benchmarking. + */ mio::osecir::Model make_model(int num) { - double cont_freq = 10; // see Polymod study + ScalarType cont_freq = 10; // see Polymod study - double nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, - nb_rec_t0 = 10, nb_dead_t0 = 0; + ScalarType nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, + nb_rec_t0 = 10, nb_dead_t0 = 0; - mio::osecir::Model model(num); - auto nb_groups = model.parameters.get_num_groups(); - double fact = 1.0 / (double)(size_t)nb_groups; + mio::osecir::Model model(num); + auto nb_groups = model.parameters.get_num_groups(); + ScalarType fact = 1.0 / (ScalarType)(size_t)nb_groups; auto& params = model.parameters; - params.set>(std::numeric_limits::max()); - params.set(0); + params.set>(std::numeric_limits::max()); + params.set>(0); params.set>(0); for (auto i = mio::AgeGroup(0); i < nb_groups; i++) { params.get>()[i] = 3.2; - params.get>()[i] = 2.; - params.get>()[i] = 6.; + params.get>()[i] = 2.0; + params.get>()[i] = 6.0; params.get>()[i] = 12; params.get>()[i] = 8; @@ -79,11 +79,11 @@ mio::osecir::Model make_model(int num) params.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), - mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), + mio::SimulationTime(30.0)); model.apply_constraints(); @@ -94,87 +94,90 @@ mio::osecir::Model make_model(int num) namespace model { /** - * @brief Secir model with consistent setup for use in benchmarking. - */ + * @brief Secir model with consistent setup for use in benchmarking. + */ mio::osecir::Model SecirAgeres(size_t num_agegroups) { mio::osecir::Model model = mio::benchmark::detail::make_model(num_agegroups); - auto nb_groups = model.parameters.get_num_groups(); - double cont_freq = 10, fact = 1.0 / (double)(size_t)nb_groups; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + auto nb_groups = model.parameters.get_num_groups(); + ScalarType cont_freq = 10, fact = 1.0 / (ScalarType)(size_t)nb_groups; + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), - mio::SimulationTime(30.)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), + mio::SimulationTime(30.0)); return model; } /** - * @brief Secir model with consistent setup for use in benchmarking with added dampings. - */ + * @brief Secir model with consistent setup for use in benchmarking with added dampings. + */ mio::osecir::Model SecirAgeresDampings(size_t num_agegroups) { mio::osecir::Model model = mio::benchmark::detail::make_model(num_agegroups); - auto nb_groups = model.parameters.get_num_groups(); - double cont_freq = 10, fact = 1.0 / (double)(size_t)nb_groups; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), - mio::SimulationTime(25.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.3), - mio::SimulationTime(40.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.8), - mio::SimulationTime(60.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.5), - mio::SimulationTime(75.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 1.0), - mio::SimulationTime(95.)); + auto nb_groups = model.parameters.get_num_groups(); + ScalarType cont_freq = 10, fact = 1.0 / (ScalarType)(size_t)nb_groups; + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), + mio::SimulationTime(25.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.3), + mio::SimulationTime(40.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.8), + mio::SimulationTime(60.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.5), + mio::SimulationTime(75.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 1.0), + mio::SimulationTime(95.0)); return model; } /** - * @brief Secir model with consistent setup for use in benchmarking with added dampings. - * Dampings are set up to challenge the integrator, not to be realistic. - */ + * @brief Secir model with consistent setup for use in benchmarking with added dampings. + * Dampings are set up to challenge the integrator, not to be realistic. + */ mio::osecir::Model SecirAgeresAbsurdDampings(size_t num_agegroups) { mio::osecir::Model model = mio::benchmark::detail::make_model(num_agegroups); - auto nb_groups = model.parameters.get_num_groups(); - double cont_freq = 10, fact = 1.0 / (double)(size_t)nb_groups; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.8), - mio::SimulationTime(10.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.5), - mio::SimulationTime(11.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.2), - mio::SimulationTime(12.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.1), - mio::SimulationTime(13.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.9), - mio::SimulationTime(30.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.2), - mio::SimulationTime(30.5)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), - mio::SimulationTime(31.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.2), - mio::SimulationTime(31.5)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.8), - mio::SimulationTime(32.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.1), - mio::SimulationTime(40.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.001), - mio::SimulationTime(44.)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.9), - mio::SimulationTime(46.)); + auto nb_groups = model.parameters.get_num_groups(); + ScalarType cont_freq = 10, fact = 1.0 / (ScalarType)(size_t)nb_groups; + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.8), + mio::SimulationTime(10.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.5), + mio::SimulationTime(11.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.2), + mio::SimulationTime(12.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.1), + mio::SimulationTime(13.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.9), + mio::SimulationTime(30.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.2), + mio::SimulationTime(30.5)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), + mio::SimulationTime(31.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.2), + mio::SimulationTime(31.5)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.8), + mio::SimulationTime(32.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.1), + mio::SimulationTime(40.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.001), + mio::SimulationTime(44.0)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.9), + mio::SimulationTime(46.0)); return model; } diff --git a/cpp/benchmarks/simulation.cpp b/cpp/benchmarks/simulation.cpp index 52e36b6647..cfdb83b12e 100644 --- a/cpp/benchmarks/simulation.cpp +++ b/cpp/benchmarks/simulation.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding diff --git a/cpp/benchmarks/simulation.h b/cpp/benchmarks/simulation.h index caf956551d..3a202fb42a 100644 --- a/cpp/benchmarks/simulation.h +++ b/cpp/benchmarks/simulation.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding @@ -32,12 +32,12 @@ namespace benchmark /// @brief parameters for simulation benchmark struct SimulationConfig { int num_agegroups; - double t0, t_max, dt, abs_tol, rel_tol, dt_min, dt_max; + ScalarType t0, t_max, dt, abs_tol, rel_tol, dt_min, dt_max; /** - * @brief creates configuration with default parameters for a secir model - * @param num_agegroups number of agegroups - * @return configuration for simulation benchmark - */ + * @brief creates configuration with default parameters for a secir model + * @param num_agegroups number of agegroups + * @return configuration for simulation benchmark + */ static SimulationConfig initialize(int num_agegroups = 10) { return SimulationConfig{num_agegroups, @@ -46,14 +46,14 @@ struct SimulationConfig { 0.5, 1e-10, 1e-5, - std::numeric_limits::min(), - std::numeric_limits::max()}; + std::numeric_limits::min(), + std::numeric_limits::max()}; } /** - * @brief reads configuration from json file - * @param path the path of the configfile - * @return configuration for simulation benchmark - */ + * @brief reads configuration from json file + * @param path the path of the configfile + * @return configuration for simulation benchmark + */ static SimulationConfig initialize(std::string path) { auto result = mio::read_json(path, mio::Tag{}); @@ -69,13 +69,13 @@ struct SimulationConfig { { auto obj = io.expect_object("bench_simulation"); auto num_agegroups_io = obj.expect_element("num_agegroups", mio::Tag{}); - auto t_io = obj.expect_element("t0", mio::Tag{}); - auto t_max_io = obj.expect_element("t_max", mio::Tag{}); - auto dt_io = obj.expect_element("dt", mio::Tag{}); - auto abs_tol_io = obj.expect_element("abs_tol", mio::Tag{}); - auto rel_tol_io = obj.expect_element("rel_tol", mio::Tag{}); - auto dt_min_io = obj.expect_element("dt_min", mio::Tag{}); - auto dt_max_io = obj.expect_element("dt_max", mio::Tag{}); + auto t_io = obj.expect_element("t0", mio::Tag{}); + auto t_max_io = obj.expect_element("t_max", mio::Tag{}); + auto dt_io = obj.expect_element("dt", mio::Tag{}); + auto abs_tol_io = obj.expect_element("abs_tol", mio::Tag{}); + auto rel_tol_io = obj.expect_element("rel_tol", mio::Tag{}); + auto dt_min_io = obj.expect_element("dt_min", mio::Tag{}); + auto dt_max_io = obj.expect_element("dt_max", mio::Tag{}); return mio::apply( io, [](auto&& num_agegroups_, auto&& t0_, auto&& t_max_, auto&& dt_, auto&& abs_tol_, auto&& rel_tol_, diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 28c883b892..e9b75cb358 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -178,7 +178,7 @@ int main() auto sim = mio::abm::Simulation(t0, std::move(model)); struct LogTimePoint : mio::LogAlways { - using Type = double; + using Type = ScalarType; static Type log(const mio::abm::Simulation<>& sim) { return sim.get_time().hours(); diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index a78e0dddf7..156b212b4f 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -118,7 +118,7 @@ int main() // Assign infection state to each person. // The infection states are chosen randomly with the following distribution - std::vector infection_distribution{0.5, 0.3, 0.05, 0.05, 0.05, 0.05, 0.0, 0.0}; + std::vector infection_distribution{0.5, 0.3, 0.05, 0.05, 0.05, 0.05, 0.0, 0.0}; for (auto& person : model.get_persons()) { mio::abm::InfectionState infection_state = mio::abm::InfectionState( mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), infection_distribution)); diff --git a/cpp/examples/ad_odeint_example.cpp b/cpp/examples/ad_odeint_example.cpp index 03e64fcc72..4b6aaf7337 100644 --- a/cpp/examples/ad_odeint_example.cpp +++ b/cpp/examples/ad_odeint_example.cpp @@ -18,7 +18,7 @@ * limitations under the License. */ -#include "ad/ad.hpp" +#include "memilio/ad/ad.h" #include "boost/numeric/odeint.hpp" #include @@ -56,7 +56,7 @@ int main() x[0] = 1.0; // start at x=1.0, p=0.0 x[1] = 0.0; ad::derivative(x[0]) = 1.0; // compute derivative with respect to x[0] (scalar tangent-linear mode) - ad::derivative(x[0]) = 0.0; + ad::derivative(x[1]) = 0.0; auto t0 = time_type(0.0); // initial time auto t_end = time_type(10.0); // stop time @@ -72,10 +72,10 @@ int main() // We want to compare AD derivatives with difference quotient // To this end, we simulate again with a perturbation of the initial value of x[0] - const double h = 1e-3; // pertubation for finite differences + const double h = 1e-3; // pertubation for finite differences std::array y = {ad::value(x[0]), ad::value(x[1])}; - x[0] = 1.0 + h; // add perturbation to initial value of x[0] - x[1] = 0.0; + x[0] = 1.0 + h; // add perturbation to initial value of x[0] + x[1] = 0.0; // integrate perturbed system boost::numeric::odeint::integrate_adaptive(make_controlled(abs_tol, rel_tol), diff --git a/cpp/examples/ad_square_example.cpp b/cpp/examples/ad_square_example.cpp index 1ba1039fca..72777818ea 100644 --- a/cpp/examples/ad_square_example.cpp +++ b/cpp/examples/ad_square_example.cpp @@ -18,16 +18,16 @@ * limitations under the License. */ -#include "ad/ad.hpp" +#include "memilio/ad/ad.h" #include /* This example computes the derivative of f(x) = x^2 in two different ways: * 1. Using the forward mode of algorithmic differentiation (AD) - * 2. Using the reverse mode of algorithmic differentiation - * + * 2. Using the reverse mode of algorithmic differentiation + * * Assume, a subroutine is given that computes y = f(x), where x is an n-dimenstional input vector - * and y is an m-dimensional output vector. - * + * and y is an m-dimensional output vector. + * * Forward mode AD (also known as tangent-linear mode) * computes Jacobian-vector products of f(x). I.e., if J(x) denotes the Jacobian of f(x) and * v is an n-dimensional seed vector, the forward mode computes the matrix-vector product @@ -36,7 +36,7 @@ * ad::value(x). The derivative information is accessed by ad::derivative(x) which is accordingly * used to set the seed vector v, i.e., ad::derivative(x) = v. * After the conmputation the Jacobian-vector product is accessed by the command ad::derivative(y). - * + * * Reverse mode (also known as adjoint mode, or back progpagation) computes vector-Jacobian products of f(x). * I.e, if J(x) denotes the Jacobian of f(x) and * w is an m-dimensional seed vector (which is a row vector), the reverse mode computes the vector-matrix product @@ -48,11 +48,11 @@ * memory location called tape. After * the forward sweep, the seed of the computation is set by the command ad::derivative(y) = w. * And a reverse sweep, where the actual derivative computation takes place, has to be performed. The derivative information of - * x has to be initalized to zero (ad::derivative(x) = 0), before the tape can be interpreted backwards. Then, + * x has to be initalized to zero (ad::derivative(x) = 0), before the tape can be interpreted backwards. Then, * the tape can interpreted backwards * and the vector-Jacobian product can be accessed the command ad::derivative(x). - * - * For a concise introduction to AD we recommend the article: + * + * For a concise introduction to AD we recommend the article: * Verma, Arun. "An introduction to automatic differentiation." Current Science (2000): 804-807. */ diff --git a/cpp/examples/adapt_rk_test.cpp b/cpp/examples/adapt_rk_test.cpp index c731762479..f257abcadf 100644 --- a/cpp/examples/adapt_rk_test.cpp +++ b/cpp/examples/adapt_rk_test.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -23,6 +23,7 @@ #include #include #include +#include void init_vectors(std::vector& y, std::vector& sol, size_t n) { @@ -77,7 +78,7 @@ int main() std::vector y; std::vector sol; - const double pi = std::acos(-1); + const double pi = std::numbers::pi_v; size_t n = 10; double t0 = 0; diff --git a/cpp/examples/cli.cpp b/cpp/examples/cli.cpp index dca37259c6..6dcfe55bcd 100644 --- a/cpp/examples/cli.cpp +++ b/cpp/examples/cli.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding diff --git a/cpp/examples/d_abm.cpp b/cpp/examples/d_abm.cpp index 0d5eea9863..09ffa04d5c 100644 --- a/cpp/examples/d_abm.cpp +++ b/cpp/examples/d_abm.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2024 German Aerospace Center (DLR-SC) * * Authors: Julia Bicker, René Schmieding @@ -56,7 +56,7 @@ int main() } //Set adoption rates - std::vector> adoption_rates; + std::vector> adoption_rates; for (size_t region = 0; region < 4; ++region) { adoption_rates.push_back({InfectionState::S, InfectionState::E, diff --git a/cpp/examples/euler_test.cpp b/cpp/examples/euler_test.cpp index ca8c880d29..80da348a7a 100644 --- a/cpp/examples/euler_test.cpp +++ b/cpp/examples/euler_test.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -23,6 +23,7 @@ #include #include #include +#include void init_vectors(std::vector& y, std::vector& sol, size_t n) { @@ -59,7 +60,7 @@ int main() std::vector y; std::vector sol; - const double pi = std::acos(-1); + const double pi = std::numbers::pi_v; size_t n = 10; double t0 = 0; diff --git a/cpp/examples/glct_secir.cpp b/cpp/examples/glct_secir.cpp index 77c92ae0e9..4e291eb1f2 100644 --- a/cpp/examples/glct_secir.cpp +++ b/cpp/examples/glct_secir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -42,9 +42,9 @@ int main() // subcompartments is chosen. The transition probabilities are defined in the StartingProbabilities. constexpr size_t NumExposed = 2, NumInfectedNoSymptoms = 6, NumInfectedSymptoms = 2, NumInfectedSevere = 2, NumInfectedCritical = 10; - using Model = mio::glsecir::Model; - using LctState = Model::LctState; + using Model = mio::glsecir::Model; + using LctState = Model::LctState; using InfectionState = LctState::InfectionState; Model model; @@ -118,13 +118,13 @@ int main() // Exposed. // The get_default of the StartingProbabilities returns the first unit vector of the defined size. // It is necessary to set it although the default method is used to define the length of the vector. - model.parameters.get() = - mio::glsecir::StartingProbabilitiesExposed().get_default( + model.parameters.get>() = + mio::glsecir::StartingProbabilitiesExposed().get_default( LctState::get_num_subcompartments()); // The get_default function returns the TransitionMatrix that is required to have an Erlang-distributed // stay time with an average of timeExposed. - model.parameters.get() = - mio::glsecir::TransitionMatrixExposedToInfectedNoSymptoms().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixExposedToInfectedNoSymptoms().get_default( LctState::get_num_subcompartments(), timeExposed); // This definition of the StartingProbability and the TransitionMatrix lead to an Erlang-distributed latent stage. @@ -138,16 +138,16 @@ int main() StartingProbabilitiesInfectedNoSymptoms[0] = 1 - recoveredPerInfectedNoSymptoms; StartingProbabilitiesInfectedNoSymptoms[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = recoveredPerInfectedNoSymptoms; - model.parameters.get() = + model.parameters.get>() = StartingProbabilitiesInfectedNoSymptoms; // Define equal TransitionMatrices for the strains. // They follow the same Erlang-distribution such that we get the same result as with one strain in the LCT model. - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedNoSymptomsToInfectedSymptoms().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedNoSymptomsToInfectedSymptoms().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedNoSymptoms); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedNoSymptomsToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedNoSymptomsToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedNoSymptoms); // Do the same for all compartments. @@ -157,12 +157,13 @@ int main() StartingProbabilitiesInfectedSymptoms[0] = severePerInfectedSymptoms; StartingProbabilitiesInfectedSymptoms[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - severePerInfectedSymptoms; - model.parameters.get() = StartingProbabilitiesInfectedSymptoms; - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSymptomsToInfectedSevere().get_default( + model.parameters.get>() = + StartingProbabilitiesInfectedSymptoms; + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSymptomsToInfectedSevere().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedSymptoms); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSymptomsToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSymptomsToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedSymptoms); // InfectedSevere. Eigen::VectorX StartingProbabilitiesInfectedSevere = @@ -170,12 +171,13 @@ int main() StartingProbabilitiesInfectedSevere[0] = criticalPerSevere; StartingProbabilitiesInfectedSevere[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - criticalPerSevere; - model.parameters.get() = StartingProbabilitiesInfectedSevere; - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSevereToInfectedCritical().get_default( + model.parameters.get>() = + StartingProbabilitiesInfectedSevere; + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSevereToInfectedCritical().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedSevere); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSevereToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSevereToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedSevere); // InfectedCritical. Eigen::VectorX StartingProbabilitiesInfectedCritical = @@ -183,23 +185,25 @@ int main() StartingProbabilitiesInfectedCritical[0] = deathsPerCritical; StartingProbabilitiesInfectedCritical[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - deathsPerCritical; - model.parameters.get() = StartingProbabilitiesInfectedCritical; - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedCriticalToDead().get_default( + model.parameters.get>() = + StartingProbabilitiesInfectedCritical; + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedCriticalToDead().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedCritical); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), timeInfectedCritical); - model.parameters.get() = 0.05; + model.parameters.get>() = 0.05; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); // From SimulationTime 5, the contact pattern is reduced to 30% of the initial value. - contact_matrix[0].add_damping(0.7, mio::SimulationTime(5.)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(5.)); - model.parameters.get() = 0.7; - model.parameters.get() = 0.25; + model.parameters.get>() = 0.7; + model.parameters.get>() = 0.25; // Perform a simulation. mio::TimeSeries result = mio::simulate(t0, tmax, dt_init, model); diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 34e89485c9..3e7443fbc9 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2024 MEmilio * * Authors: Julia Bicker diff --git a/cpp/examples/graph_stochastic_mobility.cpp b/cpp/examples/graph_stochastic_mobility.cpp index 75cca1c63e..4de856574b 100644 --- a/cpp/examples/graph_stochastic_mobility.cpp +++ b/cpp/examples/graph_stochastic_mobility.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -34,24 +34,24 @@ int main(int /*argc*/, char** /*argv*/) const auto dt = 0.1; //initial time step //total compartment sizes - double num_total = 10000, num_exp = 200, num_ins = 50, num_is = 50, num_isev = 10, num_icri = 5, num_rec = 0, - num_dead = 0; + ScalarType num_total = 10000, num_exp = 200, num_ins = 50, num_is = 50, num_isev = 10, num_icri = 5, num_rec = 0, + num_dead = 0; //model with 3 age groups - mio::osecir::Model model(3); + mio::osecir::Model model(3); auto& params = model.parameters; auto num_age_groups = params.get_num_groups(); - double fact = 1.0 / (double)(size_t)num_age_groups; + ScalarType fact = 1.0 / (ScalarType)(size_t)num_age_groups; //set initialization and model parameters for (auto i = mio::AgeGroup(0); i < num_age_groups; i++) { - params.get>()[i] = 3.2; - params.get>()[i] = 2.; - params.get>()[i] = 6.; - params.get>()[i] = 12.; - params.get>()[i] = 8.; + params.get>()[i] = 3.2; + params.get>()[i] = 2.; + params.get>()[i] = 6.; + params.get>()[i] = 12.; + params.get>()[i] = 8.; //initial populations is equally distributed among age groups model.populations[{i, mio::osecir::InfectionState::Exposed}] = fact * num_exp; @@ -64,21 +64,22 @@ int main(int /*argc*/, char** /*argv*/) model.populations.set_difference_from_group_total({i, mio::osecir::InfectionState::Susceptible}, fact * num_total); - params.get>()[i] = 0.05; - params.get>()[i] = 0.67; - params.get>()[i] = 0.09; - params.get>()[i] = 0.25; - params.get>()[i] = 0.2; - params.get>()[i] = 0.25; - params.get>()[i] = 0.3; + params.get>()[i] = 0.05; + params.get>()[i] = 0.67; + params.get>()[i] = 0.09; + params.get>()[i] = 0.25; + params.get>()[i] = 0.2; + params.get>()[i] = 0.25; + params.get>()[i] = 0.3; } //add contact pattern and contact damping - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_age_groups, (size_t)num_age_groups, fact * 10)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)num_age_groups, (size_t)num_age_groups, 0.6), - mio::SimulationTime(5.)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)num_age_groups, (size_t)num_age_groups, fact * 10)); + contact_matrix.add_damping( + Eigen::MatrixX::Constant((size_t)num_age_groups, (size_t)num_age_groups, 0.6), + mio::SimulationTime(5.)); model.apply_constraints(); @@ -94,12 +95,13 @@ int main(int /*argc*/, char** /*argv*/) fact * num_total); } - mio::Graph>>, mio::MobilityEdgeStochastic> + mio::Graph>>, + mio::MobilityEdgeStochastic> graph; graph.add_node(1001, model, t0); graph.add_node(1002, model2, t0); - auto transition_rates = mio::MobilityCoefficients(model.populations.numel()); + auto transition_rates = mio::MobilityCoefficients(model.populations.numel()); ScalarType kappa = 0.01; for (auto age = mio::AgeGroup(0); age < num_age_groups; age++) { @@ -134,7 +136,7 @@ int main(int /*argc*/, char** /*argv*/) graph.add_edge(0, 1, std::move(transition_rates)); graph.add_edge(1, 0, std::move(transition_rates)); - auto sim = mio::make_mobility_sim(t0, dt, std::move(graph)); + auto sim = mio::make_mobility_sim(t0, dt, std::move(graph)); sim.advance(tmax); diff --git a/cpp/examples/history.cpp b/cpp/examples/history.cpp index 71763d386b..b77d65657f 100644 --- a/cpp/examples/history.cpp +++ b/cpp/examples/history.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Carlotta Gerstein diff --git a/cpp/examples/ide_initialization.cpp b/cpp/examples/ide_initialization.cpp index 85abf27198..29f7280e8b 100644 --- a/cpp/examples/ide_initialization.cpp +++ b/cpp/examples/ide_initialization.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke, Anna Wendler @@ -77,10 +77,10 @@ int main(int argc, char** argv) if (filename.empty()) { std::cout << "You did not provide a valid filename. A default initialization is used." << std::endl; - using Vec = mio::TimeSeries::Vector; + using Vec = Eigen::VectorX; mio::TimeSeries init(num_agegroups * (size_t)mio::isecir::InfectionTransition::Count); - init.add_time_point(-7., - Vec::Constant((size_t)mio::isecir::InfectionTransition::Count, 1. * dt)); + init.add_time_point>( + -7., Vec::Constant((size_t)mio::isecir::InfectionTransition::Count, 1. * dt)); while (init.get_last_time() < -dt / 2.) { init.add_time_point(init.get_last_time() + dt, Vec::Constant((int)mio::isecir::InfectionTransition::Count, 1. * dt)); diff --git a/cpp/examples/ide_secir.cpp b/cpp/examples/ide_secir.cpp index e2b836ce3b..461e127153 100644 --- a/cpp/examples/ide_secir.cpp +++ b/cpp/examples/ide_secir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Anna Wendler, Lena Ploetzke @@ -32,7 +32,7 @@ int main() { - using Vec = mio::TimeSeries::Vector; + using Vec = Eigen::VectorX; size_t num_agegroups = 1; @@ -80,9 +80,9 @@ int main() // model.populations.get_last_value()[(Eigen::Index)mio::isecir::InfectionState::Recovered] = 0; // Set working parameters. - mio::SmootherCosine smoothcos(2.0); - mio::StateAgeFunctionWrapper delaydistribution(smoothcos); - std::vector vec_delaydistrib(num_transitions, delaydistribution); + mio::SmootherCosine smoothcos(2.0); + mio::StateAgeFunctionWrapper delaydistribution(smoothcos); + std::vector> vec_delaydistrib(num_transitions, delaydistribution); // TransitionDistribution is not used for SusceptibleToExposed. Therefore, the parameter can be set to any value. vec_delaydistrib[(int)mio::isecir::InfectionTransition::SusceptibleToExposed].set_distribution_parameter(-1.); vec_delaydistrib[(int)mio::isecir::InfectionTransition::InfectedNoSymptomsToInfectedSymptoms] @@ -96,12 +96,13 @@ int main() vec_prob[Eigen::Index(mio::isecir::InfectionTransition::ExposedToInfectedNoSymptoms)] = 1; model.parameters.get()[mio::AgeGroup(0)] = vec_prob; - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 10.)); - model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 10.)); + model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); - mio::ExponentialSurvivalFunction exponential(0.5); - mio::StateAgeFunctionWrapper prob(exponential); + mio::ExponentialSurvivalFunction exponential(0.5); + mio::StateAgeFunctionWrapper prob(exponential); model.parameters.get()[mio::AgeGroup(0)] = prob; model.parameters.get()[mio::AgeGroup(0)] = prob; diff --git a/cpp/examples/ide_secir_ageres.cpp b/cpp/examples/ide_secir_ageres.cpp index 844614b1ce..66ce473850 100644 --- a/cpp/examples/ide_secir_ageres.cpp +++ b/cpp/examples/ide_secir_ageres.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Hannah Tritzschak, Anna Wendler @@ -31,7 +31,7 @@ int main() { - using Vec = mio::TimeSeries::Vector; + using Vec = Eigen::VectorX; size_t num_agegroups = 2; @@ -89,18 +89,18 @@ int main() // Set working parameters. // First AgeGroup for Transition Distributions. - mio::SmootherCosine smoothcos1(2.0); - mio::StateAgeFunctionWrapper delaydistribution1(smoothcos1); - std::vector vec_delaydistrib1(num_transitions, delaydistribution1); + mio::SmootherCosine smoothcos1(2.0); + mio::StateAgeFunctionWrapper delaydistribution1(smoothcos1); + std::vector> vec_delaydistrib1(num_transitions, delaydistribution1); // TransitionDistribution is not used for SusceptibleToExposed. Therefore, the parameter can be set to any value. vec_delaydistrib1[(int)mio::isecir::InfectionTransition::SusceptibleToExposed].set_distribution_parameter(-1.); model.parameters.get()[mio::AgeGroup(0)] = vec_delaydistrib1; //Second AgeGroup for Transition Distributions. - mio::SmootherCosine smoothcos2(3.0); - mio::StateAgeFunctionWrapper delaydistribution2(smoothcos2); - std::vector vec_delaydistrib2(num_transitions, delaydistribution2); + mio::SmootherCosine smoothcos2(3.0); + mio::StateAgeFunctionWrapper delaydistribution2(smoothcos2); + std::vector> vec_delaydistrib2(num_transitions, delaydistribution2); // TransitionDistribution is not used for SusceptibleToExposed. Therefore, the parameter can be set to any value. vec_delaydistrib2[(int)mio::isecir::InfectionTransition::SusceptibleToExposed].set_distribution_parameter(-1.); @@ -114,12 +114,14 @@ int main() model.parameters.get()[group] = vec_prob; } - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, static_cast(num_agegroups)); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 10.)); + mio::ContactMatrixGroup contact_matrix = + mio::ContactMatrixGroup(1, static_cast(num_agegroups)); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 10.)); model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); - mio::ExponentialSurvivalFunction exponential(0.5); - mio::StateAgeFunctionWrapper prob(exponential); + mio::ExponentialSurvivalFunction exponential(0.5); + mio::StateAgeFunctionWrapper prob(exponential); for (mio::AgeGroup group = mio::AgeGroup(0); group < mio::AgeGroup(num_agegroups); ++group) { model.parameters.get()[group] = prob; model.parameters.get()[group] = prob; diff --git a/cpp/examples/ide_secir_graph.cpp b/cpp/examples/ide_secir_graph.cpp index c6f637bfe3..7692a61d50 100644 --- a/cpp/examples/ide_secir_graph.cpp +++ b/cpp/examples/ide_secir_graph.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Anna Wendler @@ -29,12 +29,12 @@ int main() // This is an example for running the IDE-SECIR model in a graph. It demonstrates a simple setup for a graph without // mobility that consists of two nodes and no edges. - using Vec = mio::TimeSeries::Vector; + using Vec = Eigen::VectorX; const ScalarType t0 = 0.; const ScalarType tmax = 10.; - // Set time step size of IDE solver. Note that the time step size will be constant throughout the simulation. + // Set time step size of IDE solver. Note that the time step size will be constant throughout the simulation. const ScalarType dt_ide_solver = 1.; size_t num_agegroups = 1; @@ -46,7 +46,7 @@ int main() // Create TimeSeries with num_transitions * num_agegroups elements where initial transitions needed for simulation // will be stored. We require values for the transitions for a sufficient number of time points before the start of - // the simulation to initialize our model. + // the simulation to initialize our model. size_t num_transitions = (size_t)mio::isecir::InfectionTransition::Count; mio::TimeSeries transitions_init(num_transitions * num_agegroups); @@ -62,11 +62,11 @@ int main() vec_init[(size_t)mio::isecir::InfectionTransition::InfectedSevereToRecovered] = 1.; vec_init[(size_t)mio::isecir::InfectionTransition::InfectedCriticalToDead] = 1.; vec_init[(size_t)mio::isecir::InfectionTransition::InfectedCriticalToRecovered] = 1.; - // Multiply vec_init with dt_ide_solver so that within a time interval of length 1, always the above number of - // individuals are transitioning from one compartment to another, irrespective of the chosen time step size. + // Multiply vec_init with dt_ide_solver so that within a time interval of length 1, always the above number of + // individuals are transitioning from one compartment to another, irrespective of the chosen time step size. vec_init = vec_init * dt_ide_solver; - // In this example, we use the default transition distributions. For these distributions, setting the initial time + // In this example, we use the default transition distributions. For these distributions, setting the initial time // point of the TimeSeries transitions_init at time -10 will give us a sufficient number of time points before t0=0. // For more information on this, we refer to the documentation of TransitionDistributions in // models/ide_secir/parameters.h. @@ -76,22 +76,22 @@ int main() transitions_init.add_time_point(transitions_init.get_last_time() + dt_ide_solver, vec_init); } - // Initialize IDE model that will be used in in each node in the graph below. + // Initialize IDE model that will be used in in each node in the graph below. mio::isecir::Model model(std::move(transitions_init), N_init, deaths_init, num_agegroups); model.check_constraints(dt_ide_solver); - // Set up graph with two nodes and no edges. To each node, we pass an id, the above constructed IDE model as well + // Set up graph with two nodes and no edges. To each node, we pass an id, the above constructed IDE model as well // as the time step size that will be used by the numerical solver. For simplicity, we use the same model in // both nodes. - mio::Graph, mio::MobilityEdge> g; + mio::Graph, mio::MobilityEdge> g; g.add_node(1001, model, dt_ide_solver); g.add_node(1002, model, dt_ide_solver); - // We use make_no_mobilty_sim to create a GraphSimulation for the model graph we defined above. This function allows - // us to create a simulation without having to define mobility between nodes. Any edges would be effectively + // We use make_no_mobilty_sim to create a GraphSimulation for the model graph we defined above. This function allows + // us to create a simulation without having to define mobility between nodes. Any edges would be effectively // ignored by the simulation. auto sim = mio::make_no_mobility_sim(t0, std::move(g)); - // Run the simulation. This advances all graph nodes independently. + // Run the simulation. This advances all graph nodes independently. sim.advance(tmax); // Print results of first node. diff --git a/cpp/examples/ide_seir.cpp b/cpp/examples/ide_seir.cpp index ed5dd1682f..aa67ba8947 100644 --- a/cpp/examples/ide_seir.cpp +++ b/cpp/examples/ide_seir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -25,41 +25,41 @@ int main() { /** - * Note: the initial values as well as all other parameters are randomly chosen for this example and are not + * Note: the initial values as well as all other parameters are randomly chosen for this example and are not * intended to characterize the real world. - * This example has the purpose to show how the IDE SEIR model can be applied. + * This example has the purpose to show how the IDE SEIR model can be applied. */ - using Vec = mio::TimeSeries::Vector; + using Vector = Eigen::Matrix; - int tmax = 15; - int N = 810000; - double dt = 0.1; - mio::TimeSeries init(1); + int tmax = 15; + int N = 810000; + ScalarType dt = 0.1; + mio::TimeSeries init(1); /** - * Construction of the initial TimeSeries with point of times and the corresponding number of susceptibles. - * The smallest time should be small enough. See the documentation of the IdeSeirModel constructor for + * Construction of the initial TimeSeries with point of times and the corresponding number of susceptibles. + * The smallest time should be small enough. See the documentation of the IdeSeirModel constructor for * detailed information. Initial data are chosen randomly. */ - init.add_time_point(-15.0, Vec::Constant(1, N * 0.95)); + init.add_time_point(-15.0, Vector::Constant(1, N * 0.95)); while (init.get_last_time() < 0) { init.add_time_point(init.get_last_time() + dt, - Vec::Constant(1, (double)init.get_last_value()[0] + init.get_last_time())); + Vector::Constant(1, (ScalarType)init.get_last_value()[0] + init.get_last_time())); } // Initialize model. - mio::iseir::Model model(std::move(init), dt, N); + mio::iseir::Model model(std::move(init), dt, N); // Set working parameters. model.parameters.set(3.3); model.parameters.set(8.2); model.parameters.set(0.015); - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, 1); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10.)); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, 1); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10.)); // Add damping. - contact_matrix[0].add_damping(0.7, mio::SimulationTime(10.)); - model.parameters.get>() = mio::UncertainContactMatrix(contact_matrix); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(10.)); + model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); // Carry out simulation. model.simulate(tmax); diff --git a/cpp/examples/lct_secir.cpp b/cpp/examples/lct_secir.cpp index 03278b1982..db3e5d9839 100644 --- a/cpp/examples/lct_secir.cpp +++ b/cpp/examples/lct_secir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -40,9 +40,9 @@ int main() constexpr size_t NumExposed = 2, NumInfectedNoSymptoms = 3, NumInfectedSymptoms = 1, NumInfectedSevere = 1, NumInfectedCritical = 5; using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; // Variable defines whether the class Initializer is used to define an initial vector from flows or whether a manually @@ -52,25 +52,26 @@ int main() ScalarType tmax = 10; // Set Parameters. - model.parameters.get()[0] = 3.2; - model.parameters.get()[0] = 2.; - model.parameters.get()[0] = 5.8; - model.parameters.get()[0] = 9.5; - model.parameters.get()[0] = 7.1; + model.parameters.get>()[0] = 3.2; + model.parameters.get>()[0] = 2.; + model.parameters.get>()[0] = 5.8; + model.parameters.get>()[0] = 9.5; + model.parameters.get>()[0] = 7.1; - model.parameters.get()[0] = 0.05; + model.parameters.get>()[0] = 0.05; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); // From SimulationTime 5, the contact pattern is reduced to 30% of the initial value. - contact_matrix[0].add_damping(0.7, mio::SimulationTime(5.)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(5.)); - model.parameters.get()[0] = 0.7; - model.parameters.get()[0] = 0.25; - model.parameters.get()[0] = 0.09; - model.parameters.get()[0] = 0.2; - model.parameters.get()[0] = 0.25; - model.parameters.get()[0] = 0.3; + model.parameters.get>()[0] = 0.7; + model.parameters.get>()[0] = 0.25; + model.parameters.get>()[0] = 0.09; + model.parameters.get>()[0] = 0.2; + model.parameters.get>()[0] = 0.25; + model.parameters.get>()[0] = 0.3; if (use_initializer_flows) { // Example how to use the class Initializer for the definition of an initial vector for the LCT model. @@ -104,7 +105,7 @@ int main() } // Set initialization vector for the LCT model. - mio::lsecir::Initializer initializer(std::move(flows), model); + mio::lsecir::Initializer initializer(std::move(flows), model); initializer.set_tol_for_support_max(1e-6); auto status = initializer.compute_initialization_vector(total_population, deaths, total_confirmed_cases); if (status) { diff --git a/cpp/examples/ode_seair.cpp b/cpp/examples/ode_seair.cpp index 330226c524..484254c7e5 100644 --- a/cpp/examples/ode_seair.cpp +++ b/cpp/examples/ode_seair.cpp @@ -23,7 +23,7 @@ // A detailed description of the model can be found in the publication // Tsay et al. (2020), Modeling, state estimation, and optimal control for the US COVID-19 outbreak. -#include "ad/ad.hpp" +#include "memilio/ad/ad.h" #include "ode_seair/model.h" #include "ode_seair/infection_state.h" @@ -56,16 +56,23 @@ void set_initial_values(mio::oseair::Model& model) int main() { - mio::set_log_level(mio::LogLevel::debug); using FP = typename ad::gt1s::type; // algorithmic differentiation data type: scalar tangent-linear mode FP t0 = 0; FP tmax = 10; - FP dt = 0.3; + FP dt = 0.1; + + mio::log_info("Simulating SEAIR; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::log_info("Simulating SEAIR; t={} ... {} with dt = {}.", ad::value(t0), ad::value(tmax), ad::value(dt)); + // Accurate automatic differentiation (AD) and Finite Differences (FP) require higher-precision numerical integrators than typically used. + auto AD_integrator = + std::make_unique>( + 1e-12, 1e-8, std::numeric_limits::min(), dt); + auto double_integrator = + std::make_unique>( + 1e-12, 1e-8, std::numeric_limits::min(), ad::value(dt)); // Compute derivative of the final states of the model with respect to the initial value of the suscetible population mio::oseair::Model model1; @@ -74,17 +81,17 @@ int main() ad::derivative(value) = 1.0; model1.populations[{mio::Index(mio::oseair::InfectionState::Susceptible)}] = value; model1.check_constraints(); - auto seair1 = mio::simulate>(t0, tmax, dt, model1); + auto seair1 = mio::simulate>(t0, tmax, dt, model1, std::move(AD_integrator)); // We want to compare the derivatives computed ba algorithmic differention with difference quotient. // To this end we perturbe the corresponding initial value of the by an increment h and simulate again. mio::oseair::Model model2; set_initial_values(model2); - double h = 1e-4; + double h = 1e-3; model2.populations[{mio::Index(mio::oseair::InfectionState::Susceptible)}] += h; model2.check_constraints(); - mio::TimeSeries seair2 = - mio::simulate>(ad::value(t0), ad::value(tmax), ad::value(dt), model2); + mio::TimeSeries seair2 = mio::simulate>( + ad::value(t0), ad::value(tmax), ad::value(dt), model2, std::move(double_integrator)); const std::string file_name = "seair-compare.csv"; std::ofstream file(file_name); diff --git a/cpp/examples/ode_seair_optimization.cpp b/cpp/examples/ode_seair_optimization.cpp index f86066cd52..3f2cd75ba7 100644 --- a/cpp/examples/ode_seair_optimization.cpp +++ b/cpp/examples/ode_seair_optimization.cpp @@ -16,12 +16,12 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* +* * The documentation of the Ipopt::TNLP member functions in Seair_NLP * is extracted from the Ipopt documentation */ -#include "ad/ad.hpp" +#include "memilio/ad/ad.h" #include "memilio/utils/compiler_diagnostics.h" #include "ode_seair/model.h" @@ -47,13 +47,13 @@ class Seair_NLP : public Ipopt::TNLP { public: - static constexpr double N = 327167434; // total US population - Seair_NLP() = default; - Seair_NLP(const Seair_NLP&) = delete; - Seair_NLP(Seair_NLP&&) = delete; + static constexpr double N = 327167434; // total US population + Seair_NLP() = default; + Seair_NLP(const Seair_NLP&) = delete; + Seair_NLP(Seair_NLP&&) = delete; Seair_NLP& operator=(const Seair_NLP&) = delete; - Seair_NLP& operator=(Seair_NLP&&) = delete; - ~Seair_NLP() = default; + Seair_NLP& operator=(Seair_NLP&&) = delete; + ~Seair_NLP() = default; /** Method to request the initial information about the problem. * @@ -266,7 +266,7 @@ class Seair_NLP : public Ipopt::TNLP * @param constraints are the constraints of the NLP * @param objective is the objectie of the NLP */ - template + template void eval_objective_constraints(const std::vector& x, std::vector& constraints, FP& objective); public: @@ -619,7 +619,6 @@ int main() // suitable for your optimization problem. app->Options()->SetNumericValue("tol", 1e-6); app->Options()->SetStringValue("mu_strategy", "adaptive"); - app->Options()->SetStringValue("output_file", "ipopt.out"); app->Options()->SetStringValue("hessian_approximation", "limited-memory"); app->Options()->SetStringValue("limited_memory_update_type", "bfgs"); diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index dcb41b4599..485c024ec7 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -25,31 +25,32 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0; - double tmax = 50; - double dt = 0.1; + ScalarType t0 = 0; + ScalarType tmax = 50; + ScalarType dt = 0.1; mio::log_info("Simulating SECIR; t={} ... {} with dt = {}.", t0, tmax, dt); - double cont_freq = 10; // see Polymod study + ScalarType cont_freq = 10; // see Polymod study - double nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, - nb_rec_t0 = 10, nb_dead_t0 = 0; + ScalarType nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, + nb_rec_t0 = 10, nb_dead_t0 = 0; - mio::osecir::Model model(1); + mio::osecir::Model model(1); - model.parameters.template set(60); - model.parameters.set>(0.2); + model.parameters.template set>(60); + model.parameters.set>(0.2); - model.parameters.get>() = 3.2; - model.parameters.get>() = 2.0; - model.parameters.get>() = 5.8; - model.parameters.get>() = 9.5; - model.parameters.get>() = 7.1; + model.parameters.get>() = 3.2; + model.parameters.get>() = 2.0; + model.parameters.get>() = 5.8; + model.parameters.get>() = 9.5; + model.parameters.get>() = 7.1; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, cont_freq)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); model.populations.set_total(nb_total_t0); model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Exposed}] = nb_exp_t0; @@ -64,20 +65,20 @@ int main() model.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - model.parameters.get>() = 0.05; - model.parameters.get>() = 0.7; - model.parameters.get>() = 0.09; - model.parameters.get>() = 0.25; - model.parameters.get>() = 0.45; - model.parameters.get>() = 35; - model.parameters.get>() = 0.2; - model.parameters.get>() = 0.25; - model.parameters.get>() = 0.3; + model.parameters.get>() = 0.05; + model.parameters.get>() = 0.7; + model.parameters.get>() = 0.09; + model.parameters.get>() = 0.25; + model.parameters.get>() = 0.45; + model.parameters.get>() = 35; + model.parameters.get>() = 0.2; + model.parameters.get>() = 0.25; + model.parameters.get>() = 0.3; model.apply_constraints(); // Using default Integrator - mio::TimeSeries secir = simulate(t0, tmax, dt, model); + mio::TimeSeries secir = mio::osecir::simulate(t0, tmax, dt, model); /* Example of using a different integrator @@ -88,7 +89,7 @@ int main() integrator->set_dt_max(1.0); integrator->set_rel_tolerance(1e-4); integrator->set_abs_tolerance(1e-1); - mio::TimeSeries secir = simulate(t0, tmax, dt, model, std::move(integrator)); + mio::TimeSeries secir = simulate(t0, tmax, dt, model, std::move(integrator)); */ bool print_to_terminal = true; @@ -103,13 +104,13 @@ int main() auto num_points = static_cast(secir.get_num_time_points()); for (size_t i = 0; i < num_points; i++) { printf("\n%.14f ", secir.get_time(i)); - Eigen::VectorXd res_j = secir.get_value(i); + Eigen::VectorX res_j = secir.get_value(i); for (size_t j = 0; j < (size_t)mio::osecir::InfectionState::Count; j++) { printf(" %.14f", res_j[j]); } } - Eigen::VectorXd res_j = secir.get_last_value(); + Eigen::VectorX res_j = secir.get_last_value(); printf("number total: %f", res_j[0] + res_j[1] + res_j[2] + res_j[3] + res_j[4] + res_j[5] + res_j[6] + res_j[7]); } diff --git a/cpp/examples/ode_secir_ageres.cpp b/cpp/examples/ode_secir_ageres.cpp index fb18159ad6..db60245d15 100644 --- a/cpp/examples/ode_secir_ageres.cpp +++ b/cpp/examples/ode_secir_ageres.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -27,16 +27,16 @@ int main() mio::set_log_level(mio::LogLevel::debug); - double t0 = 0; - double tmax = 50; - double dt = 0.1; + ScalarType t0 = 0; + ScalarType tmax = 50; + ScalarType dt = 0.1; mio::log_info("Simulating SECIR; t={} ... {} with dt = {}.", t0, tmax, dt); - double cont_freq = 10; // see Polymod study + ScalarType cont_freq = 10; // see Polymod study - double nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, - nb_rec_t0 = 10, nb_dead_t0 = 0; + ScalarType nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, + nb_rec_t0 = 10, nb_dead_t0 = 0; // alpha = alpha_in; // percentage of asymptomatic cases // beta = beta_in; // risk of infection from the infected symptomatic patients @@ -44,22 +44,22 @@ int main() // theta = theta_in; // icu per hospitalized // delta = delta_in; // deaths per ICUs - mio::osecir::Model model(3); - auto nb_groups = model.parameters.get_num_groups(); - double fact = 1.0 / (double)(size_t)nb_groups; + mio::osecir::Model model(3); + auto nb_groups = model.parameters.get_num_groups(); + ScalarType fact = 1.0 / (ScalarType)(size_t)nb_groups; auto& params = model.parameters; - params.set(60); - params.set>(0.2); - params.get>() = 35; + params.set>(60); + params.set>(0.2); + params.get>() = 35; for (auto i = mio::AgeGroup(0); i < nb_groups; i++) { - params.get>()[i] = 3.2; - params.get>()[i] = 2.; - params.get>()[i] = 5.8; - params.get>()[i] = 9.5; - params.get>()[i] = 7.1; + params.get>()[i] = 3.2; + params.get>()[i] = 2.; + params.get>()[i] = 5.8; + params.get>()[i] = 9.5; + params.get>()[i] = 7.1; model.populations[{i, mio::osecir::InfectionState::Exposed}] = fact * nb_exp_t0; model.populations[{i, mio::osecir::InfectionState::InfectedNoSymptoms}] = fact * nb_car_t0; @@ -73,26 +73,26 @@ int main() model.populations.set_difference_from_group_total({i, mio::osecir::InfectionState::Susceptible}, fact * nb_total_t0); - params.get>()[i] = 0.05; - params.get>()[i] = 0.7; - params.get>()[i] = 0.09; - params.get>()[i] = 0.25; - params.get>()[i] = 0.45; - params.get>()[i] = 0.2; - params.get>()[i] = 0.25; - params.get>()[i] = 0.3; + params.get>()[i] = 0.05; + params.get>()[i] = 0.7; + params.get>()[i] = 0.09; + params.get>()[i] = 0.25; + params.get>()[i] = 0.45; + params.get>()[i] = 0.2; + params.get>()[i] = 0.25; + params.get>()[i] = 0.3; } model.apply_constraints(); - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), - mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), + mio::SimulationTime(30.)); - mio::TimeSeries secir = mio::simulate>(t0, tmax, dt, model); - bool print_to_terminal = true; + mio::TimeSeries secir = mio::simulate>(t0, tmax, dt, model); + bool print_to_terminal = true; if (print_to_terminal) { @@ -101,7 +101,7 @@ int main() printf("People in\n"); for (size_t k = 0; k < (size_t)mio::osecir::InfectionState::Count; k++) { - double dummy = 0; + ScalarType dummy = 0; for (size_t i = 0; i < (size_t)params.get_num_groups(); i++) { printf("\t %s[%d]: %.0f", vars[k].c_str(), (int)i, diff --git a/cpp/examples/ode_secir_contact_changes.cpp b/cpp/examples/ode_secir_contact_changes.cpp index 3f1dd0a361..a9d78d372a 100644 --- a/cpp/examples/ode_secir_contact_changes.cpp +++ b/cpp/examples/ode_secir_contact_changes.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -22,26 +22,26 @@ #include "memilio/utils/logging.h" #include "memilio/math/euler.h" -/* +/* * This example demonstrates how to realize contact behavior changes with any of our ordinary differential * equation-based models. This example can thus easily be adapted for other models like osecirvvs, oseir, osir etc. * We print out the flows (i.e., new transmissions, infections, hospitalizations etc. per time point.) As we use an - * fixed-time step Explicit Euler, we can compare them. + * fixed-time step Explicit Euler, we can compare them. */ int main() { mio::set_log_level(mio::LogLevel::warn); - double t0 = 0; - double dt = 0.1; - double tmax = 1; + ScalarType t0 = 0; + ScalarType dt = 0.1; + ScalarType tmax = 1; - double cont_freq = 10; + ScalarType cont_freq = 10; - double nb_total_t0 = 1000, nb_inf_t0 = 10; + ScalarType nb_total_t0 = 1000, nb_inf_t0 = 10; // default model run to be compared against - mio::osecir::Model model_a(1); + mio::osecir::Model model_a(1); const auto indx_flow_SE = model_a.get_flat_flow_index( {mio::AgeGroup(0)}); @@ -49,8 +49,9 @@ int main() model_a.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_a.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_a = model_a.parameters.get>(); - contact_matrix_a[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + mio::ContactMatrixGroup& contact_matrix_a = + model_a.parameters.get>(); + contact_matrix_a[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, cont_freq)); // set probability of transmission and risk of infection to 1. model_a.parameters.get>() = 1.0; model_a.parameters.get>() = 1.0; @@ -65,14 +66,15 @@ int main() << result_a[1].get_value(1)[indx_flow_SE] << ".\n"; // The contacts are halfed: reduced transmission through damping with value 0.5 - mio::osecir::Model model_b{model_a}; + mio::osecir::Model model_b{model_a}; model_b.populations.set_total(nb_total_t0); model_b.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_b.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_b = model_b.parameters.get>(); - contact_matrix_b[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix_b[0].add_damping(0.5, mio::SimulationTime(0.)); // contact reduction happens here! + mio::ContactMatrixGroup& contact_matrix_b = + model_b.parameters.get>(); + contact_matrix_b[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, cont_freq)); + contact_matrix_b[0].add_damping(0.5, mio::SimulationTime(0.)); // contact reduction happens here! auto result_b = mio::simulate_flows(t0, tmax, dt, model_b, integrator.clone()); result_b[1].print_table({"S->E", "E->I_NS", "I_NS->I_Sy", "I_NS->R", "I_NSC->I_SyC", "I_NSC->R", "I_Sy->I_Sev", @@ -84,14 +86,15 @@ int main() << result_b[1].get_value(1)[indx_flow_SE] << ".\n"; // No contacts at all: no transmission through damping with value 1. - mio::osecir::Model model_c{model_a}; + mio::osecir::Model model_c{model_a}; model_c.populations.set_total(nb_total_t0); model_c.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_c.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_c = model_c.parameters.get>(); - contact_matrix_c[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix_c[0].add_damping(1., mio::SimulationTime(0.)); // contact reduction happens here! + mio::ContactMatrixGroup& contact_matrix_c = + model_c.parameters.get>(); + contact_matrix_c[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, cont_freq)); + contact_matrix_c[0].add_damping(1., mio::SimulationTime(0.)); // contact reduction happens here! auto result_c = mio::simulate_flows(t0, tmax, dt, model_c, integrator.clone()); result_c[1].print_table({"S->E", "E->I_NS", "I_NS->I_Sy", "I_NS->R", "I_NSC->I_SyC", "I_NSC->R", "I_Sy->I_Sev", @@ -108,9 +111,10 @@ int main() model_d.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_d.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_d = model_d.parameters.get>(); - contact_matrix_d[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix_d[0].add_damping(-1., mio::SimulationTime(0.)); // contact increase happens here! + mio::ContactMatrixGroup& contact_matrix_d = + model_d.parameters.get>(); + contact_matrix_d[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, cont_freq)); + contact_matrix_d[0].add_damping(-1., mio::SimulationTime(0.)); // contact increase happens here! auto result_d = mio::simulate_flows(t0, tmax, dt, model_d, integrator.clone()); result_d[1].print_table({"S->E", "E->I_NS", "I_NS->I_Sy", "I_NS->R", "I_NSC->I_SyC", "I_NSC->R", "I_Sy->I_Sev", diff --git a/cpp/examples/ode_secir_feedback.cpp b/cpp/examples/ode_secir_feedback.cpp index eea8885993..68bcfb5d36 100644 --- a/cpp/examples/ode_secir_feedback.cpp +++ b/cpp/examples/ode_secir_feedback.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker @@ -23,7 +23,7 @@ void initialize_model(mio::osecir::Model& model, int total_population, double cont_freq) { - model.parameters.set(60); + model.parameters.set>(60); model.parameters.set>(0.2); // time-related parameters @@ -45,8 +45,8 @@ void initialize_model(mio::osecir::Model& model, int total_population, d model.parameters.get>() = 0.3; // contact matrix - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); // initial population model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Exposed}] = 40; @@ -96,7 +96,7 @@ int main() const double cont_freq = 10; // create and initialize ODE model for a single age group - mio::osecir::Model model(1); + mio::osecir::Model model(1); initialize_model(model, total_population, cont_freq); // determine the index for the ICU state (InfectedCritical) for feedback mechanism diff --git a/cpp/examples/ode_secir_feedback_graph.cpp b/cpp/examples/ode_secir_feedback_graph.cpp index 5011bdce93..43c4c0aa2b 100644 --- a/cpp/examples/ode_secir_feedback_graph.cpp +++ b/cpp/examples/ode_secir_feedback_graph.cpp @@ -32,7 +32,7 @@ using FeedbackSim = mio::FeedbackSimulation& model, double cont_freq) { - model.parameters.set(60); + model.parameters.set>(60); model.parameters.set>(0.2); // Mean stay times per compartment @@ -54,8 +54,8 @@ void initialize_model(mio::osecir::Model& model, double cont_freq) model.parameters.get>() = 0.3; // contact matrix - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); } // helper function to initialize the feedback mechanism parameters for a simulation @@ -82,11 +82,11 @@ void initialize_feedback(FeedbackSim& feedback_simulation) } // helper function to create the graph with nodes and edges -mio::Graph, mio::MobilityEdge> +mio::Graph, mio::MobilityEdge> create_graph(int num_nodes, int total_population, double cont_freq) { // Create a graph for the metapopulation simulation - mio::Graph, mio::MobilityEdge> g; + mio::Graph, mio::MobilityEdge> g; // Create models and add nodes to the graph for (int i = 0; i < num_nodes; ++i) { diff --git a/cpp/examples/ode_secir_graph.cpp b/cpp/examples/ode_secir_graph.cpp index 35505ff182..165e794c7c 100644 --- a/cpp/examples/ode_secir_graph.cpp +++ b/cpp/examples/ode_secir_graph.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Henrik Zunker @@ -34,9 +34,9 @@ int main() const auto dt = 0.5; //time step of Mobility, daily Mobility every second step const size_t num_groups = 1; - mio::osecir::Model model(num_groups); + mio::osecir::Model model(num_groups); model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}] = 10000; - model.parameters.set(0); + model.parameters.set>(0); model.parameters.set>(0.2); model.parameters.get>() = 3.2; @@ -55,16 +55,17 @@ int main() model.parameters.get>() = 0.25; model.parameters.get>() = 0.3; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); //two mostly identical groups auto model_group1 = model; auto model_group2 = model; //some contact restrictions in model_group1 - mio::ContactMatrixGroup& contact_matrix_m1 = + mio::ContactMatrixGroup& contact_matrix_m1 = model_group1.parameters.get>(); - contact_matrix_m1[0].add_damping(0.7, mio::SimulationTime(15.)); + contact_matrix_m1[0].add_damping(0.7, mio::SimulationTime(15.0)); //infection starts in group 1 model_group1.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}] = 9990; @@ -91,13 +92,15 @@ int main() model.populations.get_flat_index({i, mio::osecir::InfectionState::InfectedSymptomsConfirmed})); } - mio::Graph>, mio::MobilityEdge> g; + mio::Graph>, mio::MobilityEdge> g; g.add_node(1001, model_group1, t0); g.add_node(1002, model_group2, t0); - g.add_edge(0, 1, Eigen::VectorXd::Constant((size_t)mio::osecir::InfectionState::Count, 0.1), indices_save_edges); - g.add_edge(1, 0, Eigen::VectorXd::Constant((size_t)mio::osecir::InfectionState::Count, 0.1), indices_save_edges); + g.add_edge(0, 1, Eigen::VectorX::Constant((size_t)mio::osecir::InfectionState::Count, 0.1), + indices_save_edges); + g.add_edge(1, 0, Eigen::VectorX::Constant((size_t)mio::osecir::InfectionState::Count, 0.1), + indices_save_edges); - auto sim = mio::make_mobility_sim(t0, dt, std::move(g)); + auto sim = mio::make_mobility_sim(t0, dt, std::move(g)); sim.advance(tmax); diff --git a/cpp/examples/ode_secir_parameter_sampling.cpp b/cpp/examples/ode_secir_parameter_sampling.cpp index c486b7aac9..e1e08390bc 100644 --- a/cpp/examples/ode_secir_parameter_sampling.cpp +++ b/cpp/examples/ode_secir_parameter_sampling.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -30,10 +30,10 @@ int main() /* * Real valued variable element */ - double mean = 5; - double stddev = 1.5; - double min = 1; - double max = 10; + ScalarType mean = 5; + ScalarType stddev = 1.5; + ScalarType min = 1; + ScalarType max = 10; // check if constructor works correctly mio::ParameterDistributionNormal some_parameter(min, max, mean, stddev); // "some parameter", @@ -47,10 +47,10 @@ int main() counter[rounded]++; } } - double acc = 0; + ScalarType acc = 0; for (int i = 0; i < 9; i++) { - acc += (double)counter[i] / 1000.0; - printf("\n [%d-%d): %.2f %.2f ", i + 1, i + 2, (double)counter[i] / 1000.0, acc); + acc += (ScalarType)counter[i] / 1000.0; + printf("\n [%d-%d): %.2f %.2f ", i + 1, i + 2, (ScalarType)counter[i] / 1000.0, acc); } printf("\n"); @@ -58,7 +58,7 @@ int main() printf("\n U(%.0f,%.0f)-distribution", min, max); mio::ParameterDistributionUniform some_other_parameter(1.0, 10.0); - double counter_unif[10] = {0}; + ScalarType counter_unif[10] = {0}; for (int i = 0; i < 1000; i++) { int rounded = (int)(some_other_parameter.get_sample(mio::thread_local_rng()) - 1); if (rounded >= 0 && rounded < 10) { @@ -67,43 +67,45 @@ int main() } acc = 0; for (int i = 0; i < 9; i++) { - acc += (double)counter_unif[i] / 1000.0; - printf("\n [%d-%d): %.2f %.2f ", i + 1, i + 2, (double)counter_unif[i] / 1000.0, acc); + acc += (ScalarType)counter_unif[i] / 1000.0; + printf("\n [%d-%d): %.2f %.2f ", i + 1, i + 2, (ScalarType)counter_unif[i] / 1000.0, acc); } /* * Contact frequency and dampings variable element */ - mio::osecir::Model model(3); + mio::osecir::Model model(3); auto& params = model.parameters; mio::AgeGroup nb_groups = params.get_num_groups(); - mio::ContactMatrixGroup cm_group{ - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.5))}; - params.get>() = cm_group; + mio::ContactMatrixGroup cm_group{mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.5))}; + params.get>() = cm_group; - params.get>().get_dampings().push_back(mio::DampingSampling( - mio::UncertainValue(0.5), mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(30.), - std::vector(1, size_t(0)), Eigen::VectorXd::Constant(Eigen::Index(nb_groups.get()), 1.0))); - params.get>().get_dampings()[0].get_value().set_distribution( + params.get>().get_dampings().push_back(mio::DampingSampling( + mio::UncertainValue(0.5), mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(30.), std::vector(1, size_t(0)), + Eigen::VectorX::Constant(Eigen::Index(nb_groups.get()), 1.0))); + params.get>().get_dampings()[0].get_value().set_distribution( mio::ParameterDistributionNormal(0.0, 1.0, 0.5, 0.2)); - params.get>().get_dampings().push_back(mio::DampingSampling( - mio::UncertainValue(0.3), mio::DampingLevel(1), mio::DampingType(0), mio::SimulationTime(10.), - std::vector(1, size_t(0)), Eigen::VectorXd::Constant(Eigen::Index(nb_groups.get()), 1.0))); - params.get>().get_dampings()[0].get_value().set_distribution( + params.get>().get_dampings().push_back(mio::DampingSampling( + mio::UncertainValue(0.3), mio::DampingLevel(1), mio::DampingType(0), + mio::SimulationTime(10.), std::vector(1, size_t(0)), + Eigen::VectorX::Constant(Eigen::Index(nb_groups.get()), 1.0))); + params.get>().get_dampings()[0].get_value().set_distribution( mio::ParameterDistributionNormal(0.0, 1.0, 0.4, 0.05)); draw_sample(model); - auto& cfmat_sample = params.get>().get_cont_freq_mat(); + auto& cfmat_sample = params.get>().get_cont_freq_mat(); printf("\n\n Number of dampings: %zu\n", cfmat_sample[0].get_dampings().size()); //Dampings are sorted automatically by time, therefore the second DampingSamping is at the first position - printf("\n First damping at %.2f with factor %.2f\n", double(cfmat_sample[0].get_dampings()[0].get_time()), + printf("\n First damping at %.2f with factor %.2f\n", ScalarType(cfmat_sample[0].get_dampings()[0].get_time()), cfmat_sample[0].get_dampings()[0].get_coeffs()(0, 0)); // printout the second damping - printf("\n Damping at day %.2f\n\t", double(cfmat_sample[0].get_dampings()[1].get_time())); + printf("\n Damping at day %.2f\n\t", ScalarType(cfmat_sample[0].get_dampings()[1].get_time())); std::cout << cfmat_sample[0].get_dampings()[1].get_coeffs() << std::endl; } diff --git a/cpp/examples/ode_secir_parameter_study.cpp b/cpp/examples/ode_secir_parameter_study.cpp index c55549e151..8ad8c1cd2b 100644 --- a/cpp/examples/ode_secir_parameter_study.cpp +++ b/cpp/examples/ode_secir_parameter_study.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -30,9 +30,10 @@ * @param t0 starting point of simulation * @param tmax end point of simulation */ -mio::IOResult write_single_run_result( - const size_t run, - const mio::Graph>, mio::MobilityEdge>& graph) +mio::IOResult +write_single_run_result(const size_t run, + const mio::Graph>, + mio::MobilityEdge>& graph) { std::string abs_path; BOOST_OUTCOME_TRY(auto&& created, mio::create_directory("results", abs_path)); @@ -58,7 +59,7 @@ mio::IOResult write_single_run_result( } //write results for this run - std::vector> all_results; + std::vector> all_results; std::vector ids; ids.reserve(graph.nodes().size()); @@ -80,30 +81,30 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0; - double tmax = 50; + ScalarType t0 = 0; + ScalarType tmax = 50; - double cont_freq = 10; // see Polymod study + ScalarType cont_freq = 10; // see Polymod study - double num_total_t0 = 10000, num_exp_t0 = 100, num_inf_t0 = 50, num_car_t0 = 50, num_hosp_t0 = 20, num_icu_t0 = 10, - num_rec_t0 = 10, num_dead_t0 = 0; + ScalarType num_total_t0 = 10000, num_exp_t0 = 100, num_inf_t0 = 50, num_car_t0 = 50, num_hosp_t0 = 20, + num_icu_t0 = 10, num_rec_t0 = 10, num_dead_t0 = 0; - mio::osecir::Model model(1); + mio::osecir::Model model(1); mio::AgeGroup num_groups = model.parameters.get_num_groups(); - double fact = 1.0 / (double)(size_t)num_groups; + ScalarType fact = 1.0 / (ScalarType)(size_t)num_groups; auto& params = model.parameters; - params.set>(std::numeric_limits::max()); - params.set(0); - params.set>(0); + params.set>(std::numeric_limits::max()); + params.set>(0); + params.set>(0); for (auto i = mio::AgeGroup(0); i < num_groups; i++) { - params.get>()[i] = 3.2; - params.get>()[i] = 2.; - params.get>()[i] = 6.; - params.get>()[i] = 12; - params.get>()[i] = 8; + params.get>()[i] = 3.2; + params.get>()[i] = 2.; + params.get>()[i] = 6.; + params.get>()[i] = 12; + params.get>()[i] = 8; model.populations[{i, mio::osecir::InfectionState::Exposed}] = fact * num_exp_t0; model.populations[{i, mio::osecir::InfectionState::InfectedNoSymptoms}] = fact * num_car_t0; @@ -115,22 +116,22 @@ int main() model.populations.set_difference_from_group_total({i, mio::osecir::InfectionState::Susceptible}, fact * num_total_t0); - params.get>()[i] = 0.05; - params.get>()[i] = 0.67; - params.get>()[i] = 0.09; - params.get>()[i] = 0.25; - params.get>()[i] = 0.2; - params.get>()[i] = 0.25; - params.get>()[i] = 0.3; + params.get>()[i] = 0.05; + params.get>()[i] = 0.67; + params.get>()[i] = 0.09; + params.get>()[i] = 0.25; + params.get>()[i] = 0.2; + params.get>()[i] = 0.25; + params.get>()[i] = 0.3; } params.apply_constraints(); - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); - mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); + mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); auto write_parameters_status = mio::write_json("parameters.json", model); if (!write_parameters_status) { @@ -140,7 +141,7 @@ int main() //create study auto num_runs = size_t(1); - mio::ParameterStudy> parameter_study(model, t0, tmax, num_runs); + mio::ParameterStudy> parameter_study(model, t0, tmax, num_runs); //run study auto sample_graph = [](auto&& graph) { diff --git a/cpp/examples/ode_secir_parameter_study_graph.cpp b/cpp/examples/ode_secir_parameter_study_graph.cpp index a75b198afd..b2e8260cc3 100644 --- a/cpp/examples/ode_secir_parameter_study_graph.cpp +++ b/cpp/examples/ode_secir_parameter_study_graph.cpp @@ -44,9 +44,9 @@ * @param min minimum of distribution. * @param max minimum of distribution. */ -void assign_uniform_distribution(mio::UncertainValue& p, double min, double max) +void assign_uniform_distribution(mio::UncertainValue& p, ScalarType min, ScalarType max) { - p = mio::UncertainValue(0.5 * (max + min)); + p = mio::UncertainValue(0.5 * (max + min)); p.set_distribution(mio::ParameterDistributionUniform(min, max)); } @@ -59,8 +59,8 @@ void assign_uniform_distribution(mio::UncertainValue& p, double min, dou * @param max minimum of distribution for each element of array. */ template -void array_assign_uniform_distribution(mio::CustomIndexArray, mio::AgeGroup>& array, - const double (&min)[N], const double (&max)[N]) +void array_assign_uniform_distribution(mio::CustomIndexArray, mio::AgeGroup>& array, + const ScalarType (&min)[N], const ScalarType (&max)[N]) { assert(N == array.numel()); for (auto i = mio::AgeGroup(0); i < mio::AgeGroup(N); ++i) { @@ -75,8 +75,8 @@ void array_assign_uniform_distribution(mio::CustomIndexArray, mio::AgeGroup>& array, - double min, double max) +void array_assign_uniform_distribution(mio::CustomIndexArray, mio::AgeGroup>& array, + ScalarType min, ScalarType max) { for (auto i = mio::AgeGroup(0); i < array.size(); ++i) { assign_uniform_distribution(array[i], min, max); @@ -89,75 +89,76 @@ void array_assign_uniform_distribution(mio::CustomIndexArray& params) +void set_covid_parameters(mio::osecir::Parameters& params) { //times - const double timeExposedMin = 2.67; - const double timeExposedMax = 4.; - const double timeInfectedNoSymptomsMin = 1.2; - const double timeInfectedNoSymptomsMax = 2.53; - - const double timeInfectedSymptomsMin[] = {5.6255, 5.6255, 5.6646, 5.5631, 5.501, 5.465}; - const double timeInfectedSymptomsMax[] = {8.427, 8.427, 8.4684, 8.3139, 8.169, 8.085}; - const double timeInfectedSevereMin[] = {3.925, 3.925, 4.85, 6.4, 7.2, 9.}; - const double timeInfectedSevereMax[] = {6.075, 6.075, 7., 8.7, 9.8, 13.}; - const double timeInfectedCriticalMin[] = {4.95, 4.95, 4.86, 14.14, 14.4, 10.}; - const double timeInfectedCriticalMax[] = {8.95, 8.95, 8.86, 20.58, 19.8, 13.2}; - - array_assign_uniform_distribution(params.get>(), timeExposedMin, timeExposedMax); - array_assign_uniform_distribution(params.get>(), + const ScalarType timeExposedMin = 2.67; + const ScalarType timeExposedMax = 4.; + const ScalarType timeInfectedNoSymptomsMin = 1.2; + const ScalarType timeInfectedNoSymptomsMax = 2.53; + + const ScalarType timeInfectedSymptomsMin[] = {5.6255, 5.6255, 5.6646, 5.5631, 5.501, 5.465}; + const ScalarType timeInfectedSymptomsMax[] = {8.427, 8.427, 8.4684, 8.3139, 8.169, 8.085}; + const ScalarType timeInfectedSevereMin[] = {3.925, 3.925, 4.85, 6.4, 7.2, 9.}; + const ScalarType timeInfectedSevereMax[] = {6.075, 6.075, 7., 8.7, 9.8, 13.}; + const ScalarType timeInfectedCriticalMin[] = {4.95, 4.95, 4.86, 14.14, 14.4, 10.}; + const ScalarType timeInfectedCriticalMax[] = {8.95, 8.95, 8.86, 20.58, 19.8, 13.2}; + + array_assign_uniform_distribution(params.get>(), timeExposedMin, + timeExposedMax); + array_assign_uniform_distribution(params.get>(), timeInfectedNoSymptomsMin, timeInfectedNoSymptomsMax); - array_assign_uniform_distribution(params.get>(), timeInfectedSymptomsMin, - timeInfectedSymptomsMax); - array_assign_uniform_distribution(params.get>(), timeInfectedSevereMin, + array_assign_uniform_distribution(params.get>(), + timeInfectedSymptomsMin, timeInfectedSymptomsMax); + array_assign_uniform_distribution(params.get>(), timeInfectedSevereMin, timeInfectedSevereMax); - array_assign_uniform_distribution(params.get>(), timeInfectedCriticalMin, - timeInfectedCriticalMax); + array_assign_uniform_distribution(params.get>(), + timeInfectedCriticalMin, timeInfectedCriticalMax); //probabilities - const double transmissionProbabilityOnContactMin[] = {0.02, 0.05, 0.05, 0.05, 0.08, 0.15}; - const double transmissionProbabilityOnContactMax[] = {0.04, 0.07, 0.07, 0.07, 0.10, 0.20}; - const double relativeTransmissionNoSymptomsMin = 1; - const double relativeTransmissionNoSymptomsMax = 1; + const ScalarType transmissionProbabilityOnContactMin[] = {0.02, 0.05, 0.05, 0.05, 0.08, 0.15}; + const ScalarType transmissionProbabilityOnContactMax[] = {0.04, 0.07, 0.07, 0.07, 0.10, 0.20}; + const ScalarType relativeTransmissionNoSymptomsMin = 1; + const ScalarType relativeTransmissionNoSymptomsMax = 1; // The precise value between Risk* (situation under control) and MaxRisk* (situation not under control) // depends on incidence and test and trace capacity - const double riskOfInfectionFromSymptomaticMin = 0.1; - const double riskOfInfectionFromSymptomaticMax = 0.3; - const double maxRiskOfInfectionFromSymptomaticMin = 0.3; - const double maxRiskOfInfectionFromSymptomaticMax = 0.5; - const double recoveredPerInfectedNoSymptomsMin[] = {0.2, 0.2, 0.15, 0.15, 0.15, 0.15}; - const double recoveredPerInfectedNoSymptomsMax[] = {0.3, 0.3, 0.25, 0.25, 0.25, 0.25}; - const double severePerInfectedSymptomsMin[] = {0.006, 0.006, 0.015, 0.049, 0.15, 0.20}; - const double severePerInfectedSymptomsMax[] = {0.009, 0.009, 0.023, 0.074, 0.18, 0.25}; - const double criticalPerSevereMin[] = {0.05, 0.05, 0.05, 0.10, 0.25, 0.35}; - const double criticalPerSevereMax[] = {0.10, 0.10, 0.10, 0.20, 0.35, 0.45}; - const double deathsPerCriticalMin[] = {0.00, 0.00, 0.10, 0.10, 0.30, 0.5}; - const double deathsPerCriticalMax[] = {0.10, 0.10, 0.18, 0.18, 0.50, 0.7}; - - array_assign_uniform_distribution(params.get>(), + const ScalarType riskOfInfectionFromSymptomaticMin = 0.1; + const ScalarType riskOfInfectionFromSymptomaticMax = 0.3; + const ScalarType maxRiskOfInfectionFromSymptomaticMin = 0.3; + const ScalarType maxRiskOfInfectionFromSymptomaticMax = 0.5; + const ScalarType recoveredPerInfectedNoSymptomsMin[] = {0.2, 0.2, 0.15, 0.15, 0.15, 0.15}; + const ScalarType recoveredPerInfectedNoSymptomsMax[] = {0.3, 0.3, 0.25, 0.25, 0.25, 0.25}; + const ScalarType severePerInfectedSymptomsMin[] = {0.006, 0.006, 0.015, 0.049, 0.15, 0.20}; + const ScalarType severePerInfectedSymptomsMax[] = {0.009, 0.009, 0.023, 0.074, 0.18, 0.25}; + const ScalarType criticalPerSevereMin[] = {0.05, 0.05, 0.05, 0.10, 0.25, 0.35}; + const ScalarType criticalPerSevereMax[] = {0.10, 0.10, 0.10, 0.20, 0.35, 0.45}; + const ScalarType deathsPerCriticalMin[] = {0.00, 0.00, 0.10, 0.10, 0.30, 0.5}; + const ScalarType deathsPerCriticalMax[] = {0.10, 0.10, 0.18, 0.18, 0.50, 0.7}; + + array_assign_uniform_distribution(params.get>(), transmissionProbabilityOnContactMin, transmissionProbabilityOnContactMax); - array_assign_uniform_distribution(params.get>(), + array_assign_uniform_distribution(params.get>(), relativeTransmissionNoSymptomsMin, relativeTransmissionNoSymptomsMax); - array_assign_uniform_distribution(params.get>(), + array_assign_uniform_distribution(params.get>(), riskOfInfectionFromSymptomaticMin, riskOfInfectionFromSymptomaticMax); - array_assign_uniform_distribution(params.get>(), + array_assign_uniform_distribution(params.get>(), maxRiskOfInfectionFromSymptomaticMin, maxRiskOfInfectionFromSymptomaticMax); - array_assign_uniform_distribution(params.get>(), + array_assign_uniform_distribution(params.get>(), recoveredPerInfectedNoSymptomsMin, recoveredPerInfectedNoSymptomsMax); - array_assign_uniform_distribution(params.get>(), + array_assign_uniform_distribution(params.get>(), severePerInfectedSymptomsMin, severePerInfectedSymptomsMax); - array_assign_uniform_distribution(params.get>(), criticalPerSevereMin, + array_assign_uniform_distribution(params.get>(), criticalPerSevereMin, criticalPerSevereMax); - array_assign_uniform_distribution(params.get>(), deathsPerCriticalMin, + array_assign_uniform_distribution(params.get>(), deathsPerCriticalMin, deathsPerCriticalMax); //sasonality - const double seasonality_min = 0.1; - const double seasonality_max = 0.3; + const ScalarType seasonality_min = 0.1; + const ScalarType seasonality_max = 0.3; - assign_uniform_distribution(params.get>(), seasonality_min, seasonality_max); + assign_uniform_distribution(params.get>(), seasonality_min, seasonality_max); - params.set(0); + params.set>(0); } /** @@ -165,10 +166,10 @@ void set_covid_parameters(mio::osecir::Parameters& params) * Same total populaton but different spread of infection in each county. * @param counties parameters for each county. */ -void set_synthetic_population_data(mio::osecir::Model& model) +void set_synthetic_population_data(mio::osecir::Model& model) { - double nb_total_t0 = 10000, nb_exp_t0 = 2, nb_inf_t0 = 0, nb_car_t0 = 0, nb_hosp_t0 = 0, nb_icu_t0 = 0, - nb_rec_t0 = 0, nb_dead_t0 = 0; + ScalarType nb_total_t0 = 10000, nb_exp_t0 = 2, nb_inf_t0 = 0, nb_car_t0 = 0, nb_hosp_t0 = 0, nb_icu_t0 = 0, + nb_rec_t0 = 0, nb_dead_t0 = 0; for (mio::AgeGroup i = 0; i < model.parameters.get_num_groups(); i++) { model.populations[{i, mio::osecir::InfectionState::Exposed}] = nb_exp_t0; @@ -185,7 +186,7 @@ void set_synthetic_population_data(mio::osecir::Model& model) } } -std::vector> get_indices_of_symptomatic_and_nonsymptomatic(mio::osecir::Model& model) +std::vector> get_indices_of_symptomatic_and_nonsymptomatic(mio::osecir::Model& model) { std::vector> indices_save_edges(2); const auto num_groups = static_cast(model.parameters.get_num_groups()); @@ -212,7 +213,7 @@ std::vector> get_indices_of_symptomatic_and_nonsymptomatic(m /** * Run the parameter study. - * Load a previously stored graph or create a new one from data. + * Load a previously stored graph or create a new one from data. * The graph is the input for the parameter study. * A newly created graph is saved and can be reused. * @param mode Mode for running the parameter study. @@ -228,19 +229,20 @@ int main() const auto num_days_sim = 30.0; const auto num_runs = 10; - mio::Graph, mio::MobilityParameters> params_graph; + mio::Graph, mio::MobilityParameters> params_graph; const int num_age_groups = 6; - mio::osecir::Model model(num_age_groups); + mio::osecir::Model model(num_age_groups); // set parameters set_covid_parameters(model.parameters); // set contact matrix - const auto cont_freq = 10.0; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix( - Eigen::MatrixXd::Constant((size_t)num_age_groups, (size_t)num_age_groups, (1. / num_age_groups) * cont_freq)); + const auto cont_freq = 10.0; + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant( + (size_t)num_age_groups, (size_t)num_age_groups, (1. / num_age_groups) * cont_freq)); // set population data set_synthetic_population_data(model); @@ -250,22 +252,22 @@ int main() params_graph.add_node(1002, model); params_graph.add_node(1003, model); - params_graph.add_edge(0, 1, - Eigen::VectorXd::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.05), - indices_save_edges); - params_graph.add_edge(1, 0, - Eigen::VectorXd::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.1), - indices_save_edges); - params_graph.add_edge(1, 2, - Eigen::VectorXd::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.15), - indices_save_edges); - params_graph.add_edge(2, 1, - Eigen::VectorXd::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.2), - indices_save_edges); + params_graph.add_edge( + 0, 1, Eigen::VectorX::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.05), + indices_save_edges); + params_graph.add_edge( + 1, 0, Eigen::VectorX::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.1), + indices_save_edges); + params_graph.add_edge( + 1, 2, Eigen::VectorX::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.15), + indices_save_edges); + params_graph.add_edge( + 2, 1, Eigen::VectorX::Constant(num_age_groups * (size_t)mio::osecir::InfectionState::Count, 0.2), + indices_save_edges); //run parameter study - auto parameter_study = - mio::ParameterStudy>{params_graph, 0.0, num_days_sim, 0.5, size_t(num_runs)}; + auto parameter_study = mio::ParameterStudy>{ + params_graph, 0.0, num_days_sim, 0.5, size_t(num_runs)}; if (mio::mpi::is_root()) { printf("Seeds: "); @@ -283,17 +285,17 @@ int main() [&](auto results_graph, auto&& run_id) { auto interpolated_result = mio::interpolate_simulation_result(results_graph); - auto params = std::vector>{}; + auto params = std::vector>{}; params.reserve(results_graph.nodes().size()); std::transform(results_graph.nodes().begin(), results_graph.nodes().end(), std::back_inserter(params), - [](auto&& node) { + [](auto&& node) { return node.property.get_simulation().get_model(); }); auto edges = std::vector>{}; edges.reserve(results_graph.edges().size()); std::transform(results_graph.edges().begin(), results_graph.edges().end(), std::back_inserter(edges), - [](auto&& edge) { + [](auto&& edge) { return edge.property.get_mobility_results(); }); @@ -302,11 +304,11 @@ int main() }); if (ensemble.size() > 0) { - auto ensemble_results = std::vector>>{}; + auto ensemble_results = std::vector>>{}; ensemble_results.reserve(ensemble.size()); - auto ensemble_params = std::vector>>{}; + auto ensemble_params = std::vector>>{}; ensemble_params.reserve(ensemble.size()); - auto ensemble_edges = std::vector>>{}; + auto ensemble_edges = std::vector>>{}; ensemble_edges.reserve(ensemble.size()); for (auto&& run : ensemble) { ensemble_results.emplace_back(std::move(std::get<0>(run))); diff --git a/cpp/examples/ode_secir_read_graph.cpp b/cpp/examples/ode_secir_read_graph.cpp index 4e086bdffc..8832aec976 100644 --- a/cpp/examples/ode_secir_read_graph.cpp +++ b/cpp/examples/ode_secir_read_graph.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -57,29 +57,28 @@ int main(int argc, char** argv) const auto t0 = 0.; const auto tmax = 10.; - using FP = double; - double cont_freq = 10; // see Polymod study + ScalarType cont_freq = 10; // see Polymod study - double nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, - nb_rec_t0 = 10, nb_dead_t0 = 0; + ScalarType nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, + nb_rec_t0 = 10, nb_dead_t0 = 0; - mio::osecir::Model model(1); + mio::osecir::Model model(1); mio::AgeGroup nb_groups = model.parameters.get_num_groups(); - double fact = 1.0 / (double)(size_t)nb_groups; + ScalarType fact = 1.0 / (ScalarType)(size_t)nb_groups; auto& params = model.parameters; - params.set>(std::numeric_limits::max()); - params.set(0); - params.set>(0); + params.set>(std::numeric_limits::max()); + params.set>(0); + params.set>(0); for (auto i = mio::AgeGroup(0); i < nb_groups; i++) { - params.get>()[i] = 3.2; - params.get>()[i] = 2.0; - params.get>()[i] = 6.; - params.get>()[i] = 12; - params.get>()[i] = 8; + params.get>()[i] = 3.2; + params.get>()[i] = 2.0; + params.get>()[i] = 6.; + params.get>()[i] = 12; + params.get>()[i] = 8; model.populations[{i, mio::osecir::InfectionState::Exposed}] = fact * nb_exp_t0; model.populations[{i, mio::osecir::InfectionState::InfectedNoSymptoms}] = fact * nb_car_t0; @@ -93,20 +92,20 @@ int main(int argc, char** argv) model.populations.set_difference_from_group_total({i, mio::osecir::InfectionState::Susceptible}, fact * nb_total_t0); - params.get>()[i] = 0.05; - params.get>()[i] = 0.67; - params.get>()[i] = 0.09; - params.get>()[i] = 0.25; - params.get>()[i] = 0.2; - params.get>()[i] = 0.25; - params.get>()[i] = 0.3; + params.get>()[i] = 0.05; + params.get>()[i] = 0.67; + params.get>()[i] = 0.09; + params.get>()[i] = 0.25; + params.get>()[i] = 0.2; + params.get>()[i] = 0.25; + params.get>()[i] = 0.3; } params.apply_constraints(); - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); @@ -121,16 +120,16 @@ int main(int argc, char** argv) std::cout << "Done" << std::endl; std::cout << "Intializing Graph..." << std::flush; - mio::Graph, mio::MobilityParameters> graph; + mio::Graph, mio::MobilityParameters> graph; for (int node = 0; node < commuter_mobility.rows(); node++) { graph.add_node(node, model); } for (int row = 0; row < commuter_mobility.rows(); row++) { for (int col = 0; col < commuter_mobility.cols(); col++) { graph.add_edge(row, col, - Eigen::VectorXd::Constant(10 * (size_t)nb_groups, - commuter_mobility(row, col) / - graph.nodes()[row].property.populations.get_total())); + Eigen::VectorX::Constant( + 10 * (size_t)nb_groups, + commuter_mobility(row, col) / graph.nodes()[row].property.populations.get_total())); } } std::cout << "Done" << std::endl; @@ -143,7 +142,7 @@ int main(int argc, char** argv) std::cout << "Done" << std::endl; std::cout << "Reading Json Files..." << std::flush; - auto graph_read_result = mio::read_graph>("graph_parameters"); + auto graph_read_result = mio::read_graph>("graph_parameters"); if (!graph_read_result) { std::cout << "Error: " << graph_read_result.error().formatted_message(); @@ -152,7 +151,7 @@ int main(int argc, char** argv) auto& graph_read = graph_read_result.value(); std::cout << "Running Simulations..." << std::flush; - auto study = mio::ParameterStudy>(graph_read, t0, tmax, 0.5, 2); + auto study = mio::ParameterStudy>(graph_read, t0, tmax, 0.5, 2); std::cout << "Done" << std::endl; return 0; diff --git a/cpp/examples/ode_secir_save_results.cpp b/cpp/examples/ode_secir_save_results.cpp index 7d952f8924..5c631fd949 100644 --- a/cpp/examples/ode_secir_save_results.cpp +++ b/cpp/examples/ode_secir_save_results.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Wadim Koslow @@ -29,27 +29,27 @@ int main() const auto tmax = 10.; const auto dt = 1.; //time step of mobility, not integration - double cont_freq = 10; // see Polymod study + ScalarType cont_freq = 10; // see Polymod study - double nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, - nb_rec_t0 = 10, nb_dead_t0 = 0; + ScalarType nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, + nb_rec_t0 = 10, nb_dead_t0 = 0; - mio::osecir::Model model(1); + mio::osecir::Model model(1); mio::AgeGroup nb_groups = model.parameters.get_num_groups(); - double fact = 1.0 / (double)(size_t)nb_groups; + ScalarType fact = 1.0 / (ScalarType)(size_t)nb_groups; auto& params = model.parameters; - params.set>(std::numeric_limits::max()); - params.set(0); - params.set>(0); + params.set>(std::numeric_limits::max()); + params.set>(0); + params.set>(0); for (auto i = mio::AgeGroup(0); i < nb_groups; i++) { - params.get>()[i] = 3.2; - params.get>()[i] = 2.0; - params.get>()[i] = 6.; - params.get>()[i] = 12; - params.get>()[i] = 8; + params.get>()[i] = 3.2; + params.get>()[i] = 2.0; + params.get>()[i] = 6.; + params.get>()[i] = 12; + params.get>()[i] = 8; model.populations[{i, mio::osecir::InfectionState::Exposed}] = fact * nb_exp_t0; model.populations[{i, mio::osecir::InfectionState::InfectedNoSymptoms}] = fact * nb_car_t0; @@ -63,25 +63,25 @@ int main() model.populations.set_difference_from_group_total({i, mio::osecir::InfectionState::Susceptible}, fact * nb_total_t0); - params.get>()[i] = 0.05; - params.get>()[i] = 0.67; - params.get>()[i] = 0.09; - params.get>()[i] = 0.25; - params.get>()[i] = 0.2; - params.get>()[i] = 0.25; - params.get>()[i] = 0.3; + params.get>()[i] = 0.05; + params.get>()[i] = 0.67; + params.get>()[i] = 0.09; + params.get>()[i] = 0.25; + params.get>()[i] = 0.2; + params.get>()[i] = 0.25; + params.get>()[i] = 0.3; } params.apply_constraints(); - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - contact_matrix.add_damping(0.3, mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + contact_matrix.add_damping(0.3, mio::SimulationTime(30.)); - auto result_from_sim = mio::osecir::simulate(t0, tmax, dt, model); - std::vector> results_from_sim = {result_from_sim, result_from_sim}; - std::vector ids = {1, 2}; + auto result_from_sim = mio::osecir::simulate(t0, tmax, dt, model); + std::vector> results_from_sim = {result_from_sim, result_from_sim}; + std::vector ids = {1, 2}; auto save_result_status = mio::save_result(results_from_sim, ids, (int)(size_t)nb_groups, "test_result.h5"); } diff --git a/cpp/examples/ode_secirts.cpp b/cpp/examples/ode_secirts.cpp index 707689a7ed..e0be27a87e 100644 --- a/cpp/examples/ode_secirts.cpp +++ b/cpp/examples/ode_secirts.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker @@ -22,6 +22,7 @@ #include "ode_secirts/parameters.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" +#include "memilio/data/analyze_result.h" int main() { @@ -30,13 +31,13 @@ int main() // After the simulation, the aggregated size of the temporary immunity states are printed. mio::set_log_level(mio::LogLevel::warn); - double t0 = 0; - double tmax = 30; - double dt = 0.1; + ScalarType t0 = 0; + ScalarType tmax = 30; + ScalarType dt = 0.1; mio::log_info("Simulating SECIRTS; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::osecirts::Model model(3); + mio::osecirts::Model model(3); auto nb_groups = model.parameters.get_num_groups(); for (mio::AgeGroup i = 0; i < nb_groups; i++) { @@ -73,75 +74,76 @@ int main() // parameters //times - model.parameters.get>()[i] = 3.33; - model.parameters.get>()[i] = 1.87; - model.parameters.get>()[i] = 7; - model.parameters.get>()[i] = 6; - model.parameters.get>()[i] = 7; - model.parameters.get>()[i] = 60; - model.parameters.get>()[i] = 60; - model.parameters.get>()[i] = 180; - model.parameters.get>()[i] = 180; + model.parameters.get>()[i] = 3.33; + model.parameters.get>()[i] = 1.87; + model.parameters.get>()[i] = 7; + model.parameters.get>()[i] = 6; + model.parameters.get>()[i] = 7; + model.parameters.get>()[i] = 60; + model.parameters.get>()[i] = 60; + model.parameters.get>()[i] = 180; + model.parameters.get>()[i] = 180; //probabilities - model.parameters.get>()[i] = 0.15; - model.parameters.get>()[i] = 0.5; - model.parameters.get>()[i] = 0.0; - model.parameters.get>()[i] = 0.4; - model.parameters.get>()[i] = 0.2; - model.parameters.get>()[i] = 0.1; - model.parameters.get>()[i] = 0.1; - model.parameters.get>()[i] = 0.1; + model.parameters.get>()[i] = 0.15; + model.parameters.get>()[i] = 0.5; + model.parameters.get>()[i] = 0.0; + model.parameters.get>()[i] = 0.4; + model.parameters.get>()[i] = 0.2; + model.parameters.get>()[i] = 0.1; + model.parameters.get>()[i] = 0.1; + model.parameters.get>()[i] = 0.1; - model.parameters.get>()[i] = 0.8; - model.parameters.get>()[i] = 0.331; - model.parameters.get>()[i] = 0.65; - model.parameters.get>()[i] = 0.243; - model.parameters.get>()[i] = 0.1; - model.parameters.get>()[i] = 0.091; - model.parameters.get>()[i] = 0.9; + model.parameters.get>()[i] = 0.8; + model.parameters.get>()[i] = 0.331; + model.parameters.get>()[i] = 0.65; + model.parameters.get>()[i] = 0.243; + model.parameters.get>()[i] = 0.1; + model.parameters.get>()[i] = 0.091; + model.parameters.get>()[i] = 0.9; } - model.parameters.get>() = 100; - model.parameters.get>() = 0.0143; - const size_t daily_vaccinations = 10; - const size_t num_days = 300; - model.parameters.get>().resize(mio::SimulationDay(num_days)); - model.parameters.get>().resize(mio::SimulationDay(num_days)); - model.parameters.get>().resize(mio::SimulationDay(num_days)); + model.parameters.get>() = 100; + model.parameters.get>() = 0.0143; + const size_t daily_vaccinations = 10; + const size_t num_days = 300; + model.parameters.get>().resize(mio::SimulationDay(num_days)); + model.parameters.get>().resize(mio::SimulationDay(num_days)); + model.parameters.get>().resize(mio::SimulationDay(num_days)); for (size_t i = 0; i < num_days; ++i) { for (mio::AgeGroup j = 0; j < nb_groups; ++j) { - auto num_vaccinations = static_cast(i * daily_vaccinations); - model.parameters.get>()[{j, mio::SimulationDay(i)}] = + auto num_vaccinations = static_cast(i * daily_vaccinations); + model.parameters.get>()[{j, mio::SimulationDay(i)}] = num_vaccinations; - model.parameters.get>()[{j, mio::SimulationDay(i)}] = + model.parameters.get>()[{j, mio::SimulationDay(i)}] = num_vaccinations; - model.parameters.get>()[{j, mio::SimulationDay(i)}] = + model.parameters.get>()[{j, mio::SimulationDay(i)}] = num_vaccinations; } } - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - const double cont_freq = 10; - const double fact = 1.0 / (double)(size_t)nb_groups; - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), - mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + const ScalarType cont_freq = 10; + const ScalarType fact = 1.0 / (ScalarType)(size_t)nb_groups; + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + contact_matrix.add_damping(Eigen::MatrixX::Constant((size_t)nb_groups, (size_t)nb_groups, 0.7), + mio::SimulationTime(30.)); - model.parameters.get>() = 0.2; + model.parameters.get>() = 0.2; model.apply_constraints(); - mio::TimeSeries result = simulate(t0, tmax, dt, model); + mio::TimeSeries result = mio::osecirts::simulate(t0, tmax, dt, model); bool print_to_terminal = true; if (print_to_terminal) { auto result_interpolated = mio::interpolate_simulation_result(result); for (auto t_indx = 0; t_indx < result_interpolated.get_num_time_points(); t_indx++) { - double timm_pi = 0.0; - double timm_ii = 0.0; + ScalarType timm_pi = 0.0; + ScalarType timm_ii = 0.0; for (mio::AgeGroup i = 0; i < nb_groups; i++) { timm_pi += result_interpolated.get_value(t_indx)[model.populations.get_flat_index( {i, mio::osecirts::InfectionState::TemporaryImmunePartialImmunity})]; diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index 3abf9750de..924a1477ac 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Martin J. Kuehn @@ -85,7 +85,7 @@ int main() auto& contact_matrix = contacts.get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(0.5); contact_matrix[0].get_baseline().diagonal().setConstant(5.0); - contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); + contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); //times model.parameters.get>()[mio::AgeGroup(0)] = 3.33; diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 1fe1ae86fd..349fef0a3f 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -51,13 +51,14 @@ int main() model.parameters.set>(6); model.parameters.set>(0.1); - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); contact_matrix[0].get_baseline().setConstant(2.7); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); model.check_constraints(); - auto seir = simulate(t0, tmax, dt, model); + auto seir = mio::simulate(t0, tmax, dt, model); seir.print_table({"S", "E", "I", "R"}); std::cout << "\nnumber total: " << seir.get_last_value().sum() << "\n"; diff --git a/cpp/examples/ode_seir_ageres.cpp b/cpp/examples/ode_seir_ageres.cpp index 4977fa742a..2e9a6c6757 100644 --- a/cpp/examples/ode_seir_ageres.cpp +++ b/cpp/examples/ode_seir_ageres.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -27,19 +27,19 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0; - double tmax = 50; - double dt = 0.001; + ScalarType t0 = 0; + ScalarType tmax = 50; + ScalarType dt = 0.001; mio::log_info("Simulating SEIR; t={} ... {} with dt = {}.", t0, tmax, dt); - double cont_freq = 10; + ScalarType cont_freq = 10; - double nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_rec_t0 = 10; + ScalarType nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_rec_t0 = 10; const size_t num_groups = 3; - mio::oseir::Model model(num_groups); - double fact = 1.0 / num_groups; + mio::oseir::Model model(num_groups); + ScalarType fact = 1.0 / num_groups; auto& params = model.parameters; @@ -50,25 +50,27 @@ int main() model.populations.set_difference_from_group_total({i, mio::oseir::InfectionState::Susceptible}, fact * nb_total_t0); - model.parameters.get>()[i] = 5.2; - model.parameters.get>()[i] = 6; - model.parameters.get>()[i] = 0.04; + model.parameters.get>()[i] = 5.2; + model.parameters.get>()[i] = 6; + model.parameters.get>()[i] = 0.04; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant(num_groups, num_groups, 0.7), mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_groups, num_groups, fact * cont_freq)); + contact_matrix.add_damping(Eigen::MatrixX::Constant(num_groups, num_groups, 0.7), + mio::SimulationTime(30.)); model.apply_constraints(); - auto seir = simulate(t0, tmax, dt, model); + auto seir = mio::simulate(t0, tmax, dt, model); std::vector vars = {"S", "E", "I", "R"}; printf("Number of time points :%d\n", static_cast(seir.get_num_time_points())); printf("People in\n"); for (size_t k = 0; k < (size_t)mio::oseir::InfectionState::Count; k++) { - double dummy = 0; + ScalarType dummy = 0; for (size_t i = 0; i < (size_t)params.get_num_groups(); i++) { printf("\t %s[%d]: %.0f", vars[k].c_str(), (int)i, diff --git a/cpp/examples/ode_seir_flows.cpp b/cpp/examples/ode_seir_flows.cpp index c490822203..9bbb6382ac 100644 --- a/cpp/examples/ode_seir_flows.cpp +++ b/cpp/examples/ode_seir_flows.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn, Rene Schmieding, Henrik Zunker @@ -30,32 +30,32 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0; - double tmax = 1; - double dt = 0.001; + ScalarType t0 = 0; + ScalarType tmax = 1; + ScalarType dt = 0.001; mio::log_info("Simulating SEIR; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::oseir::Model model(1); + mio::oseir::Model model(1); - constexpr double total_population = 10000; + constexpr ScalarType total_population = 10000; model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] = 100; model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Infected}] = 100; model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Recovered}] = 100; model.populations.set_difference_from_group_total( {mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}, total_population); - model.parameters.set>(5.2); - model.parameters.set>(6); - model.parameters.set>(0.04); + model.parameters.set>(5.2); + model.parameters.set>(6); + model.parameters.set>(0.04); - mio::ContactMatrixGroup& contact_matrix = - model.parameters.get>().get_cont_freq_mat(); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(10); model.check_constraints(); - auto seir = simulate_flows(t0, tmax, dt, model); + auto seir = mio::simulate_flows(t0, tmax, dt, model); printf("Compartments: \n"); seir[0].print_table({"S", "E", "I", "R"}); diff --git a/cpp/examples/ode_sir.cpp b/cpp/examples/ode_sir.cpp index 701899a6a4..fbd7adcd79 100644 --- a/cpp/examples/ode_sir.cpp +++ b/cpp/examples/ode_sir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn @@ -50,18 +50,18 @@ int main() model.parameters.set>(2); model.parameters.set>(0.5); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(2.7); - contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); + contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); model.check_constraints(); std::unique_ptr> integrator = std::make_unique>(); - auto sir = simulate(t0, tmax, dt, model, std::move(integrator)); + auto sir = mio::simulate(t0, tmax, dt, model, std::move(integrator)); // interpolate results - auto interpolated_results = mio::interpolate_simulation_result(sir); + auto interpolated_results = mio::interpolate_simulation_result(sir); interpolated_results.print_table({"S", "I", "R"}); std::cout << "\nPopulation total: " << sir.get_last_value().sum() << "\n"; diff --git a/cpp/examples/ode_sir_ageres.cpp b/cpp/examples/ode_sir_ageres.cpp index 120a9ae323..8805e33eb5 100644 --- a/cpp/examples/ode_sir_ageres.cpp +++ b/cpp/examples/ode_sir_ageres.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -27,20 +27,20 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0; - double tmax = 50; - double dt = 0.1; + ScalarType t0 = 0; + ScalarType tmax = 50; + ScalarType dt = 0.1; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - double cont_freq = 10; // see Polymod study + ScalarType cont_freq = 10; // see Polymod study - double nb_total_t0 = 10000, nb_inf_t0 = 50, nb_rec_t0 = 10; + ScalarType nb_total_t0 = 10000, nb_inf_t0 = 50, nb_rec_t0 = 10; const size_t num_groups = 3; - mio::osir::Model model(num_groups); + mio::osir::Model model(num_groups); - double fact = 1.0 / num_groups; + ScalarType fact = 1.0 / num_groups; auto& params = model.parameters; @@ -50,24 +50,26 @@ int main() model.populations.set_difference_from_group_total({i, mio::osir::InfectionState::Susceptible}, fact * nb_total_t0); - model.parameters.get>()[i] = 2.0; - model.parameters.get>()[i] = 0.3; + model.parameters.get>()[i] = 2.0; + model.parameters.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); - contact_matrix.add_damping(Eigen::MatrixXd::Constant(num_groups, num_groups, 0.7), mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); + contact_matrix.add_damping(Eigen::MatrixXd::Constant(num_groups, num_groups, 0.7), + mio::SimulationTime(30.)); model.apply_constraints(); - auto sir = simulate(t0, tmax, dt, model); + auto sir = mio::simulate(t0, tmax, dt, model); std::vector vars = {"S", "I", "R"}; printf("Number of time points :%d\n", static_cast(sir.get_num_time_points())); printf("People in\n"); for (size_t k = 0; k < (size_t)mio::osir::InfectionState::Count; k++) { - double dummy = 0; + ScalarType dummy = 0; for (size_t i = 0; i < (size_t)params.get_num_groups(); i++) { printf("\t %s[%d]: %.0f", vars[k].c_str(), (int)i, @@ -77,4 +79,4 @@ int main() printf("\t %s_Total: %.0f\n", vars[k].c_str(), dummy); } -} \ No newline at end of file +} diff --git a/cpp/examples/sde_seirvv.cpp b/cpp/examples/sde_seirvv.cpp index a3acfee360..4135bf0db1 100644 --- a/cpp/examples/sde_seirvv.cpp +++ b/cpp/examples/sde_seirvv.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -35,7 +35,7 @@ int main() mio::log_info("Simulating SEIRVV; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::sseirvv::Model model; + mio::sseirvv::Model model; ScalarType total_population = 180000; @@ -62,24 +62,24 @@ int main() // It is assumed that both variants have the same transmission probability // on contact and the same time exposed. The time infected is scaled by // 1.35 for the second variant. - model.parameters.get().get_baseline()(0, 0) = 1; - model.parameters.set(0.076); - model.parameters.set(0.076); - model.parameters.set(5.33); - model.parameters.set(5.33); - model.parameters.set(17.2); - model.parameters.set(17.2 * 1.35); + model.parameters.get>().get_baseline()(0, 0) = 1; + model.parameters.set>(0.076); + model.parameters.set>(0.076); + model.parameters.set>(5.33); + model.parameters.set>(5.33); + model.parameters.set>(17.2); + model.parameters.set>(17.2 * 1.35); model.check_constraints(); // Simulate the model up until tmid, with only the first variant. - auto sseirv = mio::simulate_stochastic(t0, tmid, dt, model); + auto sseirv = mio::simulate_stochastic(t0, tmid, dt, model); // Set the model population to the simulation result, so it is used as initial value for the second simulation. model.populations.array() = sseirv.get_last_value().cast>(); // The second variant enters with 100 individuals. This increases the model population to total_population + 100. model.populations[{mio::sseirvv::InfectionState::InfectedV2}] = 100; // Simulate the model from tmid to tmax, now with both variants. - auto sseirv2 = mio::simulate_stochastic(tmid, tmax, dt, model); + auto sseirv2 = mio::simulate_stochastic(tmid, tmax, dt, model); sseirv.print_table({"Susceptible", "ExposedV1", "InfectedV1", "RecoveredV1", "ExposedV2", "InfectedV2", "RecoveredV2", "ExposedV1V2", "InfectedV1V2", "RecoveredV1V2"}); sseirv2.print_table({"Susceptible", "ExposedV1", "InfectedV1", "RecoveredV1", "ExposedV2", "InfectedV2", diff --git a/cpp/examples/sde_sir.cpp b/cpp/examples/sde_sir.cpp index 9642add5ca..e72d6e2f22 100644 --- a/cpp/examples/sde_sir.cpp +++ b/cpp/examples/sde_sir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -33,7 +33,7 @@ int main() mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::ssir::Model model; + mio::ssir::Model model; model.populations[{mio::Index(mio::ssir::InfectionState::Infected)}] = 100; model.populations[{mio::Index(mio::ssir::InfectionState::Recovered)}] = 1000; @@ -41,10 +41,10 @@ int main() total_population - model.populations[{mio::Index(mio::ssir::InfectionState::Infected)}] - model.populations[{mio::Index(mio::ssir::InfectionState::Recovered)}]; - model.parameters.set(10); - model.parameters.set(1); - model.parameters.get().get_baseline()(0, 0) = 2.7; - model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.set>(10); + model.parameters.set>(1); + model.parameters.get>().get_baseline()(0, 0) = 2.7; + model.parameters.get>().add_damping(0.6, mio::SimulationTime(12.5)); model.check_constraints(); diff --git a/cpp/examples/sde_sirs.cpp b/cpp/examples/sde_sirs.cpp index d71a04221b..bd7d67479e 100644 --- a/cpp/examples/sde_sirs.cpp +++ b/cpp/examples/sde_sirs.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -26,15 +26,15 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0.; - double tmax = 5.; - double dt = 0.001; + ScalarType t0 = 0.; + ScalarType tmax = 5.; + ScalarType dt = 0.001; - double total_population = 10000; + ScalarType total_population = 10000; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::ssirs::Model model; + mio::ssirs::Model model; model.populations[{mio::Index(mio::ssirs::InfectionState::Infected)}] = 100; model.populations[{mio::Index(mio::ssirs::InfectionState::Recovered)}] = 1000; @@ -42,11 +42,12 @@ int main() total_population - model.populations[{mio::Index(mio::ssirs::InfectionState::Infected)}] - model.populations[{mio::Index(mio::ssirs::InfectionState::Recovered)}]; - model.parameters.set(10); - model.parameters.set(100); - model.parameters.set(1); - model.parameters.get().get_baseline()(0, 0) = 20.7; - model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.set>(10); + model.parameters.set>(100); + model.parameters.set>(1); + model.parameters.get>().get_baseline()(0, 0) = 20.7; + model.parameters.get>().add_damping(0.6, + mio::SimulationTime(12.5)); model.check_constraints(); diff --git a/cpp/examples/serialize.cpp b/cpp/examples/serialize.cpp index e3ea202f17..74c2237913 100644 --- a/cpp/examples/serialize.cpp +++ b/cpp/examples/serialize.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/examples/smm.cpp b/cpp/examples/smm.cpp index 1e7f5d285e..a55785e130 100644 --- a/cpp/examples/smm.cpp +++ b/cpp/examples/smm.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2024 German Aerospace Center (DLR-SC) * * Authors: Julia Bicker, René Schmieding @@ -41,9 +41,9 @@ int main() //Example how to run the stochastic metapopulation models with four regions const size_t num_regions = 4; - using Model = mio::smm::Model; + using Model = mio::smm::Model; - double numE = 12, numC = 4, numI = 12, numR = 0, numD = 0; + ScalarType numE = 12, numC = 4, numI = 12, numR = 0, numD = 0; Model model; //Population are distributed uniformly to the four regions @@ -58,8 +58,8 @@ int main() } //Set infection state adoption and spatial transition rates - std::vector> adoption_rates; - std::vector> transition_rates; + std::vector> adoption_rates; + std::vector> transition_rates; for (size_t r = 0; r < num_regions; ++r) { adoption_rates.push_back({InfectionState::S, InfectionState::E, @@ -86,11 +86,11 @@ int main() } } - model.parameters.get>() = adoption_rates; - model.parameters.get>() = transition_rates; + model.parameters.get>() = adoption_rates; + model.parameters.get>() = transition_rates; - double dt = 0.1; - double tmax = 30.; + ScalarType dt = 0.1; + ScalarType tmax = 30.0; auto sim = mio::smm::Simulation(model, 0.0, dt); sim.advance(tmax); diff --git a/cpp/examples/temporal_hybrid_dabm_osecir.cpp b/cpp/examples/temporal_hybrid_dabm_osecir.cpp index 0c8b7b2a97..df3d809f4a 100644 --- a/cpp/examples/temporal_hybrid_dabm_osecir.cpp +++ b/cpp/examples/temporal_hybrid_dabm_osecir.cpp @@ -43,15 +43,15 @@ int main() // As condition to switch between models we use a threshold of 20 infected individuals (For <20 Infected the ABM is used and for >=20 Infected the ODE-Model is used). using ABM = mio::dabm::Model>; - using ODE = mio::osecir::Model; + using ODE = mio::osecir::Model; //Initialize ABM population std::vector agents(1000); //Random variables used to initialize agents' position and infection state - auto& pos_sampler = mio::UniformDistribution::get_instance(); + auto& pos_sampler = mio::UniformDistribution::get_instance(); auto& stat_sampler = mio::DiscreteDistribution::get_instance(); //Infection state distribution - std::vector infection_state_dist{0.99, 0.01, 0., 0., 0., 0., 0., 0.}; + std::vector infection_state_dist{0.99, 0.01, 0., 0., 0., 0., 0., 0.}; //Sample agents' position and infection state for (auto& a : agents) { //Agents' positions are equally distributed in [-2, 2] x [-2, 2] @@ -62,11 +62,11 @@ int main() static_cast(stat_sampler(mio::thread_local_rng(), infection_state_dist)); } //Transmission parameters used for both models - const double contact_frequency = 10, trans_prob_on_contact = 0.06, time_E = 3., time_Ins = 2.5, time_Isy = 5.2, - time_Isev = 9., time_Icri = 7.2, mu_Ins_R = 0.2, mu_Isy_Isev = 0.1, mu_Isev_Icri = 0.1, - mu_Icri_D = 0.2; + const ScalarType contact_frequency = 10, trans_prob_on_contact = 0.06, time_E = 3., time_Ins = 2.5, time_Isy = 5.2, + time_Isev = 9., time_Icri = 7.2, mu_Ins_R = 0.2, mu_Isy_Isev = 0.1, mu_Isev_Icri = 0.1, + mu_Icri_D = 0.2; //Initialize ABM adoption rates - std::vector> adoption_rates; + std::vector> adoption_rates; //Second-order adoption rate (S->E) adoption_rates.push_back( {mio::osecir::InfectionState::Susceptible, @@ -130,7 +130,7 @@ int main() mu_Icri_D / time_Icri, {}}); //Interaction radius and noise - double interaction_radius = 0.4, noise = 0.5; + ScalarType interaction_radius = 0.4, noise = 0.5; ABM abm(agents, adoption_rates, interaction_radius, noise, {mio::osecir::InfectionState::InfectedSevere, mio::osecir::InfectionState::InfectedCritical, mio::osecir::InfectionState::Dead}); @@ -138,47 +138,48 @@ int main() //As we start modeling with the ABM, we don't need to initialize the population for the ODE-model //Initialize ODE model parameters ODE ode(1); - ode.parameters.get>()[mio::AgeGroup(0)] = time_E; - ode.parameters.get>()[mio::AgeGroup(0)] = time_Ins; - ode.parameters.get>()[mio::AgeGroup(0)] = time_Isy; - ode.parameters.get>()[mio::AgeGroup(0)] = time_Isev; - ode.parameters.get>()[mio::AgeGroup(0)] = time_Icri; - ode.parameters.get>()[mio::AgeGroup(0)] = + ode.parameters.get>()[mio::AgeGroup(0)] = time_E; + ode.parameters.get>()[mio::AgeGroup(0)] = time_Ins; + ode.parameters.get>()[mio::AgeGroup(0)] = time_Isy; + ode.parameters.get>()[mio::AgeGroup(0)] = time_Isev; + ode.parameters.get>()[mio::AgeGroup(0)] = time_Icri; + ode.parameters.get>()[mio::AgeGroup(0)] = trans_prob_on_contact; - ode.parameters.get>()[mio::AgeGroup(0)] = mu_Ins_R; - ode.parameters.get>()[mio::AgeGroup(0)] = mu_Isy_Isev; - ode.parameters.get>()[mio::AgeGroup(0)] = mu_Isev_Icri; - ode.parameters.get>()[mio::AgeGroup(0)] = mu_Icri_D; + ode.parameters.get>()[mio::AgeGroup(0)] = mu_Ins_R; + ode.parameters.get>()[mio::AgeGroup(0)] = mu_Isy_Isev; + ode.parameters.get>()[mio::AgeGroup(0)] = mu_Isev_Icri; + ode.parameters.get>()[mio::AgeGroup(0)] = mu_Icri_D; ode.apply_constraints(); - mio::ContactMatrixGroup& contact_matrix = ode.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, contact_frequency)); + mio::ContactMatrixGroup& contact_matrix = + ode.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, contact_frequency)); //Set t0 and internal dt for each model - double t0 = 0; - double dt = 0.1; + ScalarType t0 = 0; + ScalarType dt = 0.1; //Create simulations auto sim_abm = mio::dabm::Simulation(abm, t0, dt); auto sim_ode = mio::Simulation(ode, t0, dt); const auto result_fct_abm = [](const mio::dabm::Simulation>& sim, - double /*t*/) { + ScalarType /*t*/) { return sim.get_result(); }; - const auto result_fct_ode = [](const mio::Simulation& sim, double /*t*/) { + const auto result_fct_ode = [](const mio::Simulation& sim, ScalarType /*t*/) { return sim.get_result(); }; //Create hybrid simulation - double dt_switch = 0.2; - mio::hybrid::TemporalHybridSimulation, - mio::TimeSeries> + ScalarType dt_switch = 0.2; + mio::hybrid::TemporalHybridSimulation, + mio::TimeSeries> hybrid_sim(std::move(sim_abm), std::move(sim_ode), result_fct_abm, result_fct_ode, true, t0, dt_switch); //Define switching condition - const auto condition = [](const mio::TimeSeries& result_abm, const mio::TimeSeries& result_ode, - bool abm_used) { + const auto condition = [](const mio::TimeSeries& result_abm, + const mio::TimeSeries& result_ode, bool abm_used) { if (abm_used) { auto& last_value = result_abm.get_last_value().eval(); if ((last_value[(int)mio::osecir::InfectionState::Exposed] + diff --git a/cpp/memilio/CMakeLists.txt b/cpp/memilio/CMakeLists.txt index 3c8d6b0428..5079672695 100644 --- a/cpp/memilio/CMakeLists.txt +++ b/cpp/memilio/CMakeLists.txt @@ -115,6 +115,7 @@ add_library(memilio utils/string_literal.h utils/type_list.h utils/base_dir.h + ad/ad.h ) target_include_directories(memilio PUBLIC diff --git a/cpp/memilio/ad/ad.h b/cpp/memilio/ad/ad.h new file mode 100644 index 0000000000..96c93bcd29 --- /dev/null +++ b/cpp/memilio/ad/ad.h @@ -0,0 +1,58 @@ +/* +* Copyright (C) 2020-2025 MEmilio +* +* Authors: Julian Litz +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef MIO_AD_H +#define MIO_AD_H + +#include "ad/ad.hpp" +#include "memilio/math/eigen.h" + +#include +#include + +// Allow std::numeric_limits to work with AD types. +template +struct std::numeric_limits> : public numeric_limits { +}; + +// Ensures that Eigen recognizes our AD types as valid scalars. +namespace Eigen +{ +template +struct NumTraits> + : GenericNumTraits> { + using Scalar = ad::internal::active_type; + using Real = Scalar; + using NonInteger = Scalar; + using Nested = Scalar; + enum + { + IsComplex = 0, + IsInteger = 0, + IsSigned = 1, + RequireInitialization = 1, + ReadCost = 1, + AddCost = 3, + MulCost = 3 + }; +}; +} // namespace Eigen + +#endif // MIO_AD_H diff --git a/cpp/memilio/ad/include/ad/ad.hpp b/cpp/memilio/ad/include/ad/ad.hpp index 1492151261..c63248f944 100644 --- a/cpp/memilio/ad/include/ad/ad.hpp +++ b/cpp/memilio/ad/include/ad/ad.hpp @@ -4162,4 +4162,3 @@ namespace ad { } #endif - diff --git a/cpp/memilio/compartments/compartmental_model.h b/cpp/memilio/compartments/compartmental_model.h index 6abfea30cd..5173923af4 100644 --- a/cpp/memilio/compartments/compartmental_model.h +++ b/cpp/memilio/compartments/compartmental_model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Jan Kleinert, Daniel Abele @@ -139,12 +139,12 @@ struct CompartmentalModel { /** * @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 + * 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. + * @return Returns true if one or more constraints were corrected, false otherwise. */ bool apply_constraints() { @@ -201,8 +201,8 @@ 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`. + * 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. diff --git a/cpp/memilio/compartments/feedback_simulation.h b/cpp/memilio/compartments/feedback_simulation.h index 35eed632db..b1e82bba2e 100644 --- a/cpp/memilio/compartments/feedback_simulation.h +++ b/cpp/memilio/compartments/feedback_simulation.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker @@ -32,7 +32,7 @@ namespace mio { /** * @brief Daily local ICU occupancy per age group. - * + * * This parameter stores all historic (local) ICU occupancy values normalized per 100,000 inhabitants. * The values are extracted from the simulation results based on the provided ICU compartment indices * and are updated at each feedback step. @@ -89,7 +89,7 @@ struct GammaCutOff { using Type = size_t; static Type get_default(AgeGroup) { - return 45; + return Type(45); } static std::string name() { @@ -137,7 +137,7 @@ struct SoftPlusCurvatureParameter { using Type = FP; static Type get_default(AgeGroup) { - return FP(0.1); + return Type(0.1); } static std::string name() { @@ -153,7 +153,7 @@ struct NominalICUCapacity { using Type = FP; static Type get_default(AgeGroup) { - return FP(9.0); + return Type(9.0); } static std::string name() { @@ -239,7 +239,7 @@ class FeedbackSimulation * * The simulation is advanced in steps of dt_feedback. At each step, feedback * is applied, then the simulation is advanced, and afterwards the current ICU occupancy is stored. - * + * * Note that the simulation may make additional substeps depending on its own * timestep dt. When using fixed-step integrators, dt_feedback should be an integer multiple of * the simulation timestep dt. @@ -250,9 +250,10 @@ class FeedbackSimulation */ auto advance(const FP tmax, const FP dt_feedback = 1.0) { + using std::min; FP t = m_simulation.get_result().get_last_time(); while (t < tmax) { - FP dt_eff = std::min(dt_feedback, tmax - t); + FP dt_eff = min(dt_feedback, tmax - t); apply_feedback(t + dt_eff); m_simulation.advance(t + dt_eff); add_icu_occupancy(t + dt_eff); @@ -365,7 +366,7 @@ class FeedbackSimulation auto& model = m_simulation.get_model(); size_t num_groups = static_cast(model.parameters.get_num_groups()); const auto& last_values = m_simulation.get_result().get_last_value(); - Eigen::VectorXd icu_occ(num_groups); + Eigen::VectorX icu_occ(num_groups); for (size_t i = 0; i < m_icu_indices.size(); ++i) { icu_occ[i] = last_values[m_icu_indices[i]]; } @@ -405,6 +406,10 @@ class FeedbackSimulation */ void apply_feedback(FP t) { + using std::exp; + using std::log; + using std::min; + // get model parameters auto& params = m_simulation.get_model().parameters; const auto num_groups = (size_t)params.get_num_groups(); @@ -420,14 +425,14 @@ class FeedbackSimulation FP perceived_risk = calc_risk_perceived(); // add the perceived risk to the time series - m_perceived_risk.add_time_point(t, Eigen::VectorXd::Constant(num_groups, perceived_risk)); + m_perceived_risk.add_time_point(t, Eigen::VectorX::Constant(num_groups, perceived_risk)); - auto group_weights_all = Eigen::VectorXd::Constant(num_groups, 1.0); + auto group_weights_all = Eigen::VectorX::Constant(num_groups, 1.0); // define a lambda function to create a damping sampling object given a reduction factor and location auto set_contact_reduction = [=](FP val, size_t location) { - auto v = mio::UncertainValue(val); - return mio::DampingSampling(v, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(t), + auto v = mio::UncertainValue(val); + return mio::DampingSampling(v, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(t), std::vector{location}, group_weights_all); }; @@ -438,11 +443,11 @@ class FeedbackSimulation // compute the effective reduction factor using a softplus function. FP reduc_fac = (contactReductionMax[loc] - contactReductionMin[loc]) * epsilon * - std::log(std::exp(perceived_risk / epsilon) + 1.0) + + log(exp(perceived_risk / epsilon) + 1.0) + contactReductionMin[loc]; // clamp the reduction factor to the maximum allowed value. - reduc_fac = std::min(reduc_fac, contactReductionMax[loc]); + reduc_fac = min(reduc_fac, contactReductionMax[loc]); // generate the damping for the current location. auto damping = set_contact_reduction(reduc_fac, loc); diff --git a/cpp/memilio/compartments/flow_model.h b/cpp/memilio/compartments/flow_model.h index 7673042f03..614d21cda4 100644 --- a/cpp/memilio/compartments/flow_model.h +++ b/cpp/memilio/compartments/flow_model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding, Henrik Zunker @@ -61,7 +61,7 @@ using filtered_index_t = decltype(as_index( } //namespace details /** - * @brief A FlowModel is a CompartmentalModel defined by the flows between compartments. + * @brief A FlowModel is a CompartmentalModel defined by the flows between compartments. * * Inherits from @see CompartmentalModel, and defines the derivatives depending on the flows. Hence, a model implementing * FlowModel has to define the function get_flows, instead of get_derivatives. @@ -159,13 +159,13 @@ class FlowModel : public CompartmentalModel * flat_index = ((((I_{1}) * D_{2} + I_{2}) * D_{3} + I_{3}) ... ) * D_{n} + I_{n} * = I_{n} + I_{n-1} * D_{n} + I_{n-2} * D_{n} * D_{n-1} + ... + I_{1} * D_{n} * ... * D_{2} * for indices (I_{1}, ... , I_{n}) and dimension (D_{1}, ... , D_{n}). - * + * * The flows use their position in the TypeList Flows instead of the template argument Comp from the base class, - * hence they use the Population::Index type without Comp. The position is determined by the Source and Target + * hence they use the Population::Index type without Comp. The position is determined by the Source and Target * template arguments instead. As this dimension (the number of flows) may be larger or smaller * (e.g. a S->I->R model has 3 Comp's, but 2 Flows) than Comp::Count, we cannot simply use * this->populations.get_flat_index. - * + * * Instead, we omit Comp from PopIndex (getting FlowIndex), calculate the flat_index without the Comp index or * Dimension, i.e. we omit I_j and D_j corresponding to Comp from the formula above. We do this by calling * flatten_index with a FlowIndex, with dimensions derived from Pop via reduce_index. @@ -258,8 +258,8 @@ using get_initial_flows_expr_t = /** @} */ /** - * Template meta function to check if a type is a valid flow model. - * Defines a static constant of name `value`. + * 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. diff --git a/cpp/memilio/compartments/flow_simulation.h b/cpp/memilio/compartments/flow_simulation.h index 4d68abc100..3e4c5bed18 100755 --- a/cpp/memilio/compartments/flow_simulation.h +++ b/cpp/memilio/compartments/flow_simulation.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding, Henrik Zunker diff --git a/cpp/memilio/compartments/parameter_studies.h b/cpp/memilio/compartments/parameter_studies.h index 62cfe564a9..6de772c360 100644 --- a/cpp/memilio/compartments/parameter_studies.h +++ b/cpp/memilio/compartments/parameter_studies.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -43,7 +43,7 @@ namespace mio * Can simulate mobility graphs with one simulation in each node or single simulations. * @tparam S type of simulation that runs in one node of the graph. */ -template +template class ParameterStudy { public: @@ -55,12 +55,12 @@ class ParameterStudy * The Graph type that stores the parametes of the simulation. * This is the input of ParameterStudies. */ - using ParametersGraph = mio::Graph>; + using ParametersGraph = Graph>; /** * The Graph type that stores simulations and their results of each run. * This is the output of ParameterStudies for each run. */ - using SimulationGraph = mio::Graph, mio::MobilityEdge>; + using SimulationGraph = Graph, MobilityEdge>; /** * create study for graph of compartment models. @@ -70,7 +70,7 @@ class ParameterStudy * @param graph_sim_dt time step of graph simulation * @param num_runs number of runs */ - ParameterStudy(const ParametersGraph& graph, double t0, double tmax, double graph_sim_dt, size_t num_runs) + ParameterStudy(const ParametersGraph& graph, FP t0, FP tmax, FP graph_sim_dt, size_t num_runs) : m_graph(graph) , m_num_runs(num_runs) , m_t0{t0} @@ -89,9 +89,8 @@ class ParameterStudy * @param graph_sim_dt time step of graph simulation * @param num_runs number of runs */ - ParameterStudy(const ParametersGraph& graph, double t0, double tmax, double dev_rel, double graph_sim_dt, - size_t num_runs) - : ParameterStudy(graph, t0, tmax, graph_sim_dt, num_runs) + ParameterStudy(const ParametersGraph& graph, FP t0, FP tmax, FP dev_rel, FP graph_sim_dt, size_t num_runs) + : ParameterStudy(graph, t0, tmax, graph_sim_dt, num_runs) { for (auto& params_node : m_graph.nodes()) { set_params_distributions_normal(params_node, t0, tmax, dev_rel); @@ -105,22 +104,22 @@ class ParameterStudy * @param tmax end time of simulations * @param num_runs number of runs in ensemble run */ - ParameterStudy(typename Simulation::Model const& model, double t0, double tmax, size_t num_runs) - : ParameterStudy({}, t0, tmax, tmax - t0, num_runs) + ParameterStudy(typename Simulation::Model const& model, FP t0, FP tmax, size_t num_runs) + : ParameterStudy({}, t0, tmax, tmax - t0, num_runs) { m_graph.add_node(0, model); } - /* + /** * @brief Carry out all simulations in the parameter study. * Save memory and enable more runs by immediately processing and/or discarding the result. - * The result processing function is called when a run is finished. It receives the result of the run - * (a SimulationGraph object) and an ordered index. The values returned by the result processing function + * The result processing function is called when a run is finished. It receives the result of the run + * (a SimulationGraph object) and an ordered index. The values returned by the result processing function * are gathered and returned as a list. * This function is parallelized if memilio is configured with MEMILIO_ENABLE_MPI. - * The MPI processes each contribute a share of the runs. The sample function and result processing function - * are called in the same process that performs the run. The results returned by the result processing function are - * gathered at the root process and returned as a list by the root in the same order as if the programm + * The MPI processes each contribute a share of the runs. The sample function and result processing function + * are called in the same process that performs the run. The results returned by the result processing function are + * gathered at the root process and returned as a list by the root in the same order as if the programm * were running sequentially. Processes other than the root return an empty list. * @param sample_graph Function that receives the ParametersGraph and returns a sampled copy. * @param result_processing_function Processing function for simulation results, e.g., output function. @@ -210,7 +209,7 @@ class ParameterStudy return ensemble_result; } - /* + /** * @brief Carry out all simulations in the parameter study. * Convenience function for a few number of runs, but can use more memory because it stores all runs until the end. * Unlike the other overload, this function is not MPI-parallel. @@ -252,17 +251,16 @@ class ParameterStudy return ensemble_result; } - /* + /** * @brief sets the number of Monte Carlo runs * @param[in] num_runs number of runs */ - void set_num_runs(size_t num_runs) { m_num_runs = num_runs; } - /* + /** * @brief returns the number of Monte Carlo runs */ int get_num_runs() const @@ -270,32 +268,32 @@ class ParameterStudy return static_cast(m_num_runs); } - /* + /** * @brief sets end point in simulation * @param[in] tmax end point in simulation */ - void set_tmax(double tmax) + void set_tmax(FP tmax) { m_tmax = tmax; } - /* + /** * @brief returns end point in simulation */ - double get_tmax() const + FP get_tmax() const { return m_tmax; } - void set_t0(double t0) + void set_t0(FP t0) { m_t0 = t0; } - /* + /** * @brief returns start point in simulation */ - double get_t0() const + FP get_t0() const { return m_t0; } @@ -338,7 +336,7 @@ class ParameterStudy private: //sample parameters and create simulation template - mio::GraphSimulation create_sampled_simulation(SampleGraphFunction sample_graph) + GraphSimulation create_sampled_simulation(SampleGraphFunction sample_graph) { SimulationGraph sim_graph; @@ -350,7 +348,7 @@ class ParameterStudy sim_graph.add_edge(edge.start_node_idx, edge.end_node_idx, edge.property); } - return make_mobility_sim(m_t0, m_dt_graph_sim, std::move(sim_graph)); + return make_mobility_sim(m_t0, m_dt_graph_sim, std::move(sim_graph)); } std::vector distribute_runs(size_t num_runs, int num_procs) @@ -374,13 +372,13 @@ class ParameterStudy size_t m_num_runs; // Start time (should be the same for all simulations) - double m_t0; + FP m_t0; // End time (should be the same for all simulations) - double m_tmax; + FP m_tmax; // time step of the graph - double m_dt_graph_sim; + FP m_dt_graph_sim; // adaptive time step of the integrator (will be corrected if too large/small) - double m_dt_integration = 0.1; + FP m_dt_integration = 0.1; // RandomNumberGenerator m_rng; }; diff --git a/cpp/memilio/compartments/simulation.h b/cpp/memilio/compartments/simulation.h index a29f06481b..cec0fb0392 100755 --- a/cpp/memilio/compartments/simulation.h +++ b/cpp/memilio/compartments/simulation.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Jan Kleinert, Daniel Abele @@ -85,8 +85,8 @@ template using advance_expr_t = decltype(std::declval().advance(std::declval())); /** - * Template meta function to check if a type is a compartment model simulation. - * Defines a static constant of name `value`. + * Template meta function to check if a type is a compartment model simulation. + * Defines a static constant of name `value`. * The constant `value` will be equal to true if Sim is a valid compartment simulation type. * Otherwise, `value` will be equal to false. * @tparam FP floating point type, e.g., double diff --git a/cpp/memilio/compartments/simulation_base.h b/cpp/memilio/compartments/simulation_base.h index b6a0a172f4..3c9056b7de 100755 --- a/cpp/memilio/compartments/simulation_base.h +++ b/cpp/memilio/compartments/simulation_base.h @@ -69,19 +69,17 @@ class SimulationBase { } - SimulationBase& operator=(const SimulationBase& other) + SimulationBase& operator=(const SimulationBase& other) { - if(this != &other) - { - m_model = std::make_unique(*other.m_model); + if (this != &other) { + m_model = std::make_unique(*other.m_model); m_integrator = other.m_integrator; - m_result = other.m_result; - m_dt = other.m_dt; + m_result = other.m_result; + m_dt = other.m_dt; } - return *this; + return *this; } - /** * @brief Set the IntegratorCore used in the simulation. * @param[in] integrator_core A unique pointer to an object derived from IntegratorCore. diff --git a/cpp/memilio/compartments/stochastic_model.h b/cpp/memilio/compartments/stochastic_model.h index c6a6c6591f..40ebdc18ee 100644 --- a/cpp/memilio/compartments/stochastic_model.h +++ b/cpp/memilio/compartments/stochastic_model.h @@ -79,7 +79,7 @@ class StochasticModel */ FP sample_standard_normal_distribution() const { - return FP{DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0)}; + return FP{DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0)}; } /// @brief Access the model's RNG. diff --git a/cpp/memilio/data/analyze_result.cpp b/cpp/memilio/data/analyze_result.cpp index 3b3b25fa69..e6a45dd81e 100644 --- a/cpp/memilio/data/analyze_result.cpp +++ b/cpp/memilio/data/analyze_result.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, David Kerkmann, Sascha Korf @@ -25,177 +25,4 @@ namespace mio { -TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, const double abs_tol) -{ - const auto t0 = simulation_result.get_time(0); - const auto t_max = simulation_result.get_last_time(); - // add another day if the first time point is equal to day_0 up to absolute tolerance tol - const auto day0 = (t0 - abs_tol < std::ceil(t0) - 1) ? std::floor(t0) : std::ceil(t0); - // add another day if the last time point is equal to day_max up to absolute tolerance tol - const auto day_max = (t_max + abs_tol > std::floor(t_max) + 1) ? std::ceil(t_max) : std::floor(t_max); - - // create interpolation_times vector with all days between day0 and day_max - std::vector tps(static_cast(day_max) - static_cast(day0) + 1); - std::iota(tps.begin(), tps.end(), day0); - - return interpolate_simulation_result(simulation_result, tps); -} - -TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, - const std::vector& interpolation_times) -{ - assert(simulation_result.get_num_time_points() > 0 && "TimeSeries must not be empty."); - - assert(std::is_sorted(interpolation_times.begin(), interpolation_times.end()) && - "Time points for interpolation have to be sorted in non-descending order."); - - if (interpolation_times.size() >= 2) { - assert((interpolation_times[1] > simulation_result.get_time(0) && - interpolation_times.rbegin()[1] <= simulation_result.get_last_time()) && - "All but the first and the last time point of interpolation have lie between simulation times (strictly " - "for lower boundary)."); - } - - TimeSeries interpolated(simulation_result.get_num_elements()); - - if (interpolation_times.size() == 0) { - return interpolated; - } - - size_t interp_idx = 0; - // add first time point of interpolation times in case it is smaller than the first time point of simulation_result - // this is used for the case that it equals the first time point of simulation up to tolerance - // this is necessary even if the tolerance is 0 due to the way the comparison in the loop is implemented (< and >=) - if (simulation_result.get_time(0) >= interpolation_times[0]) { - interpolated.add_time_point(interpolation_times[0], simulation_result[0]); - ++interp_idx; - } - - //interpolate between pair of time points that lie on either side of each interpolation point - for (Eigen::Index sim_idx = 0; - sim_idx < simulation_result.get_num_time_points() - 1 && interp_idx < interpolation_times.size();) { - //only go to next pair of time points if no time point is added. - //otherwise check the same time points again - //in case there is more than one interpolation point between the two time points - if (simulation_result.get_time(sim_idx) < interpolation_times[interp_idx] && - simulation_result.get_time(sim_idx + 1) >= interpolation_times[interp_idx]) { - interpolated.add_time_point( - interpolation_times[interp_idx], - linear_interpolation(interpolation_times[interp_idx], simulation_result.get_time(sim_idx), - simulation_result.get_time(sim_idx + 1), simulation_result[sim_idx], - simulation_result[sim_idx + 1])); - ++interp_idx; - } - else { - ++sim_idx; - } - } - - // add last time point of interpolation times in case it is larger than the last time point of simulation_result - // this is used for the case that it equals the last time point of simulation up to tolerance - if (interp_idx < interpolation_times.size() && - simulation_result.get_last_time() < interpolation_times[interp_idx]) { - interpolated.add_time_point(interpolation_times[interp_idx], simulation_result.get_last_value()); - } - - return interpolated; -} - -std::vector>> -sum_nodes(const std::vector>>& ensemble_result) -{ - auto num_runs = ensemble_result.size(); - auto num_nodes = ensemble_result[0].size(); - auto num_time_points = ensemble_result[0][0].get_num_time_points(); - auto num_elements = ensemble_result[0][0].get_num_elements(); - - std::vector>> sum_result( - num_runs, std::vector>(1, TimeSeries::zero(num_time_points, num_elements))); - - for (size_t run = 0; run < num_runs; run++) { - for (Eigen::Index time = 0; time < num_time_points; time++) { - sum_result[run][0].get_time(time) = ensemble_result[run][0].get_time(time); - for (size_t node = 0; node < num_nodes; node++) { - sum_result[run][0][time] += ensemble_result[run][node][time]; - } - } - } - return sum_result; -} - -std::vector> ensemble_mean(const std::vector>>& ensemble_result) -{ - auto num_runs = ensemble_result.size(); - auto num_nodes = ensemble_result[0].size(); - auto num_time_points = ensemble_result[0][0].get_num_time_points(); - auto num_elements = ensemble_result[0][0].get_num_elements(); - - std::vector> mean(num_nodes, TimeSeries::zero(num_time_points, num_elements)); - - for (size_t run = 0; run < num_runs; run++) { - assert(ensemble_result[run].size() == num_nodes && "ensemble results not uniform."); - for (size_t node = 0; node < num_nodes; node++) { - assert(ensemble_result[run][node].get_num_time_points() == num_time_points && - "ensemble results not uniform."); - for (Eigen::Index time = 0; time < num_time_points; time++) { - assert(ensemble_result[run][node].get_num_elements() == num_elements && - "ensemble results not uniform."); - mean[node].get_time(time) = ensemble_result[run][node].get_time(time); - mean[node][time] += ensemble_result[run][node][time] / num_runs; - } - } - } - - return mean; -} - -std::vector> ensemble_percentile(const std::vector>>& ensemble_result, - double p) -{ - assert(p > 0.0 && p < 1.0 && "Invalid percentile value."); - - auto num_runs = ensemble_result.size(); - auto num_nodes = ensemble_result[0].size(); - auto num_time_points = ensemble_result[0][0].get_num_time_points(); - auto num_elements = ensemble_result[0][0].get_num_elements(); - - std::vector> percentile(num_nodes, TimeSeries::zero(num_time_points, num_elements)); - - std::vector single_element_ensemble(num_runs); //reused for each element - for (size_t node = 0; node < num_nodes; node++) { - for (Eigen::Index time = 0; time < num_time_points; time++) { - percentile[node].get_time(time) = ensemble_result[0][node].get_time(time); - for (Eigen::Index elem = 0; elem < num_elements; elem++) { - std::transform(ensemble_result.begin(), ensemble_result.end(), single_element_ensemble.begin(), - [=](auto& run) { - return run[node][time][elem]; - }); - std::sort(single_element_ensemble.begin(), single_element_ensemble.end()); - percentile[node][time][elem] = single_element_ensemble[static_cast(num_runs * p)]; - } - } - } - return percentile; -} - -double result_distance_2norm(const std::vector>& result1, - const std::vector>& result2) -{ - assert(result1.size() == result2.size()); - assert(result1.size() > 0); - assert(result1[0].get_num_time_points() > 0); - assert(result1[0].get_num_elements() > 0); - - auto norm_sqr = 0.0; - for (auto iter_node1 = result1.begin(), iter_node2 = result2.begin(); iter_node1 < result1.end(); - ++iter_node1, ++iter_node2) { - for (Eigen::Index time_idx = 0; time_idx < iter_node1->get_num_time_points(); ++time_idx) { - auto v1 = (*iter_node1)[time_idx]; - auto v2 = (*iter_node2)[time_idx]; - norm_sqr += ((v1 - v2).array() * (v1 - v2).array()).sum(); - } - } - return std::sqrt(norm_sqr); -} - } // namespace mio diff --git a/cpp/memilio/data/analyze_result.h b/cpp/memilio/data/analyze_result.h index 0377a14fbb..5e80a05773 100644 --- a/cpp/memilio/data/analyze_result.h +++ b/cpp/memilio/data/analyze_result.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, David Kerkmann, Sascha Korf @@ -22,6 +22,7 @@ #include "memilio/utils/time_series.h" #include "memilio/mobility/metapopulation_mobility_instant.h" +#include "memilio/math/interpolation.h" #include "memilio/io/io.h" #include @@ -40,8 +41,8 @@ namespace mio * @param abs_tol absolute tolerance given for doubles t0 and tmax to account for small deviations from whole days. * @return interpolated time series */ -TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, - const double abs_tol = 1e-14); +template +TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, const FP abs_tol = 1e-14); /** * @brief interpolate time series with freely chosen time points that lie in between the time points of the given time series up to a given tolerance. @@ -50,8 +51,10 @@ TimeSeries interpolate_simulation_result(const TimeSeries& simul * @param interpolations_times std::vector of time points at which simulation results are interpolated. * @return interpolated time series at given interpolation points */ -TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, - const std::vector& interpolation_times); +template +TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, + + const std::vector& interpolation_times); /** * helper template, type returned by overload interpolate_simulation_result(T t) @@ -76,8 +79,8 @@ std::vector> interpolate_ensemble_results(const std::vecto return interpolated; } -std::vector>> -sum_nodes(const std::vector>>& ensemble_result); +template +std::vector>> sum_nodes(const std::vector>>& ensemble_result); /** * @brief computes mean of each compartment, node, and time point over all runs @@ -87,7 +90,8 @@ sum_nodes(const std::vector>>& ensemble_result); * @param ensemble_results uniform results of multiple simulation runs * @return mean of the results over all runs */ -std::vector> ensemble_mean(const std::vector>>& ensemble_results); +template +std::vector> ensemble_mean(const std::vector>>& ensemble_results); /** * @brief computes the p percentile of the result for each compartment, node, and time point. @@ -100,19 +104,19 @@ std::vector> ensemble_mean(const std::vector> ensemble_percentile(const std::vector>>& ensemble_result, - double p); +template +std::vector> ensemble_percentile(const std::vector>>& ensemble_result, FP p); /** * interpolate time series with evenly spaced, integer time points for each node. * @see interpolate_simulation_result * @param graph_result graph of simulations whose results will be interpolated * @return one interpolated time series per node */ -template -std::vector> -interpolate_simulation_result(const Graph, MobilityEdge>& graph_result) +template +std::vector> +interpolate_simulation_result(const Graph, MobilityEdge>& graph_result) { - std::vector> interpolated; + std::vector> interpolated; interpolated.reserve(graph_result.nodes().size()); std::transform(graph_result.nodes().begin(), graph_result.nodes().end(), std::back_inserter(interpolated), [](auto& n) { @@ -129,8 +133,9 @@ interpolate_simulation_result(const Graph, MobilityEd * @param result2 second result. * @return Computed distance between result1 and result2. */ -double result_distance_2norm(const std::vector>& result1, - const std::vector>& result2); +template +FP result_distance_2norm(const std::vector>& result1, + const std::vector>& result2); /** * Compute the distance between two compartment model simulation results in one compartment. @@ -142,10 +147,12 @@ double result_distance_2norm(const std::vector>& result1 * @param compartment the compartment to compare. * @return Computed distance between result1 and result2. */ -template -double result_distance_2norm(const std::vector>& result1, - const std::vector>& result2, InfectionState compartment) +template +FP result_distance_2norm(const std::vector>& result1, + const std::vector>& result2, InfectionState compartment) { + using std::sqrt; + assert(result1.size() == result2.size()); assert(result1.size() > 0); assert(result1[0].get_num_time_points() > 0); @@ -167,7 +174,187 @@ double result_distance_2norm(const std::vector>& result1 } } } - return std::sqrt(norm_sqr); + return sqrt(norm_sqr); +} + +template +TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, const FP abs_tol) +{ + using std::ceil; + using std::floor; + const auto t0 = simulation_result.get_time(0); + const auto t_max = simulation_result.get_last_time(); + // add another day if the first time point is equal to day_0 up to absolute tolerance tol + const auto day0 = (t0 - abs_tol < ceil(t0) - 1) ? floor(t0) : ceil(t0); + // add another day if the last time point is equal to day_max up to absolute tolerance tol + const auto day_max = (t_max + abs_tol > floor(t_max) + 1) ? ceil(t_max) : floor(t_max); + + // create interpolation_times vector with all days between day0 and day_max + std::vector tps(static_cast(day_max) - static_cast(day0) + 1); + std::iota(tps.begin(), tps.end(), day0); + + return interpolate_simulation_result(simulation_result, tps); +} + +template +TimeSeries interpolate_simulation_result(const TimeSeries& simulation_result, + const std::vector& interpolation_times) +{ + assert(simulation_result.get_num_time_points() > 0 && "TimeSeries must not be empty."); + + assert(std::is_sorted(interpolation_times.begin(), interpolation_times.end()) && + "Time points for interpolation have to be sorted in non-descending order."); + + if (interpolation_times.size() >= 2) { + assert((interpolation_times[1] > simulation_result.get_time(0) && + interpolation_times.rbegin()[1] <= simulation_result.get_last_time()) && + "All but the first and the last time point of interpolation have lie between simulation times (strictly " + "for lower boundary)."); + } + + TimeSeries interpolated(simulation_result.get_num_elements()); + + if (interpolation_times.size() == 0) { + return interpolated; + } + + size_t interp_idx = 0; + // add first time point of interpolation times in case it is smaller than the first time point of simulation_result + // this is used for the case that it equals the first time point of simulation up to tolerance + // this is necessary even if the tolerance is 0 due to the way the comparison in the loop is implemented (< and >=) + if (simulation_result.get_time(0) >= interpolation_times[0]) { + interpolated.add_time_point(interpolation_times[0], simulation_result[0]); + ++interp_idx; + } + + //interpolate between pair of time points that lie on either side of each interpolation point + for (Eigen::Index sim_idx = 0; + sim_idx < simulation_result.get_num_time_points() - 1 && interp_idx < interpolation_times.size();) { + //only go to next pair of time points if no time point is added. + //otherwise check the same time points again + //in case there is more than one interpolation point between the two time points + if (simulation_result.get_time(sim_idx) < interpolation_times[interp_idx] && + simulation_result.get_time(sim_idx + 1) >= interpolation_times[interp_idx]) { + interpolated.add_time_point( + interpolation_times[interp_idx], + linear_interpolation(interpolation_times[interp_idx], simulation_result.get_time(sim_idx), + simulation_result.get_time(sim_idx + 1), simulation_result[sim_idx], + simulation_result[sim_idx + 1])); + ++interp_idx; + } + else { + ++sim_idx; + } + } + + // add last time point of interpolation times in case it is larger than the last time point of simulation_result + // this is used for the case that it equals the last time point of simulation up to tolerance + if (interp_idx < interpolation_times.size() && + simulation_result.get_last_time() < interpolation_times[interp_idx]) { + interpolated.add_time_point(interpolation_times[interp_idx], simulation_result.get_last_value()); + } + + return interpolated; +} + +template +std::vector>> sum_nodes(const std::vector>>& ensemble_result) +{ + auto num_runs = ensemble_result.size(); + auto num_nodes = ensemble_result[0].size(); + auto num_time_points = ensemble_result[0][0].get_num_time_points(); + auto num_elements = ensemble_result[0][0].get_num_elements(); + + std::vector>> sum_result( + num_runs, std::vector>(1, TimeSeries::zero(num_time_points, num_elements))); + + for (size_t run = 0; run < num_runs; run++) { + for (Eigen::Index time = 0; time < num_time_points; time++) { + sum_result[run][0].get_time(time) = ensemble_result[run][0].get_time(time); + for (size_t node = 0; node < num_nodes; node++) { + sum_result[run][0][time] += ensemble_result[run][node][time]; + } + } + } + return sum_result; +} + +template +std::vector> ensemble_mean(const std::vector>>& ensemble_result) +{ + auto num_runs = ensemble_result.size(); + auto num_nodes = ensemble_result[0].size(); + auto num_time_points = ensemble_result[0][0].get_num_time_points(); + auto num_elements = ensemble_result[0][0].get_num_elements(); + + std::vector> mean(num_nodes, TimeSeries::zero(num_time_points, num_elements)); + + for (size_t run = 0; run < num_runs; run++) { + assert(ensemble_result[run].size() == num_nodes && "ensemble results not uniform."); + for (size_t node = 0; node < num_nodes; node++) { + assert(ensemble_result[run][node].get_num_time_points() == num_time_points && + "ensemble results not uniform."); + for (Eigen::Index time = 0; time < num_time_points; time++) { + assert(ensemble_result[run][node].get_num_elements() == num_elements && + "ensemble results not uniform."); + mean[node].get_time(time) = ensemble_result[run][node].get_time(time); + mean[node][time] += ensemble_result[run][node][time] / num_runs; + } + } + } + + return mean; +} + +template +std::vector> ensemble_percentile(const std::vector>>& ensemble_result, FP p) +{ + assert(p > 0.0 && p < 1.0 && "Invalid percentile value."); + + auto num_runs = ensemble_result.size(); + auto num_nodes = ensemble_result[0].size(); + auto num_time_points = ensemble_result[0][0].get_num_time_points(); + auto num_elements = ensemble_result[0][0].get_num_elements(); + + std::vector> percentile(num_nodes, TimeSeries::zero(num_time_points, num_elements)); + + std::vector single_element_ensemble(num_runs); //reused for each element + for (size_t node = 0; node < num_nodes; node++) { + for (Eigen::Index time = 0; time < num_time_points; time++) { + percentile[node].get_time(time) = ensemble_result[0][node].get_time(time); + for (Eigen::Index elem = 0; elem < num_elements; elem++) { + std::transform(ensemble_result.begin(), ensemble_result.end(), single_element_ensemble.begin(), + [=](auto& run) { + return run[node][time][elem]; + }); + std::sort(single_element_ensemble.begin(), single_element_ensemble.end()); + percentile[node][time][elem] = single_element_ensemble[static_cast(num_runs * p)]; + } + } + } + return percentile; +} + +template +FP result_distance_2norm(const std::vector>& result1, + const std::vector>& result2) +{ + using std::sqrt; + assert(result1.size() == result2.size()); + assert(result1.size() > 0); + assert(result1[0].get_num_time_points() > 0); + assert(result1[0].get_num_elements() > 0); + + auto norm_sqr = 0.0; + for (auto iter_node1 = result1.begin(), iter_node2 = result2.begin(); iter_node1 < result1.end(); + ++iter_node1, ++iter_node2) { + for (Eigen::Index time_idx = 0; time_idx < iter_node1->get_num_time_points(); ++time_idx) { + auto v1 = (*iter_node1)[time_idx]; + auto v2 = (*iter_node2)[time_idx]; + norm_sqr += ((v1 - v2).array() * (v1 - v2).array()).sum(); + } + } + return sqrt(norm_sqr); } /** diff --git a/cpp/memilio/epidemiology/adoption_rate.h b/cpp/memilio/epidemiology/adoption_rate.h index a56dfcdc54..705f729025 100644 --- a/cpp/memilio/epidemiology/adoption_rate.h +++ b/cpp/memilio/epidemiology/adoption_rate.h @@ -32,10 +32,10 @@ namespace mio * The population having "status" is multiplied with "factor." * @tparam Status An infection state enum. */ -template +template struct Influence { Status status; - ScalarType factor; + FP factor; }; /** @@ -45,13 +45,13 @@ struct Influence { * the sum over all "influences", which scale their "status" with the respective "factor". * @tparam Status An infection state enum. */ -template +template struct AdoptionRate { Status from; // i Status to; // j mio::regions::Region region; // k - ScalarType factor; // gammahat_{ij}^k - std::vector> influences; // influences[tau] = ( Psi_{i,j,tau} , gamma_{i,j,tau} ) + FP factor; // gammahat_{ij}^k + std::vector> influences; // influences[tau] = ( Psi_{i,j,tau} , gamma_{i,j,tau} ) }; } // namespace mio diff --git a/cpp/memilio/epidemiology/contact_matrix.h b/cpp/memilio/epidemiology/contact_matrix.h index 83ce44517f..4a92ee41ab 100644 --- a/cpp/memilio/epidemiology/contact_matrix.h +++ b/cpp/memilio/epidemiology/contact_matrix.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Martin J. Kuehn, Daniel Abele @@ -21,7 +21,10 @@ #define EPI_ODE_CONTACT_FREQUENCY_MATRIX_H #include "memilio/epidemiology/damping.h" +#include "memilio/math/matrix_shape.h" +#include "memilio/math/math_utils.h" #include "memilio/utils/stl_util.h" +#include "memilio/utils/logging.h" #include #include @@ -37,9 +40,10 @@ namespace mio * Coefficient wise expression, so B, D, M matrices must have the same shape. * @see Damping * @see ContactMatrix + * @tparam FP floating point, e.g. double * @tparam D instance of Dampings or compatible type */ -template +template class DampingMatrixExpression { public: @@ -49,7 +53,7 @@ class DampingMatrixExpression /** * construct with baseline and minimum contacts and zero dampings. - * @param baseline matrix expression + * @param baseline matrix expression * @param minimum matrix expression, must be same size as baseline * @tparam M, K matrix expressions compatible with Matrix type */ @@ -64,7 +68,7 @@ class DampingMatrixExpression /** * construct with only baseline, minimum and dampings is zero. - * @param baseline matrix expression + * @param baseline matrix expression * @tparam M matrix expressions compatible with Matrix type */ template @@ -184,14 +188,11 @@ class DampingMatrixExpression * @param t time in the simulation * @return matrix expression (num_groups x num_groups) */ - auto get_matrix_at(SimulationTime t) const + auto get_matrix_at(SimulationTime t) const { + assert(Shape::get_shape_of(m_dampings.get_matrix_at(t)) == Shape::get_shape_of(m_baseline)); return m_baseline - (m_dampings.get_matrix_at(t).array() * (m_baseline - m_minimum).array()).matrix(); } - auto get_matrix_at(double t) const - { - return get_matrix_at(SimulationTime(t)); - } /** * gtest printer. @@ -204,7 +205,7 @@ class DampingMatrixExpression } /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -264,9 +265,10 @@ class DampingMatrixExpression /** * represents a collection of DampingMatrixExpressions that are summed up. + * @param FP floating point, e.g. double * @tparam E some instance of DampingMatrixExpression or compatible type. */ -template +template class DampingMatrixExpressionGroup { public: @@ -329,14 +331,14 @@ class DampingMatrixExpressionGroup } /** - * get the number of groups. + * Get the dimensions of the matrices. */ Shape get_shape() const { return m_matrices[0].get_shape(); } - /** + /** * equality operators. */ bool operator==(const DampingMatrixExpressionGroup& other) const @@ -387,13 +389,12 @@ class DampingMatrixExpressionGroup * @param t point in time * @return matrix expression of size num_groups x num_groups */ - template - auto get_matrix_at(T t) const + auto get_matrix_at(SimulationTime t) const { - return Eigen::MatrixXd::NullaryExpr( + return Eigen::Matrix::NullaryExpr( get_shape().rows(), get_shape().cols(), [t, this](Eigen::Index i, Eigen::Index j) { - return std::accumulate(m_matrices.begin(), m_matrices.end(), 0.0, [t, i, j](double s, auto& m) { - return s + m.get_matrix_at(t)(i, j); + return std::accumulate(m_matrices.begin(), m_matrices.end(), FP(0.0), [t, i, j](FP s, auto& m) { + return evaluate_intermediate(s + m.get_matrix_at(t)(i, j)); }); }); } @@ -430,7 +431,7 @@ class DampingMatrixExpressionGroup } /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -493,16 +494,17 @@ class DampingMatrixExpressionGroup * The effective contacts are B - D * (B - M), where B is the baseline, D are * combined dampings and M is the minimum. * The minimum is not necessarily smaller than the baseline in every entry, - * but it reflects the state at maximum lockdown. In places where the + * but it reflects the state at maximum lockdown. In places where the * minimum is greater than the baseline, a positive damping increases * instead of reduces contacts. * All these members are matrix valued, e.g. B_ij are the normal contacts * that one person in group i has with persons in group j. */ -class ContactMatrix : public DampingMatrixExpression +template +class ContactMatrix : public DampingMatrixExpression> { public: - using Base = DampingMatrixExpression; + using Base = DampingMatrixExpression>; using Base::Base; /** @@ -527,12 +529,13 @@ class ContactMatrix : public DampingMatrixExpression /** * represents a collection of contact frequency matrices that whose sum is the total * number of contacts. - * can separate matrices of contacts in different contexts, e.g. work, leisure, etc. + * can separate matrices of contacts in different contexts, e.g. work, leisure, etc. */ -class ContactMatrixGroup : public DampingMatrixExpressionGroup +template +class ContactMatrixGroup : public DampingMatrixExpressionGroup> { public: - using Base = DampingMatrixExpressionGroup; + using Base = DampingMatrixExpressionGroup>; using Base::Base; /** diff --git a/cpp/memilio/epidemiology/damping.h b/cpp/memilio/epidemiology/damping.h index c5199acca7..4ed89f17a8 100644 --- a/cpp/memilio/epidemiology/damping.h +++ b/cpp/memilio/epidemiology/damping.h @@ -20,6 +20,7 @@ #ifndef DAMPING_H #define DAMPING_H +#include "memilio/config.h" #include "memilio/math/eigen.h" #include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/type_safe.h" @@ -49,28 +50,30 @@ DECL_TYPESAFE(int, DampingType); /** * double simulation time. */ -class MEMILIO_ENABLE_EBO SimulationTime : public TypeSafe, - public OperatorAdditionSubtraction, - public OperatorScalarMultiplicationDivision, - public OperatorComparison +template +class MEMILIO_ENABLE_EBO SimulationTime : public TypeSafe>, + public OperatorAdditionSubtraction>, + public OperatorScalarMultiplicationDivision, FP>, + public OperatorComparison> { public: - using TypeSafe::TypeSafe; + using TypeSafe>::TypeSafe; }; /** * represent interventions or effects that affect contact frequencies between multiple groups. * Dampings have a level and a type and are active from a certain point in time forward. * Dampings are square matrix valued, coefficient d_ij affects the contacts from group i to group j. + * @tparam FP Floating point type, e.g., double * @tparam S Matrix shape type */ -template -class Damping : public std::tuple +template +class Damping : public std::tuple> { public: using Shape = S; using Matrix = typename Shape::Matrix; - using Base = std::tuple; + using Base = std::tuple>; /** * create a default Damping. @@ -92,7 +95,7 @@ class Damping : public std::tuple - Damping(const Eigen::MatrixBase& m, DampingLevel level, DampingType type, SimulationTime t) + Damping(const Eigen::MatrixBase& m, DampingLevel level, DampingType type, SimulationTime t) : Base(m, level, type, t) { assert((get_coeffs().array() <= 1.).all() && "damping coefficient out of range"); @@ -108,7 +111,7 @@ class Damping : public std::tuple::value, void>> - Damping(double d, DampingLevel level, DampingType type, SimulationTime t, T... shape_args) + Damping(FP d, DampingLevel level, DampingType type, SimulationTime t, T... shape_args) : Damping(Matrix::Constant(Shape(shape_args...).rows(), Shape(shape_args...).cols(), d), level, type, t) { } @@ -120,7 +123,7 @@ class Damping : public std::tuple - Damping(const Eigen::MatrixBase& m, SimulationTime t) + Damping(const Eigen::MatrixBase& m, SimulationTime t) : Damping(m, DampingLevel(0), DampingType(0), t) { } @@ -133,7 +136,7 @@ class Damping : public std::tuple::value, void>> - Damping(double d, SimulationTime t, T... shape_args) + Damping(FP d, SimulationTime t, T... shape_args) : Damping(d, DampingLevel(0), DampingType(0), t, shape_args...) { } @@ -141,13 +144,13 @@ class Damping : public std::tuple& get_time() { - return std::get(*this); + return std::get>(*this); } - const SimulationTime& get_time() const + const SimulationTime& get_time() const { - return std::get(*this); + return std::get>(*this); } /** @@ -199,7 +202,7 @@ class Damping : public std::tuple(self) << ',' << std::get(self) << ',' + *os << '[' << std::get>(self) << ',' << std::get(self) << ',' << std::get(self) << ']'; *os << '\n' << std::get(self); } @@ -226,7 +229,7 @@ class Damping : public std::tuple deserialize(IOContext& io) { auto obj = io.expect_object("Damping"); - auto ti = obj.expect_element("Time", Tag{}); + auto ti = obj.expect_element("Time", Tag>{}); auto ty = obj.expect_element("Type", Tag{}); auto l = obj.expect_element("Level", Tag{}); auto c = obj.expect_element("Coeffs", Tag{}); @@ -243,9 +246,10 @@ class Damping : public std::tuple +template class Dampings { public: @@ -297,7 +301,7 @@ class Dampings add_(value_type(std::forward(t)...)); } template - void add(double d, T... t) + void add(FP d, T... t) { add_(value_type(d, std::forward(t)..., m_shape)); } @@ -349,24 +353,22 @@ class Dampings * @param t time in the simulation * @return matrix expression */ - auto get_matrix_at(SimulationTime t) const + auto get_matrix_at(SimulationTime t) const { assert(!m_accumulated_dampings_cached.empty() && "Cache is not current. Did you disable the automatic cache update?"); - auto ub = - std::upper_bound(m_accumulated_dampings_cached.begin(), m_accumulated_dampings_cached.end(), - std::make_tuple(t), [](auto&& tup1, auto&& tup2) { - return double(std::get(tup1)) < double(std::get(tup2)); - }); - auto damping = - smoother_cosine(double(t), double(std::get(*ub)) - 1, double(std::get(*ub)), - std::get(*(ub - 1)), std::get(*ub)); + + auto ub = std::upper_bound(m_accumulated_dampings_cached.begin(), m_accumulated_dampings_cached.end(), + std::make_tuple(t), [](auto&& tup1, auto&& tup2) { + return std::get>(tup1) < std::get>(tup2); + }); + + auto damping = smoother_cosine( + t.get(), (std::get>(*ub) - mio::SimulationTime(1.0)).get(), + std::get>(*ub).get(), std::get(*(ub - 1)), std::get(*ub)); + return damping; } - auto get_matrix_at(double t) const - { - return get_matrix_at(SimulationTime(t)); - } /** * access one damping in this collection. @@ -435,7 +437,7 @@ class Dampings { for (auto& d : self.m_dampings) { *os << '\n' - << '[' << std::get(d) << ',' << std::get(d) << ',' + << '[' << std::get>(d) << ',' << std::get(d) << ',' << std::get(d) << ']'; *os << '\n' << std::get(d); } @@ -537,18 +539,18 @@ class Dampings private: std::vector m_dampings; Shape m_shape; - std::vector> m_accumulated_dampings_cached; + std::vector>> m_accumulated_dampings_cached; bool m_automatic_cache_update = true; }; -template -void Dampings::update_cache() +template +void Dampings::update_cache() { using std::get; if (m_accumulated_dampings_cached.empty()) { m_accumulated_dampings_cached.emplace_back(Matrix::Zero(m_shape.rows(), m_shape.cols()), - SimulationTime(std::numeric_limits::lowest())); + SimulationTime(std::numeric_limits::lowest())); std::vector, DampingLevel, DampingType>> active_by_type; std::vector> sum_by_level; @@ -556,26 +558,28 @@ void Dampings::update_cache() //update active damping update_active_dampings(damping, active_by_type, sum_by_level); auto combined_damping = inclusive_exclusive_sum(sum_by_level); - assert((combined_damping.array() <= 1).all() && "unexpected error, accumulated damping out of range."); - if (floating_point_equal(double(get(damping)), - double(get(m_accumulated_dampings_cached.back())), 1e-15, 1e-15)) { - std::get(m_accumulated_dampings_cached.back()) = combined_damping; + assert((combined_damping.array() <= FP(1.0)).all() && + "unexpected error, accumulated damping out of range."); + if (floating_point_equal(get>(damping).get(), + get>(m_accumulated_dampings_cached.back()).get(), 1e-15, + 1e-15)) { + get(m_accumulated_dampings_cached.back()) = combined_damping; } else { - m_accumulated_dampings_cached.emplace_back(combined_damping, get(damping)); + m_accumulated_dampings_cached.emplace_back(combined_damping, get>(damping)); } } m_accumulated_dampings_cached.emplace_back(get(m_accumulated_dampings_cached.back()), - SimulationTime(std::numeric_limits::max())); + SimulationTime(std::numeric_limits::max())); } } -template -void Dampings::add_(const value_type& damping) +template +void Dampings::add_(const value_type& damping) { assert(damping.get_shape() == m_shape && "Inconsistent matrix shape."); - insert_sorted_replace(m_dampings, damping, [](auto& tup1, auto& tup2) { + insert_sorted_replace(m_dampings, damping, [](auto& tup1, auto& tup2) { return std::make_tuple(tup1.get_time(), int(tup1.get_type()), int(tup1.get_level())) < std::make_tuple(tup2.get_time(), int(tup2.get_type()), int(tup2.get_level())); }); @@ -585,8 +589,8 @@ void Dampings::add_(const value_type& damping) } } -template -void Dampings::update_active_dampings( +template +void Dampings::update_active_dampings( const value_type& damping, std::vector, DampingLevel, DampingType>>& active_by_type, std::vector>& sum_by_level) @@ -632,10 +636,14 @@ void Dampings::update_active_dampings( /** * aliases for common damping specializations. */ -using SquareDamping = Damping; -using SquareDampings = Dampings; -using VectorDamping = Damping; -using VectorDampings = Dampings; +template +using SquareDamping = Damping>; +template +using SquareDampings = Dampings>; +template +using VectorDamping = Damping>; +template +using VectorDampings = Dampings>; } // namespace mio diff --git a/cpp/memilio/epidemiology/damping_sampling.h b/cpp/memilio/epidemiology/damping_sampling.h index 0cad1df97f..864a503aa8 100644 --- a/cpp/memilio/epidemiology/damping_sampling.h +++ b/cpp/memilio/epidemiology/damping_sampling.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -35,12 +35,12 @@ namespace mio * The damping value is weighted by group (e.g. age) to be able to e.g. construct dampings that only * apply to specific groups. */ -template +template class DampingSampling { public: /** - * Creates a DampingSampling. + * Creates a DampingSampling. * @param value random value that all matrix coefficients depend on. * @param level level of the damping. * @param type type of the damping. @@ -49,7 +49,7 @@ class DampingSampling * @param groups weights of age groups. */ template - DampingSampling(const UncertainValue& value, DampingLevel level, DampingType type, SimulationTime time, + DampingSampling(const UncertainValue& value, DampingLevel level, DampingType type, SimulationTime time, const std::vector matrices, const Eigen::MatrixBase& groups) : m_value(value) , m_level(level) @@ -124,7 +124,7 @@ class DampingSampling * Get the time the damping becomes active. * @return the damping time. */ - SimulationTime get_time() const + SimulationTime get_time() const { return m_time; } @@ -133,7 +133,7 @@ class DampingSampling * Set the time the damping becomes active. * @param t the damping time. */ - void set_time(SimulationTime t) + void set_time(SimulationTime t) { m_time = t; } @@ -162,7 +162,7 @@ class DampingSampling * The groups correspond to e.g. age groups in the SECIR model. * @return weights of groups. */ - const Eigen::VectorXd& get_group_weights() const + const Eigen::VectorX& get_group_weights() const { return m_groups; } @@ -186,7 +186,7 @@ class DampingSampling } /** - * equality comparison operators. + * equality comparison operators. * @{ */ bool operator==(const DampingSampling& other) const @@ -201,7 +201,7 @@ class DampingSampling /**@}*/ /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -224,12 +224,12 @@ class DampingSampling static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("DampingSampling"); - auto ti = obj.expect_element("Time", Tag{}); + auto ti = obj.expect_element("Time", Tag>{}); auto ty = obj.expect_element("Type", Tag{}); auto l = obj.expect_element("Level", Tag{}); auto v = obj.expect_element("Value", Tag>{}); auto m = obj.expect_list("MatrixIndices", Tag{}); - auto g = obj.expect_element("GroupWeights", Tag{}); + auto g = obj.expect_element("GroupWeights", Tag>{}); return apply( io, [](auto&& ti_, auto&& ty_, auto&& l_, auto&& v_, auto&& m_, auto&& g_) { @@ -242,9 +242,9 @@ class DampingSampling UncertainValue m_value; DampingLevel m_level; DampingType m_type; - SimulationTime m_time; + SimulationTime m_time; std::vector m_matrices; - Eigen::VectorXd m_groups; + Eigen::VectorX m_groups; }; /** @@ -260,7 +260,7 @@ void apply_dampings(DampingExpression& damping_expression, const DampingSampling damping_expression.set_automatic_cache_update(false); for (auto& d : dampings) { for (auto& i : d.get_matrix_indices()) { - auto m = make_matrix(double(d.get_value()) * d.get_group_weights()); + auto m = make_matrix(d.get_value().value() * d.get_group_weights()); damping_expression[i].add_damping(m, d.get_level(), d.get_type(), d.get_time()); } } @@ -273,9 +273,9 @@ void apply_dampings(DampingExpression& damping_expression, const DampingSampling * d_ij = 1 - sqrt((1 - g_i) * (1 - g_j)) * where d_ij is a coefficient of the matrix * and g_i,g_j are coefficients of the group vector. - * For diagonal elements (i.e. contacts of group with itself): d_ii = g_i; - * the damping of the corresponding group is applied directly. - * For off diagonal elements (i.e. contacts of group with other group): d_ij between g_i and g_j; + * For diagonal elements (i.e. contacts of group with itself): d_ii = g_i; + * the damping of the corresponding group is applied directly. + * For off diagonal elements (i.e. contacts of group with other group): d_ij between g_i and g_j; * the dampings of both groups are combined and applied equally. * @param groups damping value weighted by group. * @return square matrix expression of damping coefficients. @@ -297,10 +297,10 @@ auto make_contact_damping_matrix(V&& groups) * @param groups damping value weighted by group. * @return vector expression of mobility coefficient damping. */ -template -auto make_mobility_damping_vector(ColumnVectorShape shape, V&& groups) +template +auto make_mobility_damping_vector(ColumnVectorShape shape, V&& groups) { - return Eigen::VectorXd::NullaryExpr(shape.size(), [shape, groups = std::forward(groups)](Eigen::Index i) { + return Eigen::VectorX::NullaryExpr(shape.size(), [shape, groups = std::forward(groups)](Eigen::Index i) { auto num_groups = groups.size(); auto num_compartments = size_t(shape.size()) / num_groups; return groups[size_t(i) / num_compartments]; diff --git a/cpp/memilio/epidemiology/dynamic_npis.h b/cpp/memilio/epidemiology/dynamic_npis.h index b59c6d9292..8525c8e15b 100644 --- a/cpp/memilio/epidemiology/dynamic_npis.h +++ b/cpp/memilio/epidemiology/dynamic_npis.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Martin J. Kuehn, Daniel Abele @@ -32,7 +32,7 @@ namespace mio * represents non-pharmaceutical interventions (NPI) that are activated during the simulation if * some value (e.g. infections) exceeds specified thresholds. */ -template +template class DynamicNPIs { public: @@ -41,7 +41,7 @@ class DynamicNPIs * @param threshold the threshold that may be exceeded. * @param dampings the NPIs */ - void set_threshold(double threshold, const std::vector>& dampings) + void set_threshold(FP threshold, const std::vector>& dampings) { insert_sorted_replace(m_thresholds, std::make_pair(threshold, dampings), [](auto& t1, auto& t2) { return t1.first > t2.first; @@ -54,7 +54,7 @@ class DynamicNPIs * @return iterator (see get_thresholds()) to the exceeded threshold if found. * get_thresholds().end() otherwise */ - auto get_max_exceeded_threshold(double value) + auto get_max_exceeded_threshold(FP value) { //thresholds are sorted by value descending, so upper_bound returns the first threshold that is smaller using binary search auto iter_max_exceeded_threshold = @@ -84,7 +84,7 @@ class DynamicNPIs * get the duration of the NPIs. * @return the duration of the NPIs. */ - SimulationTime get_duration() const + SimulationTime get_duration() const { return m_duration; } @@ -93,7 +93,7 @@ class DynamicNPIs * set the duration of the NPIs. * @param v the duration of the NPIs. */ - void set_duration(SimulationTime v) + void set_duration(SimulationTime v) { m_duration = v; } @@ -105,14 +105,14 @@ class DynamicNPIs /** * @return the interval at which the NPIs are checked. */ - SimulationTime get_interval() const + SimulationTime get_interval() const { return m_interval; } /** * @param interval The interval at which the NPIs are checked. */ - void set_interval(SimulationTime interval) + void set_interval(SimulationTime interval) { m_interval = interval; } @@ -121,20 +121,20 @@ class DynamicNPIs /** * Get/Set the base value of the thresholds. * The base value determines the unit of the threshold values. - * E.g. If the base value is X, the thresholds should be interpreted as cases per X people. + * E.g. If the base value is X, the thresholds should be interpreted as cases per X people. * @{ */ /** * @return The base value of the thresholds. */ - double get_base_value() const + FP get_base_value() const { return m_base; } /** * @return The base value of the thresholds. */ - void set_base_value(double v) + void set_base_value(FP v) { m_base = v; } @@ -153,7 +153,7 @@ class DynamicNPIs } /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -174,10 +174,10 @@ class DynamicNPIs static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("DynamicNPIs"); - auto t = obj.expect_list("Thresholds", Tag>>>{}); - auto d = obj.expect_element("Duration", Tag{}); - auto i = obj.expect_element("Interval", Tag{}); - auto b = obj.expect_element("BaseValue", Tag{}); + auto t = obj.expect_list("Thresholds", Tag>>>{}); + auto d = obj.expect_element("Duration", Tag>{}); + auto i = obj.expect_element("Interval", Tag>{}); + auto b = obj.expect_element("BaseValue", Tag{}); return apply( io, [](auto&& t_, auto&& d_, auto&& i_, auto&& b_) { @@ -194,16 +194,16 @@ class DynamicNPIs } private: - std::vector>>> m_thresholds; - SimulationTime m_duration{14.0}; - SimulationTime m_interval{3.0}; - double m_base{1.0}; + std::vector>>> m_thresholds; + SimulationTime m_duration{14.0}; + SimulationTime m_interval{3.0}; + FP m_base{1.0}; }; /** * Get a list of indices of specified dampings. - * Returns the indices of dampings that match the given type and level and that become active in the specified - * time span (excluding the particular interval boundaries, begin and end). + * Returns the indices of dampings that match the given type and level and that become active in the specified + * time span (excluding the particular interval boundaries, begin and end). * Utility for implementation of dynamic NPIs. * @param damping_expr some matrix expression that contains dampings, e.g. a ContactMatrix. * @param lvl damping level to match @@ -212,15 +212,14 @@ class DynamicNPIs * @param end end of the time span that contains the dampings. * @return list of indices in range damping_expr.get_dampings() */ -template +template std::vector get_damping_indices(const DampingExpr& damping_expr, DampingLevel lvl, DampingType type, - SimulationTime begin, SimulationTime end) + SimulationTime begin, SimulationTime end) { std::vector indices; for (size_t i = 0; i < damping_expr.get_dampings().size(); ++i) { const auto d = damping_expr.get_dampings()[i]; - if (d.get_level() == lvl && d.get_type() == type && double(d.get_time()) > double(begin) && - double(d.get_time()) < double(end)) { + if (d.get_level() == lvl && d.get_type() == type && d.get_time() > begin && d.get_time() < end) { indices.push_back(i); } } @@ -238,13 +237,13 @@ std::vector get_damping_indices(const DampingExpr& damping_expr, Damping * @return matrix of damping coefficients if active damping is found. * zero matrix otherwise. */ -template +template Eigen::Ref get_active_damping(const DampingExpr& damping_expr, DampingLevel lvl, - DampingType type, SimulationTime t) + DampingType type, SimulationTime t) { auto ub = std::find_if(damping_expr.get_dampings().rbegin(), damping_expr.get_dampings().rend(), [lvl, type, t](auto& d) { - return d.get_level() == lvl && d.get_type() == type && double(d.get_time()) <= double(t); + return d.get_level() == lvl && d.get_type() == type && d.get_time() <= t; }); if (ub != damping_expr.get_dampings().rend()) { return ub->get_coeffs(); @@ -256,13 +255,13 @@ Eigen::Ref get_active_damping(const DampingE * implement dynamic NPIs for a time span. * Adds or removes dampings to ensure that the active dampings during the specified * time span is at least as big as the specified dynamic dampings. - * If another damping of the same type and level is active at the beginning of the time span or + * If another damping of the same type and level is active at the beginning of the time span or * becomes active during the time span, the coefficient wise maximum of the new damping and the existing damping * is used. * At the end of the time span, another set of dampings may be added that restores the dampings on each level and type as they * would have been without the dynamic npis that have just been implemented. * Examples: - * a) no damping exists yet, dynamic npi of value `d`: + * a) no damping exists yet, dynamic npi of value `d`: * one damping is added at the beginning of the time span that has the value `d`, * another damping is added at the end of the time span that has a value zero. * b) damping of value `a` is active before the beginning of the time span, dynamic npi of value `d` is added: @@ -270,7 +269,7 @@ Eigen::Ref get_active_damping(const DampingE * another damping is added at the end of the time span that has the value a * b) damping of value `a` becomes active at a time `t_a` between the beginning of the time span and the end, dynamic npi of value `d` is added: * one damping is added at the beginning of the time span that has the value `d`, - * the value of the damping at time `t_a` is set to `max(d, a)`, + * the value of the damping at time `t_a` is set to `max(d, a)`, * another damping is added at the end of the time span that has the value a * @param damping_expr_group a group of matrix expressions that contains dampings, e.g. a ContactMatrixGroup. * @param dynamic_npis the NPIs to be implemented @@ -278,9 +277,9 @@ Eigen::Ref get_active_damping(const DampingE * @param end end of the time span that the NPIs will be active for. * @param make_matrix function to make a matrix of the same shape as the damping expression, see e.g. make_contact_damping_matrix */ -template +template void implement_dynamic_npis(DampingExprGroup& damping_expr_group, const std::vector>& npis, - SimulationTime begin, SimulationTime end, MakeMatrix&& make_matrix) + SimulationTime begin, SimulationTime end, MakeMatrix&& make_matrix) { for (auto& npi : npis) { for (auto& mat_idx : npi.get_matrix_indices()) { @@ -288,10 +287,10 @@ void implement_dynamic_npis(DampingExprGroup& damping_expr_group, const std::vec auto level = npi.get_level(); auto& damping_expr = damping_expr_group[mat_idx]; - auto active = get_active_damping(damping_expr, level, type, begin); - auto active_end = - get_active_damping(damping_expr, level, type, end).eval(); //copy because it may be removed or changed - auto value = make_matrix(double(npi.get_value()) * npi.get_group_weights()); + auto active = get_active_damping(damping_expr, level, type, begin); + auto active_end = get_active_damping(damping_expr, level, type, end) + .eval(); //copy because it may be removed or changed + auto value = make_matrix(npi.get_value().value() * npi.get_group_weights()); auto npi_implemented = false; @@ -302,7 +301,7 @@ void implement_dynamic_npis(DampingExprGroup& damping_expr_group, const std::vec } //replace dampings during the new npi - auto damping_indices = get_damping_indices(damping_expr, level, type, begin, end); + auto damping_indices = get_damping_indices(damping_expr, level, type, begin, end); for (auto& i : damping_indices) { auto& d = damping_expr.get_dampings()[i]; damping_expr.add_damping(max(d.get_coeffs(), value), level, type, d.get_time()); diff --git a/cpp/memilio/epidemiology/lct_infection_state.h b/cpp/memilio/epidemiology/lct_infection_state.h index fbccebb4db..e6339f10ca 100644 --- a/cpp/memilio/epidemiology/lct_infection_state.h +++ b/cpp/memilio/epidemiology/lct_infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -31,13 +31,13 @@ namespace mio * @brief Provides the functionality to be able to work with subcompartments in an LCT model. * This class just stores the number of subcompartments for each InfectionState and not the number of individuals in - * each subcompartment. + * each subcompartment. * * @tparam InfectionStates An enum class that defines the basic infection states. - * @tparam Ns Number of subcompartments for each infection state defined in InfectionState. + * @tparam Ns Number of subcompartments for each infection state defined in InfectionState. * The number of given template arguments must be equal to the entry Count from InfectionStates. */ -template +template class LctInfectionState { public: @@ -51,7 +51,7 @@ class LctInfectionState /** * @brief Gets the number of subcompartments in an infection state. * - * @tparam State Infection state for which the number of subcompartments should be returned. + * @tparam State Infection state for which the number of subcompartments should be returned. * @return Number of subcompartments for State. Returned value is always at least one. */ template @@ -64,10 +64,10 @@ class LctInfectionState /** * @brief Gets the index of the first subcompartment of an infection state. * - * In a simulation, the number of individuals in the subcompartments are stored in vectors. + * In a simulation, the number of individuals in the subcompartments are stored in vectors. * Accordingly, the index of the first subcompartment of State in such a vector is returned. - * @tparam State: Infection state for which the index should be returned. - * @return Index of the first subcompartment for a vector with one entry per subcompartment. + * @tparam State: Infection state for which the index should be returned. + * @return Index of the first subcompartment for a vector with one entry per subcompartment. * Returned value is always non-negative. */ template @@ -82,19 +82,19 @@ class LctInfectionState } /** - * @brief Cumulates a vector with the number of individuals in each subcompartment (with subcompartments - * according to the LctInfectionState) to produce a Vector that divides the population only into the infection + * @brief Cumulates a vector with the number of individuals in each subcompartment (with subcompartments + * according to the LctInfectionState) to produce a Vector that divides the population only into the infection * states defined in InfectionStates. * - * @param[in] subcompartments Vector with number of individuals in each subcompartment. + * @param[in] subcompartments Vector with number of individuals in each subcompartment. * The size of the vector has to match the LctInfectionState. * @return Vector with accumulated values for the InfectionStates. */ - static Eigen::VectorX calculate_compartments(const Eigen::VectorX& subcompartments) + static Eigen::VectorX calculate_compartments(const Eigen::VectorX& subcompartments) { assert(subcompartments.rows() == Count); - Eigen::VectorX compartments((Eigen::Index)InfectionState::Count); + Eigen::VectorX compartments((Eigen::Index)InfectionState::Count); // Use segment of the vector subcompartments of each InfectionState and sum up the values of subcompartments. compartments[(Eigen::Index)InfectionState::Susceptible] = subcompartments[0]; compartments[(Eigen::Index)InfectionState::Exposed] = diff --git a/cpp/memilio/epidemiology/lct_populations.h b/cpp/memilio/epidemiology/lct_populations.h index 507b72bdc7..6e1c783c4c 100644 --- a/cpp/memilio/epidemiology/lct_populations.h +++ b/cpp/memilio/epidemiology/lct_populations.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -34,25 +34,25 @@ namespace mio /** * @brief A class template for compartment populations of LCT models. * - * Populations can be split up into different categories, e.g. by age group, yearly income group, gender etc. - * In LCT models, we want to use different numbers of subcompartments, i.e. different LctStates, + * Populations can be split up into different categories, e.g. by age group, yearly income group, gender etc. + * In LCT models, we want to use different numbers of subcompartments, i.e. different LctStates, * for each group of a category. * (Therefore, we can't use the normal Populations class because it expects the same InfectionStates for each group.) - * - * This template must be instantiated with one LctState for each group of a category. + * + * This template must be instantiated with one LctState for each group of a category. * The purpose of the LctStates is to define the number of subcompartments for each InfectionState. - * The number of LctStates also determines the number of groups. - * If you want to use more than one category, e.g. age and gender, you have to define num_age_groups * num_genders - * LctStates, because the number of subcompartments can be different + * The number of LctStates also determines the number of groups. + * If you want to use more than one category, e.g. age and gender, you have to define num_age_groups * num_genders + * LctStates, because the number of subcompartments can be different * even for (female, A05-A14) and (female, A80+) or (male, A05-A14). * - * The class created from this template contains a "flat array" of compartment populations and some functions for - * retrieving or setting the populations. The order in the flat array is: First, all (sub-)compartments of the + * The class created from this template contains a "flat array" of compartment populations and some functions for + * retrieving or setting the populations. The order in the flat array is: First, all (sub-)compartments of the * first group, afterwards all (sub-)compartments of the second group and so on. * */ -template +template class LctPopulations { public: @@ -66,7 +66,7 @@ class LctPopulations LctPopulations() { set_count(); - m_y = InternalArrayType::Constant(m_count, 1, 0.); + m_y = InternalArrayType::Constant(m_count, UncertainValue(0.0)); } /** @@ -119,7 +119,7 @@ class LctPopulations } } /** - * @brief Returns an Eigen copy of the vector of populations. + * @brief Returns an Eigen copy of the vector of populations. * This can be used as initial conditions for the ODE solver. * @return Eigen::VectorXd of populations. */ @@ -152,11 +152,11 @@ class LctPopulations } /** - * @brief Checks whether all compartments have non-negative values. - * This function can be used to prevent slightly negative function values in compartment sizes that are produced + * @brief Checks whether all compartments have non-negative values. + * This function can be used to prevent slightly negative function values in compartment sizes that are produced * due to rounding errors if, e.g., population sizes were computed in a complex way. * - * Attention: This function should be used with care. It can not and will not set model parameters and + * 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. @@ -167,7 +167,7 @@ class LctPopulations { bool corrected = false; for (int i = 0; i < m_y.array().size(); i++) { - if (m_y.array()[i] < 0) { + if (m_y.array()[i] < 0.0) { log_warning("Constraint check: Compartment size {:d} changed from {:.4f} to {:d}", i, m_y.array()[i], 0); m_y.array()[i] = 0.; @@ -183,7 +183,7 @@ class LctPopulations */ bool check_constraints() const { - if ((m_y.array() < 0.).any()) { + if ((m_y.array() < 0.0).any()) { log_error("Constraint check: At least one compartment size is smaller {}.", 0); return true; } diff --git a/cpp/memilio/epidemiology/populations.h b/cpp/memilio/epidemiology/populations.h index 1ff0dd527c..5c0f918633 100644 --- a/cpp/memilio/epidemiology/populations.h +++ b/cpp/memilio/epidemiology/populations.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Jan Kleinert, Daniel Abele @@ -24,6 +24,7 @@ #include "memilio/utils/uncertain_value.h" #include "memilio/utils/custom_index_array.h" #include "memilio/math/eigen.h" +#include "memilio/math/math_utils.h" #include @@ -48,7 +49,7 @@ namespace mio * */ -template +template class Populations : public CustomIndexArray, Categories...> { public: @@ -77,7 +78,7 @@ class Populations : public CustomIndexArray, Categories...> /** * @brief get_compartments returns an Eigen copy of the vector of populations. This can be used * as initial conditions for the ODE solver - * @return Eigen::VectorXd of populations + * @return Eigen::VectorX of populations */ inline Eigen::VectorX get_compartments() const { @@ -112,10 +113,12 @@ class Populations : public CustomIndexArray, Categories...> * @return total population of the group */ template - ScalarType get_group_total(mio::Index group_idx) const + FP get_group_total(mio::Index group_idx) const { auto const s = this->template slice({(size_t)group_idx, 1}); - return std::accumulate(s.begin(), s.end(), 0.); + return std::accumulate(s.begin(), s.end(), FP(0.0), [](const FP& a, const UncertainValue& b) { + return evaluate_intermediate(a + b); + }); } /** @@ -130,10 +133,11 @@ class Populations : public CustomIndexArray, Categories...> * @param value the new value for the total population */ template - void set_group_total(mio::Index group_idx, ScalarType value) + void set_group_total(mio::Index group_idx, FP value) { - ScalarType current_population = get_group_total(group_idx); - auto s = this->template slice({(size_t)group_idx, 1}); + using std::fabs; + FP current_population = get_group_total(group_idx); + auto s = this->template slice({(size_t)group_idx, 1}); if (fabs(current_population) < 1e-12) { for (auto& v : s) { @@ -151,15 +155,15 @@ class Populations : public CustomIndexArray, Categories...> * @brief get_total returns the total population of all compartments * @return total population */ - ScalarType get_total() const + FP get_total() const { - return this->array().template cast().sum(); + return this->array().template cast().sum(); } /** * @brief set_difference_from_group_total sets the total population for a given group from a difference * - * This function sets the population size + * This function sets the population size * * @param total_population the new value for the total population * @param indices The indices of the compartment @@ -167,12 +171,12 @@ class Populations : public CustomIndexArray, Categories...> * @param group_idx The enum of the group within the category */ template - void set_difference_from_group_total(Index const& midx, ScalarType total_group_population) + void set_difference_from_group_total(Index const& midx, FP total_group_population) { - auto group_idx = mio::get(midx); - ScalarType current_population = get_group_total(group_idx); - size_t idx = this->get_flat_index(midx); + auto group_idx = mio::get(midx); + FP current_population = get_group_total(group_idx); + size_t idx = this->get_flat_index(midx); current_population -= this->array()[idx]; assert(current_population <= total_group_population + 1e-10); @@ -185,15 +189,16 @@ class Populations : public CustomIndexArray, Categories...> * * This function rescales all the compartments populations proportionally. If all compartments * have zero population, the total population gets distributed equally over all - * compartments. + * compartments. * * @param value the new value for the total population */ - void set_total(ScalarType value) + void set_total(FP value) { - double current_population = get_total(); + using std::fabs; + FP current_population = get_total(); if (fabs(current_population) < 1e-12) { - double ysize = double(this->array().size()); + FP ysize = static_cast(this->array().size()); for (size_t i = 0; i < this->get_num_compartments(); i++) { this->array()[(Eigen::Index)i] = value / ysize; } @@ -212,10 +217,10 @@ class Populations : public CustomIndexArray, Categories...> * @param indices the index of the compartment * @param total_population the new value for the total population */ - void set_difference_from_total(Index midx, double total_population) + void set_difference_from_total(Index midx, FP total_population) { - double current_population = get_total(); - size_t idx = this->get_flat_index(midx); + FP current_population = get_total(); + size_t idx = this->get_flat_index(midx); current_population -= this->array()[idx]; assert(current_population <= total_population); @@ -224,11 +229,11 @@ class Populations : public CustomIndexArray, Categories...> } /** - * @brief Checks whether all compartments have non-negative values. + * @brief Checks whether all compartments have non-negative values. * 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 can not and will not set model parameters and + * 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. @@ -239,10 +244,10 @@ class Populations : public CustomIndexArray, Categories...> { bool corrected = false; for (int i = 0; i < this->array().size(); i++) { - if (this->array()[i] < 0) { + if (this->array()[i] < 0.0) { log_warning("Constraint check: Compartment size {:d} changed from {:.4f} to {:d}", i, this->array()[i], 0); - this->array()[i] = 0; + this->array()[i] = 0.0; corrected = true; } } @@ -257,7 +262,7 @@ class Populations : public CustomIndexArray, Categories...> { for (int i = 0; i < this->array().size(); i++) { FP value = this->array()[i]; - if (value < 0.) { + if (value < 0.0) { log_error("Constraint check: Compartment size {} is {} and smaller {}", i, value, 0); return true; } diff --git a/cpp/memilio/epidemiology/state_age_function.h b/cpp/memilio/epidemiology/state_age_function.h index 684986e2e7..6387a60110 100644 --- a/cpp/memilio/epidemiology/state_age_function.h +++ b/cpp/memilio/epidemiology/state_age_function.h @@ -76,6 +76,7 @@ namespace mio * * See ExponentialSurvivalFunction, SmootherCosine and ConstantFunction for examples of derived classes. */ +template struct StateAgeFunction { /** @@ -85,16 +86,16 @@ struct StateAgeFunction { * @param[in] init_location A parameter to shift the function. * @param[in] init_scale A parameter to scale the function. Parameter has to be positive. */ - StateAgeFunction(ScalarType init_distribution_parameter, ScalarType init_location = 0, ScalarType init_scale = 1) + StateAgeFunction(FP init_distribution_parameter, FP init_location = 0, FP init_scale = 1) : m_distribution_parameter{init_distribution_parameter} , m_location{init_location} , m_scale{init_scale} - , m_mean{-1.} // Initialize mean as not set. - , m_support_max{-1.} // Initialize support maximum as not set. + , m_mean{-1.0} // Initialize mean as not set. + , m_support_max{-1.0} // Initialize support maximum as not set. { if (m_scale <= 0) { log_error("The scale parameter of a StateAgeFunction has to be positive. Set scale to 1."); - m_scale = 1.; + m_scale = 1.0; } } @@ -106,27 +107,27 @@ struct StateAgeFunction { /** * @brief Copy constructor. */ - StateAgeFunction(const StateAgeFunction& other) = default; + StateAgeFunction(const StateAgeFunction& other) = default; /** * @brief Move constructor. */ - StateAgeFunction(StateAgeFunction&& other) = default; + StateAgeFunction(StateAgeFunction&& other) = default; /** * @brief Copy assignment operator. */ - StateAgeFunction& operator=(const StateAgeFunction& other) = default; + StateAgeFunction& operator=(const StateAgeFunction& other) = default; /** * @brief Move assignment operator. */ - StateAgeFunction& operator=(StateAgeFunction&& other) = default; + StateAgeFunction& operator=(StateAgeFunction&& other) = default; /** * @brief Comparison operator. */ - bool operator==(const StateAgeFunction& other) const + bool operator==(const StateAgeFunction& other) const { return (typeid(*this).name() == typeid(other).name() && m_distribution_parameter == other.get_distribution_parameter() && m_location == other.get_location() && @@ -140,7 +141,7 @@ struct StateAgeFunction { * * @param[in] state_age Time at which the function is evaluated. */ - virtual ScalarType eval(ScalarType state_age) = 0; + virtual FP eval(FP state_age) = 0; /** * @brief Get the m_distribution_parameter object. @@ -149,7 +150,7 @@ struct StateAgeFunction { * * @return ScalarType */ - ScalarType get_distribution_parameter() const + FP get_distribution_parameter() const { return m_distribution_parameter; } @@ -166,7 +167,7 @@ struct StateAgeFunction { * *@param[in] new_distribution_parameter New parameter for StateAgeFunction. */ - void set_distribution_parameter(ScalarType new_distribution_parameter) + void set_distribution_parameter(FP new_distribution_parameter) { m_distribution_parameter = new_distribution_parameter; m_support_max = -1.; @@ -180,7 +181,7 @@ struct StateAgeFunction { * * @return ScalarType */ - ScalarType get_location() const + FP get_location() const { return m_location; } @@ -197,7 +198,7 @@ struct StateAgeFunction { * *@param[in] new_location New location for StateAgeFunction. */ - void set_location(ScalarType new_location) + void set_location(FP new_location) { m_location = new_location; m_support_max = -1.; @@ -211,7 +212,7 @@ struct StateAgeFunction { * * @return ScalarType */ - ScalarType get_scale() const + FP get_scale() const { return m_scale; } @@ -228,7 +229,7 @@ struct StateAgeFunction { * *@param[in] new_scale New Scale for StateAgeFunction. */ - void set_scale(ScalarType new_scale) + void set_scale(FP new_scale) { if (new_scale <= 0) { log_error("The scale parameter of a StateAgeFunction has to be positive. Set scale to 1."); @@ -253,11 +254,12 @@ struct StateAgeFunction { * @param[in] tol Tolerance used for cutting the support if the function value falls below. * @return ScalarType support_max */ - virtual ScalarType get_support_max(ScalarType dt, ScalarType tol = 1e-10) + virtual FP get_support_max(FP dt, FP tol = 1e-10) { - ScalarType support_max = 0.; + FP support_max = 0.0; - if (!floating_point_equal(m_support_tol, tol, 1e-14) || floating_point_equal(m_support_max, -1., 1e-14)) { + if (!floating_point_equal(m_support_tol, tol, 1e-14) || + floating_point_equal(m_support_max, -1.0, 1e-14)) { while (eval(support_max) >= tol) { support_max += dt; } @@ -284,14 +286,15 @@ struct StateAgeFunction { * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance. * @return ScalarType mean value. */ - virtual ScalarType get_mean(ScalarType dt = 1., ScalarType tol = 1e-10) + virtual FP get_mean(FP dt = 1.0, FP tol = 1e-10) { - if (!floating_point_equal(m_mean_tol, tol, 1e-14) || floating_point_equal(m_mean, -1., 1e-14)) { + using std::ceil; + if (!floating_point_equal(m_mean_tol, tol, 1e-14) || floating_point_equal(m_mean, -1., 1e-14)) { // Integration using Trapezoidal rule. - ScalarType mean = 0.5 * dt * eval(0 * dt); - ScalarType supp_max_idx = std::ceil(get_support_max(dt, tol) / dt); + FP mean = 0.5 * dt * eval(FP(0 * dt)); + FP supp_max_idx = ceil(get_support_max(dt, tol) / dt); for (int i = 1; i < supp_max_idx; i++) { - mean += dt * eval(i * dt); + mean += dt * eval(FP(i * dt)); } m_mean = mean; @@ -317,24 +320,24 @@ struct StateAgeFunction { * * @return std::unique_ptr unique pointer to a StateAgeFunction */ - std::unique_ptr clone() const + std::unique_ptr> clone() const { - return std::unique_ptr(clone_impl()); + return std::unique_ptr>(clone_impl()); } protected: /** * @brief Pure virtual method that implements cloning. */ - virtual StateAgeFunction* clone_impl() const = 0; + virtual StateAgeFunction* clone_impl() const = 0; - ScalarType m_distribution_parameter; ///< Parameter for function in derived class. - ScalarType m_location; ///< Location parameter for function in derived class. - ScalarType m_scale; ///< Scale parameter for function in derived class. - ScalarType m_mean; ///< Mean value of the function. - ScalarType m_mean_tol{-1}; ///< Tolerance for computation of the mean (initialize as not set). - ScalarType m_support_max; ///< Maximum of the support of the function. - ScalarType m_support_tol{-1}; ///< Tolerance for computation of the support (initialize as not set). + FP m_distribution_parameter; ///< Parameter for function in derived class. + FP m_location; ///< Location parameter for function in derived class. + FP m_scale; ///< Scale parameter for function in derived class. + FP m_mean; ///< Mean value of the function. + FP m_mean_tol{-1.0}; ///< Tolerance for computation of the mean (initialize as not set). + FP m_support_max; ///< Maximum of the support of the function. + FP m_support_tol{-1.0}; ///< Tolerance for computation of the support (initialize as not set). }; /************************************** @@ -344,7 +347,9 @@ struct StateAgeFunction { /** * @brief Class that defines the survival function corresponding to the exponential distribution depending on the state age. */ -struct ExponentialSurvivalFunction : public StateAgeFunction { + +template +struct ExponentialSurvivalFunction : public StateAgeFunction { /** * @brief Constructs a new ExponentialSurvivalFunction object. @@ -353,8 +358,8 @@ struct ExponentialSurvivalFunction : public StateAgeFunction { * @param[in] init_location Location parameter to shift the ExponentialSurvivalFunction function. * Should be a positive number to fulfill characteristics of a TransitionDistribution. */ - ExponentialSurvivalFunction(ScalarType init_distribution_parameter, ScalarType init_location = 0) - : StateAgeFunction(init_distribution_parameter, init_location) + ExponentialSurvivalFunction(FP init_distribution_parameter, FP init_location = 0) + : StateAgeFunction(init_distribution_parameter, init_location) { } @@ -366,12 +371,13 @@ struct ExponentialSurvivalFunction : public StateAgeFunction { * @param[in] state_age Time at which the function is evaluated. * @return Evaluation of the function at state_age. */ - ScalarType eval(ScalarType state_age) override + FP eval(FP state_age) override { - if (state_age <= m_location) { + using std::exp; + if (state_age <= this->m_location) { return 1; } - return std::exp(-m_distribution_parameter * (state_age - m_location)); + return exp(-this->m_distribution_parameter * (state_age - this->m_location)); } /** @@ -384,11 +390,11 @@ struct ExponentialSurvivalFunction : public StateAgeFunction { * (unused for ExponentialSurvivalFunction). * @return ScalarType mean value. */ - ScalarType get_mean(ScalarType dt = 1., ScalarType tol = 1e-10) override + FP get_mean(FP dt = 1.0, FP tol = 1e-10) override { unused(dt); unused(tol); - return 1. / m_distribution_parameter + m_location; + return 1.0 / this->m_distribution_parameter + this->m_location; } protected: @@ -397,9 +403,9 @@ struct ExponentialSurvivalFunction : public StateAgeFunction { * * @return Pointer to StateAgeFunction. */ - StateAgeFunction* clone_impl() const override + StateAgeFunction* clone_impl() const override { - return new ExponentialSurvivalFunction(*this); + return new ExponentialSurvivalFunction(*this); } }; @@ -407,82 +413,83 @@ struct ExponentialSurvivalFunction : public StateAgeFunction { * @brief Class that defines an smoother_cosine function depending on the state age. * This function is a StateAgeFunction of type a) and can therefore be seen as a survival function. */ -struct SmootherCosine : public StateAgeFunction { +template +struct SmootherCosine : public StateAgeFunction { /** * @brief Constructs a new SmootherCosine object. * * This function is a StateAgeFunction of type a) and can therefore be seen as a survival function. - * + * * @param[in] init_distribution_parameter specifies the initial parameter of the function. - * @param[in] init_location Location paramter to shift the SmootherCosine function. + * @param[in] init_location Location paramter to shift the SmootherCosine function. * Should be a positive number to fulfill characteristics of a TransitionDistribution. */ - SmootherCosine(ScalarType init_distribution_parameter, ScalarType init_location = 0) - : StateAgeFunction(init_distribution_parameter, init_location) + SmootherCosine(FP init_distribution_parameter, FP init_location = 0) + : StateAgeFunction(init_distribution_parameter, init_location) { } /** * @brief Defines smoother cosine function depending on state_age. * - * Used function goes through points (0+m_location,1) and (m_distribution_parameter+m_location,0) and is + * Used function goes through points (0+m_location,1) and (m_distribution_parameter+m_location,0) and is * interpolated in between using a smoothed cosine function. - * + * * @param[in] state_age Time at which the function is evaluated. - * @return Evaluation of the function at state_age. + * @return Evaluation of the function at state_age. */ - ScalarType eval(ScalarType state_age) override + FP eval(FP state_age) override { - if (state_age <= m_location) { - return 1.; + if (state_age <= this->m_location) { + return 1.0; } - return smoother_cosine(state_age - m_location, 0.0, m_distribution_parameter, 1.0, 0.0); + return smoother_cosine(state_age - this->m_location, 0.0, this->m_distribution_parameter, 1.0, 0.0); } /** - * @brief Computes the maximum of the support of the function. - * + * @brief Computes the maximum of the support of the function. + * * For SmootherCosine, the maximum of the support is equal to the function parameter. * - * @param[in] dt Time step size. - * @param[in] tol Tolerance used for cutting the support if the function value falls below. + * @param[in] dt Time step size. + * @param[in] tol Tolerance used for cutting the support if the function value falls below. * @return ScalarType support_max */ - ScalarType get_support_max(ScalarType dt, ScalarType tol = 1e-10) override + FP get_support_max(FP dt, FP tol = 1e-10) override { unused(dt); unused(tol); - m_support_max = m_distribution_parameter + m_location; - return m_support_max; + this->m_support_max = this->m_distribution_parameter + this->m_location; + return this->m_support_max; } /** - * @brief Computes the mean value of the function. - * + * @brief Computes the mean value of the function. + * * For the associated distribution to SmootherCosine, the mean value is 0.5 * m_distribution_parameter + m_location. * - * @param[in] dt Time step size used for the numerical integration (unused for SmootherCosine). - * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance - * (unused for SmootherCosine). + * @param[in] dt Time step size used for the numerical integration (unused for SmootherCosine). + * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance + * (unused for SmootherCosine). * @return ScalarType mean value. */ - ScalarType get_mean(ScalarType dt = 1., ScalarType tol = 1e-10) override + FP get_mean(FP dt = 1.0, FP tol = 1e-10) override { unused(dt); unused(tol); - return 0.5 * m_distribution_parameter + m_location; + return 0.5 * this->m_distribution_parameter + this->m_location; } protected: /** * @brief Clones unique pointer to a StateAgeFunction. - * + * * @return std::unique_ptr unique pointer to a StateAgeFunction. */ - StateAgeFunction* clone_impl() const override + StateAgeFunction* clone_impl() const override { - return new SmootherCosine(*this); + return new SmootherCosine(*this); } }; @@ -490,68 +497,69 @@ struct SmootherCosine : public StateAgeFunction { * @brief Class that defines an GammaSurvivalFunction function depending on the state age. * A survival function is defined as 1 - cumulative density function. * GammaSurvivalFunction is derived from StateAgeFunction. - * The shape parameter of the Gamma function is the parameter of the StateAgeFunction. + * The shape parameter of the Gamma function is the parameter of the StateAgeFunction. * If shape is an unsigned integer, the Gamma distribution simplifies to an Erlang distribution. + * Does not support automatic differentiation. */ -struct GammaSurvivalFunction : public StateAgeFunction { +struct GammaSurvivalFunction : public StateAgeFunction { /** * @brief Constructs a new GammaSurvivalFunction object. * - * @param[in] init_shape Parameter shape of the GammaSurvivalFunction. + * @param[in] init_shape Parameter shape of the GammaSurvivalFunction. * For the Erlang distribution, shape has to be a positive integer. * Choosing shape = 1 leads to an exponential function with parameter 1/scale. - * @param[in] init_location Location paramter to shift the GammaSurvivalFunction. + * @param[in] init_location Location paramter to shift the GammaSurvivalFunction. * Should be a positive number to fulfill characteristics of a TransitionDistribution. - * @param[in] init_scale Parameter shape of the GammaSurvivalFunction. + * @param[in] init_scale Parameter shape of the GammaSurvivalFunction. * Corresponds to the inverse of the rate parameter of a Gamma distribution. */ GammaSurvivalFunction(ScalarType init_shape = 1, ScalarType init_location = 0, ScalarType init_scale = 1) - : StateAgeFunction(init_shape, init_location, init_scale) + : StateAgeFunction(init_shape, init_location, init_scale) { } /** * @brief Defines GammaSurvivalFunction depending on state_age. - * + * * @param[in] state_age Time at which the function is evaluated. - * @return Evaluation of the function at state_age. + * @return Evaluation of the function at state_age. */ ScalarType eval(ScalarType state_age) override { - if (state_age <= m_location) { + if (state_age <= this->m_location) { return 1; } - boost::math::gamma_distribution> gamma(m_distribution_parameter, - m_scale); - return boost::math::cdf(boost::math::complement(gamma, state_age - m_location)); + boost::math::gamma_distribution> gamma( + this->m_distribution_parameter, this->m_scale); + return boost::math::cdf(boost::math::complement(gamma, state_age - this->m_location)); } /** - * @brief Computes the mean value of the function. - * - * For the gamma distribution, the mean value is m_distribution_parameter*m_scale+m_location, + * @brief Computes the mean value of the function. + * + * For the gamma distribution, the mean value is m_distribution_parameter*m_scale+m_location, * where m_distribution_parameter is the shape parameter. * - * @param[in] dt Time step size used for the numerical integration (unused for GammaSurvivalFunction). - * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance - * (unused for GammaSurvivalFunction). + * @param[in] dt Time step size used for the numerical integration (unused for GammaSurvivalFunction). + * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance + * (unused for GammaSurvivalFunction). * @return ScalarType mean value. */ - ScalarType get_mean(ScalarType dt = 1., ScalarType tol = 1e-10) override + ScalarType get_mean(ScalarType dt = 1.0, ScalarType tol = 1e-10) override { unused(dt); unused(tol); - return m_distribution_parameter * m_scale + m_location; + return this->m_distribution_parameter * this->m_scale + this->m_location; } protected: /** * @brief Implements clone for GammaSurvivalFunction. - * + * * @return Pointer to StateAgeFunction. */ - StateAgeFunction* clone_impl() const override + StateAgeFunction* clone_impl() const override { return new GammaSurvivalFunction(*this); } @@ -560,40 +568,41 @@ struct GammaSurvivalFunction : public StateAgeFunction { /** * @brief Class that defines an LognormSurvivalFunction function depending on the state age. * A survival function is defined as 1 - cumulative density function. + * Does not support automatic differentiation. */ -struct LognormSurvivalFunction : public StateAgeFunction { +struct LognormSurvivalFunction : public StateAgeFunction { /** * @brief Constructs a new LognormSurvivalFunction object. - * + * * Location and scale parameters are according to these parameters in the python package scipy. * * @param[in] init_distribution_parameter Specifies the initial function parameter of the function. * @param[in] init_location Location parameter of LognormSurvivalFunction. The parameter can be - * used to shift the function. Should be non-negative to fulfill the conditions of a + * used to shift the function. Should be non-negative to fulfill the conditions of a * StateAgeFunction. * @param[in] init_scale Scale parameter of LognormSurvivalFunction. */ LognormSurvivalFunction(ScalarType init_distribution_parameter, ScalarType init_location = 0, ScalarType init_scale = 1) - : StateAgeFunction(init_distribution_parameter, init_location, init_scale) + : StateAgeFunction(init_distribution_parameter, init_location, init_scale) { } /** * @brief Defines the value of the LognormSurvivalFunction depending on state_age. - * + * * @param[in] state_age Time at which the function is evaluated. - * @return Evaluation of the function at state_age. + * @return Evaluation of the function at state_age. */ ScalarType eval(ScalarType state_age) override { - if (state_age < m_location) { + if (state_age < this->m_location) { return 1; } - boost::math::lognormal_distribution> logn(0., - m_distribution_parameter); - return boost::math::cdf(boost::math::complement(logn, (state_age - m_location) / m_scale)); + boost::math::lognormal_distribution> logn( + 0.0, this->m_distribution_parameter); + return boost::math::cdf(boost::math::complement(logn, (state_age - this->m_location) / this->m_scale)); } // Closed form for the mean value is kind of complex, use default implementation. @@ -602,10 +611,10 @@ struct LognormSurvivalFunction : public StateAgeFunction { protected: /** * @brief Implements clone for LognormSurvivalFunction. - * + * * @return Pointer to StateAgeFunction. */ - StateAgeFunction* clone_impl() const override + StateAgeFunction* clone_impl() const override { return new LognormSurvivalFunction(*this); } @@ -614,42 +623,43 @@ struct LognormSurvivalFunction : public StateAgeFunction { /** * @brief Class that defines a constant function. */ -struct ConstantFunction : public StateAgeFunction { +template +struct ConstantFunction : public StateAgeFunction { /** * @brief Constructs a new ConstantFunction object. - * + * * @param init_distribution_parameter specifies value of the constant function. */ - ConstantFunction(ScalarType init_distribution_parameter) - : StateAgeFunction(init_distribution_parameter) + ConstantFunction(FP init_distribution_parameter) + : StateAgeFunction(init_distribution_parameter) { } /** * @brief Defines constant function. * - * The function parameter defines the value of the function. + * The function parameter defines the value of the function. * * @param state_age Time at which the function is evaluated. - * @return Evaluation of the function at state_age. + * @return Evaluation of the function at state_age. */ - ScalarType eval(ScalarType state_age) override + FP eval(FP state_age) override { unused(state_age); - return m_distribution_parameter; + return this->m_distribution_parameter; } /** - * @brief Computes the maximum of the support of the function. - * - * For ConstantFunction the maximum of the support would be infinity. This is why we do not want to use it - * as a TransitionDistribution and getting the maximum of the support doe not make sense. + * @brief Computes the maximum of the support of the function. * - * @param[in] dt Time step size. - * @param[in] tol Tolerance used for cutting the support if the function value falls below. + * For ConstantFunction the maximum of the support would be infinity. This is why we do not want to use it + * as a TransitionDistribution and getting the maximum of the support doe not make sense. + * + * @param[in] dt Time step size. + * @param[in] tol Tolerance used for cutting the support if the function value falls below. * @return ScalarType support_max */ - ScalarType get_support_max(ScalarType dt, ScalarType tol = 1e-10) override + FP get_support_max(FP dt, FP tol = 1e-10) override { // In case of a ConstantFunction we would have support_max = infinity // This type of function is not suited to be a TransitionDistribution @@ -657,62 +667,62 @@ struct ConstantFunction : public StateAgeFunction { unused(dt); unused(tol); - m_support_max = -2.; + this->m_support_max = -2.0; log_error("This function is not suited to be a TransitionDistribution. Do not call in case of " "StateAgeFunctions of type b); see documentation of StateAgeFunction Base class."); - return m_support_max; + return this->m_support_max; } /** - * @brief Computes the mean value of the function. - * + * @brief Computes the mean value of the function. + * * For ConstantFunction, the mean value is the function parameter. * - * @param[in] dt Time step size used for the numerical integration (unused for ConstantFunction). - * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance (unused for ConstantFunction). + * @param[in] dt Time step size used for the numerical integration (unused for ConstantFunction). + * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance (unused for ConstantFunction). * @return ScalarType mean value. */ - ScalarType get_mean(ScalarType dt = 1., ScalarType tol = 1e-10) override + FP get_mean(FP dt = 1.0, FP tol = 1e-10) override { unused(dt); unused(tol); log_warning("Attention: This function is not suited to be a TransitionDistribution. Do not call in case of " "StateAgeFunctions of type b); see documentation of StateAgeFunction Base class."); - return m_distribution_parameter; + return this->m_distribution_parameter; } protected: /** * @brief Clones unique pointer to a StateAgeFunction. - * + * * @return std::unique_ptr unique pointer to a StateAgeFunction */ - StateAgeFunction* clone_impl() const override + StateAgeFunction* clone_impl() const override { - return new ConstantFunction(*this); + return new ConstantFunction(*this); } }; /** * @brief Class that defines the probability density function corresponding to the Erlang distribution with the parameters shape and scale depending on the state age. * Class is needed for the initialization of the subcompartments for LCT model. - * ErlangDensity is derived from StateAgeFunction. - * The shape parameter of the Erlang function is the distribution parameter of the StateAgeFunction. + * ErlangDensity is derived from StateAgeFunction. + * The shape parameter of the Erlang function is the distribution parameter of the StateAgeFunction. * Attention: The density is not a survival function and does not have the characteristics of a TransitionDistribution!! * The function is of the StateAgeFunction-Type b). */ -struct ErlangDensity : public StateAgeFunction { +struct ErlangDensity : public StateAgeFunction { /** * @brief Constructs a new ErlangDensity object. - * + * * @param[in] init_shape Parameter shape of the ErlangDensity. For the Erlang distribution, shape has to be a positive integer. * @param[in] init_scale Parameter scale of the ErlangDensity. Corresponds to the inverse rate parameter. */ ErlangDensity(unsigned int init_shape, ScalarType init_scale) - : StateAgeFunction(init_shape, 0., init_scale) + : StateAgeFunction(init_shape, 0.0, init_scale) { } @@ -720,27 +730,29 @@ struct ErlangDensity : public StateAgeFunction { * @brief Defines ErlangDensity depending on state_age. * * Parameters scale and shape are used. - * + * * @param[in] state_age Time at which the function is evaluated. - * @return Evaluation of the function at state_age. + * @return Evaluation of the function at state_age. */ ScalarType eval(ScalarType state_age) override { + using std::exp; + using std::pow; if (state_age < 0) { return 0; } - int shape = (int)m_distribution_parameter; - return std::pow(state_age / m_scale, shape - 1) / (m_scale * boost::math::factorial(shape - 1)) * - std::exp(-state_age / m_scale); + int shape = static_cast(this->m_distribution_parameter); + return pow(state_age / this->m_scale, shape - 1) / + (this->m_scale * boost::math::factorial(shape - 1)) * exp(-state_age / this->m_scale); } /** - * @brief Computes the maximum of the support of the function. - * + * @brief Computes the maximum of the support of the function. + * * Calculates the smallest time value t where function(tau)=0 for all tau>t. * - * @param[in] dt Time step size. - * @param[in] tol Tolerance used for cutting the support if the function value falls below. + * @param[in] dt Time step size. + * @param[in] tol Tolerance used for cutting the support if the function value falls below. * @return ScalarType support_max */ ScalarType get_support_max(ScalarType dt, ScalarType tol = 1e-10) override @@ -748,46 +760,47 @@ struct ErlangDensity : public StateAgeFunction { // We are looking for the smallest time value t where function(tau)=0 for all tau>t. Thus support max is bigger // than the mean. // We use, that the density is monotonically decreasing for tau>mean here. - ScalarType mean = m_distribution_parameter * m_scale; - ScalarType support_max = (ScalarType)dt * (int)(mean / dt); + ScalarType mean = this->m_distribution_parameter * this->m_scale; + ScalarType support_max = dt * static_cast(mean / dt); - if (!floating_point_equal(m_support_tol, tol, 1e-14) || floating_point_equal(m_support_max, -1., 1e-14)) { + if (!floating_point_equal(this->m_support_tol, tol, 1e-14) || + floating_point_equal(this->m_support_max, -1.0, 1e-14)) { while (eval(support_max) >= tol) { support_max += dt; } - m_support_max = support_max; - m_support_tol = tol; + this->m_support_max = support_max; + this->m_support_tol = tol; } - return m_support_max; + return this->m_support_max; } /** - * @brief Computes the mean value of the function. - * - * For the Erlang distribution, the mean value is the m_distribution_parameter * m_scale. + * @brief Computes the mean value of the function. + * + * For the Erlang distribution, the mean value is the m_distribution_parameter * m_scale. * - * @param[in] dt Time step size used for the numerical integration (unused for ErlangDensity). - * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance (unused for ErlangDensity). + * @param[in] dt Time step size used for the numerical integration (unused for ErlangDensity). + * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance (unused for ErlangDensity). * @return ScalarType mean value. */ - ScalarType get_mean(ScalarType dt = 1., ScalarType tol = 1e-10) override + ScalarType get_mean(ScalarType dt = 1.0, ScalarType tol = 1e-10) override { unused(dt); unused(tol); log_warning("Attention: This function is not suited to be a TransitionDistribution. Do not call in case of " "StateAgeFunctions of type b); see documentation of StateAgeFunction Base class."); - return m_distribution_parameter * m_scale; + return this->m_distribution_parameter * this->m_scale; } protected: /** * @brief Implements clone for ErlangDensity. - * + * * @return Pointer to StateAgeFunction. */ - StateAgeFunction* clone_impl() const override + StateAgeFunction* clone_impl() const override { return new ErlangDensity(*this); } @@ -801,8 +814,8 @@ struct ErlangDensity : public StateAgeFunction { * @brief Wrapper around StateAgeFunction so that one can work with an arbitrary StateAgeFunction. * * This way we can define e.g. the parameter TransmissionProbabilityOnContact as type StateAgeFunctionWrapper - * and set it with a specific StateAgeFunction for each example. - * + * and set it with a specific StateAgeFunction for each example. + * * Example from IDE-SECIR model: * * ExponentialSurvivalFunction exponential(1.0); @@ -810,58 +823,59 @@ struct ErlangDensity : public StateAgeFunction { * model.parameters.set(prob); * */ +template struct StateAgeFunctionWrapper { StateAgeFunctionWrapper() - : m_function(mio::SmootherCosine(1.0).clone()) + : m_function(mio::SmootherCosine(1.0).clone()) { } /** * @brief Constructs a new StateAgeFunctionWrapper object - * + * * @param[in] init_function specifies the initial function. */ - StateAgeFunctionWrapper(StateAgeFunction& init_function) + StateAgeFunctionWrapper(StateAgeFunction& init_function) : m_function(init_function.clone()) { } /** - * @brief Copy constructor. + * @brief Copy constructor. */ - StateAgeFunctionWrapper(StateAgeFunctionWrapper const& other) + StateAgeFunctionWrapper(StateAgeFunctionWrapper const& other) : m_function(other.m_function->clone()) { } /** - * @brief Move constructor. + * @brief Move constructor. */ - StateAgeFunctionWrapper(StateAgeFunctionWrapper&& other) = default; + StateAgeFunctionWrapper(StateAgeFunctionWrapper&& other) = default; /** - * @brief Copy assignment. + * @brief Copy assignment. */ - StateAgeFunctionWrapper& operator=(StateAgeFunctionWrapper const& other) + StateAgeFunctionWrapper& operator=(StateAgeFunctionWrapper const& other) { m_function = other.m_function->clone(); return *this; } /** - * @brief Move assignment. + * @brief Move assignment. */ - StateAgeFunctionWrapper& operator=(StateAgeFunctionWrapper&& other) = default; + StateAgeFunctionWrapper& operator=(StateAgeFunctionWrapper&& other) = default; /** - * @brief Destructor. + * @brief Destructor. */ ~StateAgeFunctionWrapper() = default; /** - * @brief Comparison operator. + * @brief Comparison operator. */ - bool operator==(const StateAgeFunctionWrapper& other) const + bool operator==(const StateAgeFunctionWrapper& other) const { return (m_function->get_state_age_function_type() == other.get_state_age_function_type() && m_function->get_distribution_parameter() == other.get_distribution_parameter() && @@ -873,7 +887,7 @@ struct StateAgeFunctionWrapper { * * @param[in] new_function function that we want to set member m_function to. */ - void set_state_age_function(StateAgeFunction& new_function) + void set_state_age_function(StateAgeFunction& new_function) { m_function = new_function.clone(); } @@ -881,7 +895,7 @@ struct StateAgeFunctionWrapper { /** * @brief Get type of StateAgeFunction, i.e. which derived class is used. * - * @return string + * @return string */ std::string get_state_age_function_type() const { @@ -892,98 +906,98 @@ struct StateAgeFunctionWrapper { * @brief Accesses eval of m_function. * * @param[in] state_age Time at which the function is evaluated. - * @return Evaluation of the function at state_age. + * @return Evaluation of the function at state_age. */ - ScalarType eval(ScalarType state_age) const + FP eval(FP state_age) const { return m_function->eval(state_age); } /** * @brief Get the m_distribution_parameter object of m_function. - * - * @return ScalarType + * + * @return ScalarType */ - ScalarType get_distribution_parameter() const + FP get_distribution_parameter() const { return m_function->get_distribution_parameter(); } /** - * @brief Set the m_distribution_parameter object of m_function. - * + * @brief Set the m_distribution_parameter object of m_function. + * * @param[in] new_distribution_parameter New parameter for StateAgeFunction. */ - void set_distribution_parameter(ScalarType new_distribution_parameter) + void set_distribution_parameter(FP new_distribution_parameter) { m_function->set_distribution_parameter(new_distribution_parameter); } /** * @brief Get the m_location object of m_function. - * - * @return ScalarType + * + * @return ScalarType */ - ScalarType get_location() const + FP get_location() const { return m_function->get_location(); } /** - * @brief Set the m_location object of m_function. - * + * @brief Set the m_location object of m_function. + * * @param[in] new_location New location for StateAgeFunction. */ - void set_location(ScalarType new_location) + void set_location(FP new_location) { m_function->set_location(new_location); } /** * @brief Get the m_scale object of m_function. - * - * @return ScalarType + * + * @return ScalarType */ - ScalarType get_scale() const + FP get_scale() const { return m_function->get_scale(); } /** - * @brief Set the m_scale object of m_function. - * + * @brief Set the m_scale object of m_function. + * * @param[in] new_scale New scale for StateAgeFunction. */ - void set_scale(ScalarType new_scale) + void set_scale(FP new_scale) { m_function->set_scale(new_scale); } - /* - * @brief Get the m_support_max object of m_function. - * - * @param[in] dt Time step size at which function will be evaluated. - * @param[in] tol Tolerance used for cutting the support if the function value falls below. + /** + * @brief Get the m_support_max object of m_function. + * + * @param[in] dt Time step size at which function will be evaluated. + * @param[in] tol Tolerance used for cutting the support if the function value falls below. * @return ScalarType m_support_max */ - ScalarType get_support_max(ScalarType dt, ScalarType tol = 1e-10) const + FP get_support_max(FP dt, FP tol = 1e-10) const { return m_function->get_support_max(dt, tol); } /** - * @brief Get the m_mean object of m_function. - * - * @param[in] dt Time step size used for the numerical integration. - * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance. + * @brief Get the m_mean object of m_function. + * + * @param[in] dt Time step size used for the numerical integration. + * @param[in] tol The maximum support used for numerical integration is calculated using this tolerance. * @return ScalarType m_mean */ - ScalarType get_mean(ScalarType dt = 1., ScalarType tol = 1e-10) const + FP get_mean(FP dt = 1.0, FP tol = 1e-10) const { return m_function->get_mean(dt, tol); } private: - std::unique_ptr m_function; ///< Stores StateAgeFunction that is used in Wrapper. + std::unique_ptr> m_function; ///< Stores StateAgeFunction that is used in Wrapper. }; } // namespace mio diff --git a/cpp/memilio/epidemiology/uncertain_matrix.h b/cpp/memilio/epidemiology/uncertain_matrix.h index 91c651794f..6bd8c36482 100644 --- a/cpp/memilio/epidemiology/uncertain_matrix.h +++ b/cpp/memilio/epidemiology/uncertain_matrix.h @@ -39,20 +39,21 @@ namespace mio * that are sampled to modify the contacts at some points in time. * @see UncertainValue */ -template +template class UncertainContactMatrix { public: UncertainContactMatrix(size_t num_matrices = 1, Eigen::Index num_groups = 1) - : UncertainContactMatrix(ContactMatrixGroup(num_matrices, num_groups)) + : UncertainContactMatrix(ContactMatrixGroup(num_matrices, num_groups)) { } - UncertainContactMatrix(const ContactMatrixGroup& cont_freq) + UncertainContactMatrix(const ContactMatrixGroup& cont_freq) : m_cont_freq(cont_freq) , m_dampings() - , m_school_holiday_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {}, - Eigen::VectorXd::Zero(cont_freq.get_num_groups())) + , m_school_holiday_damping(mio::UncertainValue(0.0), mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.0), {}, + Eigen::VectorX::Zero(cont_freq.get_num_groups())) , m_school_holidays() { } @@ -61,7 +62,7 @@ class UncertainContactMatrix * @brief Conversion to const ContactMatrix reference by returning the * ContactMatrix contained in UncertainContactMatrix */ - operator ContactMatrixGroup const &() const + operator ContactMatrixGroup const&() const { return m_cont_freq; } @@ -70,7 +71,7 @@ class UncertainContactMatrix * @brief Conversion to ContactMatrix reference by returning the * ContactMatrix contained in UncertainContactMatrix */ - operator ContactMatrixGroup&() + operator ContactMatrixGroup&() { return m_cont_freq; } @@ -79,7 +80,7 @@ class UncertainContactMatrix * @brief Set an UncertainContactMatrix from a ContactMatrix, * all distributions remain unchanged. */ - UncertainContactMatrix& operator=(const ContactMatrixGroup& cont_freq) + UncertainContactMatrix& operator=(const ContactMatrixGroup& cont_freq) { m_cont_freq = cont_freq; return *this; @@ -89,7 +90,7 @@ class UncertainContactMatrix * @brief Returns the ContactMatrix reference * of the UncertainContactMatrix object */ - ContactMatrixGroup& get_cont_freq_mat() + ContactMatrixGroup& get_cont_freq_mat() { return m_cont_freq; } @@ -98,7 +99,7 @@ class UncertainContactMatrix * @brief Returns the const ContactMatrix reference * of the UncertainContactMatrix object */ - ContactMatrixGroup const& get_cont_freq_mat() const + ContactMatrixGroup const& get_cont_freq_mat() const { return m_cont_freq; } @@ -138,11 +139,11 @@ class UncertainContactMatrix * one period is a pair of start and end dates. * @{ */ - std::vector>& get_school_holidays() + std::vector, SimulationTime>>& get_school_holidays() { return m_school_holidays; } - const std::vector>& get_school_holidays() const + const std::vector, SimulationTime>>& get_school_holidays() const { return m_school_holidays; } @@ -153,7 +154,7 @@ class UncertainContactMatrix * @param accum accumulating current and newly sampled dampings if true; * default: false; removing all previously set dampings */ - ContactMatrixGroup draw_sample(bool accum = false) + ContactMatrixGroup draw_sample(bool accum = false) { draw_sample_dampings(); return make_matrix(accum); @@ -175,7 +176,7 @@ class UncertainContactMatrix * @param accum accumulating current and newly dampings if true; * default: false; removing all previously set dampings */ - ContactMatrixGroup make_matrix(bool accum = false) + ContactMatrixGroup make_matrix(bool accum = false) { if (!accum) { m_cont_freq.clear_dampings(); @@ -222,18 +223,18 @@ class UncertainContactMatrix * @see mio::deserialize */ template - static IOResult deserialize(IOContext& io) + static IOResult> deserialize(IOContext& io) { auto obj = io.expect_object("UncertainContactMatrix"); if (!(io.flags() & IOF_OmitDistributions)) { - auto c = obj.expect_element("ContactMatrix", Tag{}); + auto c = obj.expect_element("ContactMatrix", Tag>{}); auto e = obj.expect_element("SchoolHolidayDamping", Tag>{}); - auto f = obj.expect_list("SchoolHolidays", Tag>{}); + auto f = obj.expect_list("SchoolHolidays", Tag, SimulationTime>>{}); auto d = obj.expect_list("Dampings", Tag>{}); return apply( io, [](auto&& c_, auto&& d_, auto&& e_, auto&& f_) { - auto m = UncertainContactMatrix{c_}; + auto m = UncertainContactMatrix{c_}; m.get_dampings() = d_; m.get_school_holiday_damping() = e_; m.get_school_holidays() = f_; @@ -242,21 +243,21 @@ class UncertainContactMatrix c, d, e, f); } else { - auto c = obj.expect_element("ContactMatrix", Tag{}); + auto c = obj.expect_element("ContactMatrix", Tag>{}); return apply( io, [](auto&& c_) { - return UncertainContactMatrix{c_}; + return UncertainContactMatrix{c_}; }, c); } } private: - ContactMatrixGroup m_cont_freq; + ContactMatrixGroup m_cont_freq; std::vector> m_dampings; DampingSampling m_school_holiday_damping; - std::vector> m_school_holidays; + std::vector, SimulationTime>> m_school_holidays; }; } // namespace mio diff --git a/cpp/memilio/geography/regions.h b/cpp/memilio/geography/regions.h index c8dbeb5978..2ef7d84c80 100644 --- a/cpp/memilio/geography/regions.h +++ b/cpp/memilio/geography/regions.h @@ -47,59 +47,59 @@ struct Region : public mio::Index { }; /** - * Id of a state. - * For Germany the Ids are: - * 1 = Schleswig-Holstein - * 2 = Hamburg - * 3 = Niedersachsen - * 4 = Bremen - * 5 = Nordrhein-Westfalen - * 6 = Hessen - * 7 = Rheinland-Pfalz - * 8 = Baden-Württemberg - * 9 = Bayern - * 10 = Saarland - * 11 = Berlin - * 12 = Brandenburg - * 13 = Mecklenburg-Vorpommern - * 14 = Sachsen - * 15 = Sachsen-Anhalt - * 16 = Thüringen - */ + * Id of a state. + * For Germany the Ids are: + * 1 = Schleswig-Holstein + * 2 = Hamburg + * 3 = Niedersachsen + * 4 = Bremen + * 5 = Nordrhein-Westfalen + * 6 = Hessen + * 7 = Rheinland-Pfalz + * 8 = Baden-Württemberg + * 9 = Bayern + * 10 = Saarland + * 11 = Berlin + * 12 = Brandenburg + * 13 = Mecklenburg-Vorpommern + * 14 = Sachsen + * 15 = Sachsen-Anhalt + * 16 = Thüringen + */ DECL_TYPESAFE(int, StateId); /** - * Id of a county. - * Format ssxxx where ss is the id of the state that the county is in (first s may be 0) and xxx are other digits. - * Ids are generally not consecutive, even within one state. - */ + * Id of a county. + * Format ssxxx where ss is the id of the state that the county is in (first s may be 0) and xxx are other digits. + * Ids are generally not consecutive, even within one state. + */ DECL_TYPESAFE(int, CountyId); DECL_TYPESAFE(int, DistrictId); /** - * get the id of the state that the specified county is in. - * @param[in, out] county a county id. - */ + * get the id of the state that the specified county is in. + * @param[in, out] county a county id. + */ StateId get_state_id(int county); /** - * get the holidays in a german state. - * @param[in] state id of the state. - * @return range of pairs of start and end dates of holiday periods, sorted by start date. - */ + * get the holidays in a german state. + * @param[in] state id of the state. + * @return range of pairs of start and end dates of holiday periods, sorted by start date. + */ Range>::const_iterator, std::vector>::const_iterator>> get_holidays(StateId state); /** - * get the holidays in a german state in a given time period. - * The returned periods may not be completely included in the queried period, - * they may only partially overlap. - * @param[in] state id of the state. - * @param[in] start_date start of the queried period. - * @param[in] end_date end of the queried period. - * @return range of pairs of start and end dates of holiday periods, sorted by start date. - */ + * get the holidays in a german state in a given time period. + * The returned periods may not be completely included in the queried period, + * they may only partially overlap. + * @param[in] state id of the state. + * @param[in] start_date start of the queried period. + * @param[in] end_date end of the queried period. + * @return range of pairs of start and end dates of holiday periods, sorted by start date. + */ Range>::const_iterator, std::vector>::const_iterator>> get_holidays(StateId state, Date start_date, Date end_date); diff --git a/cpp/memilio/io/binary_serializer.cpp b/cpp/memilio/io/binary_serializer.cpp index e238f02015..a89920dc55 100644 --- a/cpp/memilio/io/binary_serializer.cpp +++ b/cpp/memilio/io/binary_serializer.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/io/binary_serializer.h b/cpp/memilio/io/binary_serializer.h index 21f1762ada..6d06950e07 100644 --- a/cpp/memilio/io/binary_serializer.h +++ b/cpp/memilio/io/binary_serializer.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/io/cli.h b/cpp/memilio/io/cli.h index b012e7d222..5f6e17aeb9 100644 --- a/cpp/memilio/io/cli.h +++ b/cpp/memilio/io/cli.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: René Schmieding @@ -520,7 +520,7 @@ IOResult write_parameters_to_file(const Set& parameters, const std::string * @brief Read parameters from the specified file into the given parameter set. * @param[in, out] parameters An instance of Set. * @param[in] filepath The file to read the parameters from. - * @tparam Parameters A list of parameter types. + * @tparam Parameters A list of parameter types. * @tparam Set A parameter set template. Will be used as Set. */ template class Set> @@ -542,7 +542,7 @@ IOResult read_parameters_from_file(Set& parameters, const s /** * @brief A cli that takes json values and stores them in a parameter set. - * + * * Note that the first element of argv will always be skipped, assuming it is the name of the executable * as called from the command line. executable_name simply allows to "clean up" this name, as it may contain a path. * @@ -624,7 +624,7 @@ mio::IOResult command_line_interface(const std::string& executable_name, c /** * @brief A cli that takes json values and stores them in a parameter set. - * + * * Note that the first element of argv will always be skipped, assuming it is the name of the executable * as called from the command line. executable_name simply allows to "clean up" this name, as it may contain a path. * @@ -641,7 +641,7 @@ mio::IOResult command_line_interface(const std::string& executable_name, c * @param[in] argc Argument count, must be the length of argv. Can be directly passed from main. * @param[in] argv Argument list for the Programm. Can be directly passed from main. * @param[in] os Output stream to write to. Default: std::cout. - * @tparam Parameters A list of parameter types. + * @tparam Parameters A list of parameter types. * @tparam Set A parameter set template. Will be used as Set. Default: mio::ParameterSet. * @return An instance of Set if no errors occured, the error code otherwise. */ diff --git a/cpp/memilio/io/epi_data.cpp b/cpp/memilio/io/epi_data.cpp index 24e86a00e0..4b46e8693c 100644 --- a/cpp/memilio/io/epi_data.cpp +++ b/cpp/memilio/io/epi_data.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/io/epi_data.h b/cpp/memilio/io/epi_data.h index 3e4572b870..ac62270809 100644 --- a/cpp/memilio/io/epi_data.h +++ b/cpp/memilio/io/epi_data.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -73,18 +73,18 @@ class StringDate : public Date class ConfirmedCasesNoAgeEntry { public: - double num_confirmed; - double num_recovered; - double num_deaths; + ScalarType num_confirmed; + ScalarType num_recovered; + ScalarType num_deaths; Date date; template static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("ConfirmedCasesNoAgeEntry"); - auto num_confirmed = obj.expect_element("Confirmed", Tag{}); - auto num_recovered = obj.expect_element("Recovered", Tag{}); - auto num_deaths = obj.expect_element("Deaths", Tag{}); + auto num_confirmed = obj.expect_element("Confirmed", Tag{}); + auto num_recovered = obj.expect_element("Recovered", Tag{}); + auto num_deaths = obj.expect_element("Deaths", Tag{}); auto date = obj.expect_element("Date", Tag{}); return apply( io, @@ -128,9 +128,9 @@ class ConfirmedCasesDataEntry public: static std::vector age_group_names; - double num_confirmed; - double num_recovered; - double num_deaths; + ScalarType num_confirmed; + ScalarType num_recovered; + ScalarType num_deaths; Date date; AgeGroup age_group; boost::optional state_id; @@ -141,9 +141,9 @@ class ConfirmedCasesDataEntry static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("ConfirmedCasesDataEntry"); - auto num_confirmed = obj.expect_element("Confirmed", Tag{}); - auto num_recovered = obj.expect_element("Recovered", Tag{}); - auto num_deaths = obj.expect_element("Deaths", Tag{}); + auto num_confirmed = obj.expect_element("Confirmed", Tag{}); + auto num_recovered = obj.expect_element("Recovered", Tag{}); + auto num_deaths = obj.expect_element("Deaths", Tag{}); auto date = obj.expect_element("Date", Tag{}); auto age_group_str = obj.expect_element("Age_RKI", Tag{}); auto state_id = obj.expect_optional("ID_State", Tag{}); @@ -206,7 +206,7 @@ inline IOResult> read_confirmed_cases_data( class DiviEntry { public: - double num_icu; + ScalarType num_icu; Date date; boost::optional state_id; boost::optional county_id; @@ -216,7 +216,7 @@ class DiviEntry static IOResult deserialize(IoContext& io) { auto obj = io.expect_object("DiviEntry"); - auto num_icu = obj.expect_element("ICU", Tag{}); + auto num_icu = obj.expect_element("ICU", Tag{}); auto date = obj.expect_element("Date", Tag{}); auto state_id = obj.expect_optional("ID_State", Tag{}); auto county_id = obj.expect_optional("ID_County", Tag{}); @@ -303,7 +303,7 @@ class PopulationDataEntry public: static std::vector age_group_names; - CustomIndexArray population; + CustomIndexArray population; boost::optional state_id; boost::optional county_id; boost::optional district_id; @@ -315,17 +315,17 @@ class PopulationDataEntry auto state_id = obj.expect_optional("ID_State", Tag{}); auto county_id = obj.expect_optional("ID_County", Tag{}); auto district_id = obj.expect_optional("ID_District", Tag{}); - std::vector> age_groups; + std::vector> age_groups; age_groups.reserve(age_group_names.size()); std::transform(age_group_names.begin(), age_group_names.end(), std::back_inserter(age_groups), [&obj](auto&& age_name) { - return obj.expect_element(age_name, Tag{}); + return obj.expect_element(age_name, Tag{}); }); return apply( io, [](auto&& ag, auto&& sid, auto&& cid, auto&& did) { return PopulationDataEntry{ - CustomIndexArray(AgeGroup(ag.size()), ag.begin(), ag.end()), sid, cid, did}; + CustomIndexArray(AgeGroup(ag.size()), ag.begin(), ag.end()), sid, cid, did}; }, details::unpack_all(age_groups), state_id, county_id, district_id); } @@ -333,11 +333,11 @@ class PopulationDataEntry namespace details { -inline void get_rki_age_interpolation_coefficients(const std::vector& age_ranges, - std::vector>& interpolation, +inline void get_rki_age_interpolation_coefficients(const std::vector& age_ranges, + std::vector>& interpolation, std::vector& carry_over) { - std::array param_ranges = {5., 10., 20., 25., 20., 20.}; + std::array param_ranges = {5., 10., 20., 25., 20., 20.}; assert(param_ranges.size() == ConfirmedCasesDataEntry::age_group_names.size() && "Number of RKI age groups does not match number of age ranges."); @@ -345,7 +345,7 @@ inline void get_rki_age_interpolation_coefficients(const std::vector& ag size_t counter = 0; //residual of param age groups - double res = 0.0; + ScalarType res = 0.0; for (size_t i = 0; i < age_ranges.size(); i++) { // if current param age group didn't fit into previous rki age group, transfer residual to current age group @@ -390,15 +390,15 @@ inline void get_rki_age_interpolation_coefficients(const std::vector& ag inline std::vector interpolate_to_rki_age_groups(const std::vector& population_data) { - std::vector age_ranges = {3., 3., 9., 3., 7., 5., 10., 10., 15., 10., 25.}; - std::vector> coefficients{age_ranges.size()}; + std::vector age_ranges = {3., 3., 9., 3., 7., 5., 10., 10., 15., 10., 25.}; + std::vector> coefficients{age_ranges.size()}; std::vector carry_over{}; get_rki_age_interpolation_coefficients(age_ranges, coefficients, carry_over); std::vector interpolated{population_data}; for (auto region_entry_idx = size_t(0); region_entry_idx < population_data.size(); ++region_entry_idx) { interpolated[region_entry_idx].population = - CustomIndexArray(AgeGroup(ConfirmedCasesDataEntry::age_group_names.size()), 0.0); + CustomIndexArray(AgeGroup(ConfirmedCasesDataEntry::age_group_names.size()), 0.0); size_t interpolated_age_idx = 0; for (size_t age_idx = 0; age_idx < coefficients.size(); age_idx++) { @@ -483,7 +483,7 @@ class VaccinationDataEntry public: static std::vector age_group_names; - double num_vaccinations_partial, num_vaccinations_completed, num_vaccinations_refreshed_first, + ScalarType num_vaccinations_partial, num_vaccinations_completed, num_vaccinations_refreshed_first, num_vaccinations_refreshed_additional; Date date; AgeGroup age_group; @@ -495,10 +495,10 @@ class VaccinationDataEntry static IOResult deserialize(IoContext& io) { auto obj = io.expect_object("VaccinationDataEntry"); - auto num_vaccinations_partial = obj.expect_element("Vacc_partially", Tag{}); - auto num_vaccinations_completed = obj.expect_element("Vacc_completed", Tag{}); - auto num_vaccinations_refreshed_first = obj.expect_optional("Vacc_refreshed", Tag{}); - auto num_vaccinations_refreshed_additional = obj.expect_optional("Vacc_refreshed_2", Tag{}); + auto num_vaccinations_partial = obj.expect_element("Vacc_partially", Tag{}); + auto num_vaccinations_completed = obj.expect_element("Vacc_completed", Tag{}); + auto num_vaccinations_refreshed_first = obj.expect_optional("Vacc_refreshed", Tag{}); + auto num_vaccinations_refreshed_additional = obj.expect_optional("Vacc_refreshed_2", Tag{}); auto date = obj.expect_element("Date", Tag{}); auto age_group_str = obj.expect_element("Age_RKI", Tag{}); auto state_id = obj.expect_optional("ID_County", Tag{}); diff --git a/cpp/memilio/io/hdf5_cpp.h b/cpp/memilio/io/hdf5_cpp.h index e1ff9ccf69..bc87cfe6d3 100644 --- a/cpp/memilio/io/hdf5_cpp.h +++ b/cpp/memilio/io/hdf5_cpp.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/io/history.h b/cpp/memilio/io/history.h index 48ec0f17d0..515370dcce 100644 --- a/cpp/memilio/io/history.h +++ b/cpp/memilio/io/history.h @@ -1,7 +1,7 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * -* Authors: Sascha Korf, Rene Schmieding +* Authors: Sascha Korf, Rene Schmieding * * Contact: Martin J. Kuehn * @@ -50,7 +50,7 @@ struct DataWriterToMemory { } }; -/** +/** * @brief History class that handles writers and loggers. * History provides a function "log" to add a new record and a function "get_log" to access all records. * @@ -101,8 +101,8 @@ class History /** * @brief Get the data object. - * - * @return const WriteWrapper::Data& + * + * @return const WriteWrapper::Data& */ const typename WriteWrapper::Data& get_log() const { diff --git a/cpp/memilio/io/io.h b/cpp/memilio/io/io.h index b04ea79442..fdcf9424d4 100644 --- a/cpp/memilio/io/io.h +++ b/cpp/memilio/io/io.h @@ -108,16 +108,16 @@ class StatusCodeCategory : public std::error_category { public: /** - * name of the status - */ + * name of the status + */ virtual const char* name() const noexcept override final { return "StatusCode"; } /** - * convert enum to string message. - */ + * convert enum to string message. + */ virtual std::string message(int c) const override final { switch (static_cast(c)) { @@ -141,8 +141,8 @@ class StatusCodeCategory : public std::error_category } /** - * convert to standard error code to make it comparable. - */ + * convert to standard error code to make it comparable. + */ virtual std::error_condition default_error_condition(int c) const noexcept override final { switch (static_cast(c)) { diff --git a/cpp/memilio/io/json_serializer.cpp b/cpp/memilio/io/json_serializer.cpp index 0f3ca7b85c..9da6b98be4 100644 --- a/cpp/memilio/io/json_serializer.cpp +++ b/cpp/memilio/io/json_serializer.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/io/json_serializer.h b/cpp/memilio/io/json_serializer.h index b1abde08e4..95a40e2157 100644 --- a/cpp/memilio/io/json_serializer.h +++ b/cpp/memilio/io/json_serializer.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -37,9 +37,9 @@ namespace mio /** * JsonType allows the conversion of basic types for serialization. - * All types that are directly handled by the json serializer inherit - * from std::true_type. They also define the transform functions for - * conversion from and to Json::Value. All others inherit from + * All types that are directly handled by the json serializer inherit + * from std::true_type. They also define the transform functions for + * conversion from and to Json::Value. All others inherit from * std::false_type, external serialization functions need to be available. * @tparam T the type to be serialized. * @{ diff --git a/cpp/memilio/io/mobility_io.cpp b/cpp/memilio/io/mobility_io.cpp index 5786aa949b..cdcffa762a 100644 --- a/cpp/memilio/io/mobility_io.cpp +++ b/cpp/memilio/io/mobility_io.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Wadim Koslow, Henrik Zunker, Martin J. Kuehn @@ -159,7 +159,7 @@ IOResult read_mobility_plain(const std::string& filename) } #ifdef MEMILIO_HAS_HDF5 -IOResult save_edges(const std::vector>>& ensemble_edges, +IOResult save_edges(const std::vector>>& ensemble_edges, const std::vector>& pairs_edges, const fs::path& result_dir, bool save_single_runs, bool save_percentiles) { @@ -203,8 +203,8 @@ IOResult save_edges(const std::vector>>& en return success(); } -IOResult save_edges(const std::vector>& results, const std::vector>& ids, - const std::string& filename) +IOResult save_edges(const std::vector>& results, + const std::vector>& ids, const std::string& filename) { const auto num_edges = results.size(); size_t edge_indx = 0; @@ -237,18 +237,18 @@ IOResult save_edges(const std::vector>& results, const "Failed to create the 'Time' DataSet in group " + h5group_name + " in the file: " + filename); - auto values_t = std::vector(result.get_times().begin(), result.get_times().end()); + auto values_t = std::vector(result.get_times().begin(), result.get_times().end()); MEMILIO_H5_CHECK(H5Dwrite(dset_t.id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, values_t.data()), StatusCode::UnknownError, "Failed to write 'Time' data in group " + h5group_name + " in the file: " + filename); int start_id = ids[edge_indx].first; - auto total = Eigen::Matrix::Zero(num_timepoints, - num_elements) + auto total = Eigen::Matrix::Zero( + num_timepoints, num_elements) .eval(); while (edge_indx < num_edges && ids[edge_indx].first == start_id) { const auto& result_edge = results[edge_indx]; - auto edge_result = Eigen::Matrix::Zero( + auto edge_result = Eigen::Matrix::Zero( num_timepoints, num_elements) .eval(); for (Eigen::Index t_idx = 0; t_idx < result_edge.get_num_time_points(); ++t_idx) { diff --git a/cpp/memilio/io/mobility_io.h b/cpp/memilio/io/mobility_io.h index e7ebb4f045..20ed71f3e9 100644 --- a/cpp/memilio/io/mobility_io.h +++ b/cpp/memilio/io/mobility_io.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Wadim Koslow, Henrik Zunker, Martin J. Kuehn @@ -44,7 +44,7 @@ IOResult count_lines(const std::string& filename); /** * @brief Reads formatted mobility or contact data which is given in columns * from_str to_str from_rs to_rs count_abs - * and separated by tabs. Writes it into a NxN Eigen Matrix, + * and separated by tabs. Writes it into a NxN Eigen Matrix, * where N is the number of regions * @param filename name of file to be read */ @@ -52,7 +52,7 @@ IOResult read_mobility_formatted(const std::string& filename); /** * @brief Reads txt mobility data or contact which is given by values only - * and separated by spaces. Writes it into a NxN Eigen + * and separated by spaces. Writes it into a NxN Eigen * Matrix, where N is the number of regions * @param filename name of file to be read */ @@ -192,10 +192,10 @@ IOResult>> read_graph(const std::string& dir * @param[in] results Simulation results per edge of the graph. * @param[in] ids Identifiers for the start and end node of the edges. * @param[in] filename Name of the file where the results will be saved. - * @return Any io errors that occur during writing of the files. + * @return Any io errors that occur during writing of the files. */ -IOResult save_edges(const std::vector>& results, const std::vector>& ids, - const std::string& filename); +IOResult save_edges(const std::vector>& results, + const std::vector>& ids, const std::string& filename); /** * @brief Saves the results of a simulation for each edge in the graph. @@ -206,7 +206,7 @@ IOResult save_edges(const std::vector>& results, const * @param[in] save_percentiles [Default: true] Defines if percentiles are written. * @return Any io errors that occur during writing of the files. */ -IOResult save_edges(const std::vector>>& ensemble_edges, +IOResult save_edges(const std::vector>>& ensemble_edges, const std::vector>& pairs_edges, const fs::path& result_dir, bool save_single_runs = true, bool save_percentiles = true); diff --git a/cpp/memilio/io/parameters_io.cpp b/cpp/memilio/io/parameters_io.cpp index f9f9f8b3c0..dff30087e7 100644 --- a/cpp/memilio/io/parameters_io.cpp +++ b/cpp/memilio/io/parameters_io.cpp @@ -30,11 +30,11 @@ namespace mio { -IOResult>> read_population_data(const std::vector& population_data, - const std::vector& vregion) +IOResult>> +read_population_data(const std::vector& population_data, const std::vector& vregion) { - std::vector> vnum_population( - vregion.size(), std::vector(ConfirmedCasesDataEntry::age_group_names.size(), 0.0)); + std::vector> vnum_population( + vregion.size(), std::vector(ConfirmedCasesDataEntry::age_group_names.size(), 0.0)); for (auto&& county_entry : population_data) { //accumulate population of states or country from population of counties @@ -62,8 +62,8 @@ IOResult>> read_population_data(const std::vecto return success(vnum_population); } -IOResult>> read_population_data(const std::string& path, - const std::vector& vregion) +IOResult>> read_population_data(const std::string& path, + const std::vector& vregion) { BOOST_OUTCOME_TRY(auto&& population_data, mio::read_population_data(path)); return read_population_data(population_data, vregion); diff --git a/cpp/memilio/io/parameters_io.h b/cpp/memilio/io/parameters_io.h index 3ea00fc73b..9dee5046f2 100644 --- a/cpp/memilio/io/parameters_io.h +++ b/cpp/memilio/io/parameters_io.h @@ -63,7 +63,7 @@ int get_region_id(const EpiDataEntry& data_entry) * * @return An IOResult indicating success or failure. */ -template +template IOResult compute_divi_data(const std::vector& divi_data, const std::vector& vregion, Date date, std::vector& vnum_icu) { @@ -106,7 +106,7 @@ IOResult compute_divi_data(const std::vector& divi_data, const * * @return An IOResult indicating success or failure. */ -template +template IOResult read_divi_data(const std::string& path, const std::vector& vregion, Date date, std::vector& vnum_icu) { @@ -122,8 +122,8 @@ IOResult read_divi_data(const std::string& path, const std::vector& v * @return An IOResult containing a vector of vectors, where each inner vector represents the population * distribution across age groups for a specific region, or an error if the function fails. */ -IOResult>> read_population_data(const std::vector& population_data, - const std::vector& vregion); +IOResult>> +read_population_data(const std::vector& population_data, const std::vector& vregion); /** * @brief Reads population data from census data. @@ -133,8 +133,8 @@ IOResult>> read_population_data(const std::vecto * @return An IOResult containing a vector of vectors, where each inner vector represents the population * distribution across age groups for a specific region, or an error if the function fails. */ -IOResult>> read_population_data(const std::string& path, - const std::vector& vregion); +IOResult>> read_population_data(const std::string& path, + const std::vector& vregion); } // namespace mio diff --git a/cpp/memilio/io/result_io.cpp b/cpp/memilio/io/result_io.cpp index 8bd0fe7592..9fc3aa23b7 100644 --- a/cpp/memilio/io/result_io.cpp +++ b/cpp/memilio/io/result_io.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kuehn @@ -31,8 +31,8 @@ namespace mio { -IOResult save_result(const std::vector>& results, const std::vector& ids, int num_groups, - const std::string& filename) +IOResult save_result(const std::vector>& results, const std::vector& ids, + int num_groups, const std::string& filename) { int region_idx = 0; H5File file{H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)}; @@ -52,16 +52,16 @@ IOResult save_result(const std::vector>& results, const H5DataSet dset_t{H5Dcreate(region_h5group.id, "Time", H5T_NATIVE_DOUBLE, dspace_t.id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)}; MEMILIO_H5_CHECK(dset_t.id, StatusCode::UnknownError, "Time DataSet could not be created (Time)."); - auto values_t = std::vector(result.get_times().begin(), result.get_times().end()); + auto values_t = std::vector(result.get_times().begin(), result.get_times().end()); MEMILIO_H5_CHECK(H5Dwrite(dset_t.id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, values_t.data()), StatusCode::UnknownError, "Time data could not be written."); - auto total = Eigen::Matrix::Zero(num_timepoints, - num_infectionstates) + auto total = Eigen::Matrix::Zero( + num_timepoints, num_infectionstates) .eval(); for (int group_idx = 0; group_idx <= num_groups; ++group_idx) { - auto group = Eigen::Matrix::Zero( + auto group = Eigen::Matrix::Zero( num_timepoints, num_infectionstates) .eval(); if (group_idx < num_groups) { @@ -140,7 +140,7 @@ IOResult> read_result(const std::string& filename) H5Sget_simple_extent_dims(dataspace_t.id, dims_t, NULL); auto num_timepoints = Eigen::Index(dims_t[0]); - auto time = std::vector(num_timepoints); + auto time = std::vector(num_timepoints); MEMILIO_H5_CHECK(H5Dread(dataset_t.id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, time.data()), StatusCode::UnknownError, "Time data could not be read."); @@ -159,19 +159,19 @@ IOResult> read_result(const std::string& filename) } auto num_infectionstates = Eigen::Index(dims_total[1]); - auto total_values = - Eigen::Matrix(num_timepoints, num_infectionstates); + auto total_values = Eigen::Matrix( + num_timepoints, num_infectionstates); MEMILIO_H5_CHECK( H5Dread(dataset_total.id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, total_values.data()), StatusCode::UnknownError, "Totals data could not be read"); - auto totals = TimeSeries(num_infectionstates); + auto totals = TimeSeries(num_infectionstates); totals.reserve(num_timepoints); for (auto t_idx = 0; t_idx < num_timepoints; ++t_idx) { totals.add_time_point(time[t_idx], slice(total_values, {t_idx, 1}, {0, num_infectionstates}).transpose()); } - auto groups = TimeSeries(num_infectionstates * num_groups); + auto groups = TimeSeries(num_infectionstates * num_groups); groups.reserve(num_timepoints); for (Eigen::Index t_idx = 0; t_idx < num_timepoints; ++t_idx) { groups.add_time_point(time[t_idx]); @@ -203,7 +203,7 @@ IOResult> read_result(const std::string& filename) return failure(StatusCode::InvalidFileFormat, "Number of infection states does not match."); } - auto group_values = Eigen::Matrix( + auto group_values = Eigen::Matrix( num_timepoints, num_infectionstates); MEMILIO_H5_CHECK( H5Dread(dataset_values.id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, group_values.data()), diff --git a/cpp/memilio/io/result_io.h b/cpp/memilio/io/result_io.h index a3615b12c5..e325100e48 100644 --- a/cpp/memilio/io/result_io.h +++ b/cpp/memilio/io/result_io.h @@ -43,8 +43,8 @@ namespace mio * @param filename Name of file * @return Any io errors that occur during writing of the files. */ -IOResult save_result(const std::vector>& result, const std::vector& ids, int num_groups, - const std::string& filename); +IOResult save_result(const std::vector>& result, const std::vector& ids, + int num_groups, const std::string& filename); class SimulationResult { @@ -65,7 +65,7 @@ class SimulationResult * @param groups Simulation results of individual groups. * @param total Simulation results as the sum over all groups. */ - SimulationResult(const TimeSeries& groups, const TimeSeries& totals) + SimulationResult(const TimeSeries& groups, const TimeSeries& totals) : m_groups(groups) , m_totals(totals) { @@ -74,7 +74,7 @@ class SimulationResult /** * @brief Simulation results of individual groups. */ - const TimeSeries& get_groups() const + const TimeSeries& get_groups() const { return m_groups; } @@ -82,14 +82,14 @@ class SimulationResult /** * @brief Simulation results of the sum over all groups. */ - const TimeSeries& get_totals() const + const TimeSeries& get_totals() const { return m_totals; } private: - TimeSeries m_groups; - TimeSeries m_totals; + TimeSeries m_groups; + TimeSeries m_totals; }; /** @@ -109,14 +109,15 @@ IOResult> read_result(const std::string& filename) * @return Any io errors that occur during writing of the files. */ template -IOResult save_result_with_params(const std::vector>& result, const std::vector& params, - const std::vector& county_ids, const fs::path& result_dir, size_t run_idx) +IOResult save_result_with_params(const std::vector>& result, + const std::vector& params, const std::vector& county_ids, + const fs::path& result_dir, size_t run_idx) { auto result_dir_run = result_dir / ("run" + std::to_string(run_idx)); BOOST_OUTCOME_TRY(create_directory(result_dir_run.string())); BOOST_OUTCOME_TRY(save_result(result, county_ids, (int)(size_t)params[0].parameters.get_num_groups(), (result_dir_run / "Result.h5").string())); - BOOST_OUTCOME_TRY(write_graph(create_graph_without_edges>(params, county_ids), + BOOST_OUTCOME_TRY(write_graph(create_graph_without_edges>(params, county_ids), result_dir_run.string(), IOF_OmitDistributions)); return success(); } @@ -133,7 +134,7 @@ IOResult save_result_with_params(const std::vector>& re * @return Any io errors that occur during writing of the files. */ template -IOResult save_results(const std::vector>>& ensemble_results, +IOResult save_results(const std::vector>>& ensemble_results, const std::vector>& ensemble_params, const std::vector& county_ids, const fs::path& result_dir, bool save_single_runs = true, bool save_percentiles = true) { @@ -211,7 +212,7 @@ IOResult save_results(const std::vector>>& auto ensemble_params_p95 = ensemble_params_percentile(ensemble_params, 0.95); auto make_graph = [&county_ids](auto&& params) { - return create_graph_without_edges>(params, county_ids); + return create_graph_without_edges>(params, county_ids); }; BOOST_OUTCOME_TRY( write_graph(make_graph(ensemble_params_p05), result_dir_p05.string(), IOF_OmitDistributions)); diff --git a/cpp/memilio/io/serializer_base.cpp b/cpp/memilio/io/serializer_base.cpp index d517ebc347..66b8a4952e 100644 --- a/cpp/memilio/io/serializer_base.cpp +++ b/cpp/memilio/io/serializer_base.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/io/serializer_base.h b/cpp/memilio/io/serializer_base.h index b8e068aa3c..4ca00c741e 100644 --- a/cpp/memilio/io/serializer_base.h +++ b/cpp/memilio/io/serializer_base.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/math/adapt_rk.h b/cpp/memilio/math/adapt_rk.h index 07e80353c7..daf1c3f569 100644 --- a/cpp/memilio/math/adapt_rk.h +++ b/cpp/memilio/math/adapt_rk.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Martin J. Kuehn, Daniel Abele @@ -59,10 +59,11 @@ namespace mio * | 16/135 0 6656/12825 28561/56430 -9/50 2/55 * */ +template class Tableau { public: - std::vector entries; + std::vector> entries; /** * @brief default is Runge-Kutta-Fehlberg4(5) tableau @@ -71,7 +72,7 @@ class Tableau { entries.resize(5); for (size_t i = 0; i < entries.size(); i++) { - entries.at(i).resize(i + 2); + entries[i].resize(i + 2); } entries[0][0] = 0.25; @@ -106,11 +107,12 @@ class Tableau * | 16/135 0 6656/12825 28561/56430 -9/50 2/55 * */ +template class TableauFinal { public: - Eigen::VectorXd entries_low; - Eigen::VectorXd entries_high; + Eigen::VectorX entries_low; + Eigen::VectorX entries_high; /** * @brief default is Runge-Kutta-Fehlberg4(5) tableau @@ -148,7 +150,7 @@ class RKIntegratorCore : public OdeIntegratorCore * @brief Setting up the integrator */ RKIntegratorCore() - : OdeIntegratorCore(std::numeric_limits::min(), std::numeric_limits::max()) + : OdeIntegratorCore(std::numeric_limits::min(), std::numeric_limits::max()) , m_abs_tol(1e-10) , m_rel_tol(1e-5) { @@ -157,48 +159,48 @@ class RKIntegratorCore : public OdeIntegratorCore /** * @brief Set up the integrator * @param abs_tol absolute tolerance - * @param rel_tol relative tolerance + * @param rel_tol relative tolerance * @param dt_min lower bound for time step dt * @param dt_max upper bound for time step dt */ - RKIntegratorCore(const double abs_tol, const double rel_tol, const double dt_min, const double dt_max) + RKIntegratorCore(const FP abs_tol, const FP rel_tol, const FP dt_min, const FP dt_max) : OdeIntegratorCore(dt_min, dt_max) , m_abs_tol(abs_tol) , m_rel_tol(rel_tol) { } - std::unique_ptr> clone() const override + std::unique_ptr> clone() const override { return std::make_unique(*this); } /// @param tol the required absolute tolerance for the comparison with the Fehlberg approximation - void set_abs_tolerance(double tol) + void set_abs_tolerance(FP tol) { m_abs_tol = tol; } /// @param tol the required relative tolerance for the comparison with the Fehlberg approximation - void set_rel_tolerance(double tol) + void set_rel_tolerance(FP tol) { m_rel_tol = tol; } /// @param dt_min sets the minimum step size - void set_dt_min(double dt_min) + void set_dt_min(FP dt_min) { this->get_dt_min() = dt_min; } /// @param dt_max sets the maximum step size - void set_dt_max(double dt_max) + void set_dt_max(FP dt_max) { this->get_dt_max() = dt_max; } // Allow setting different RK tableau schemes - void set_tableaus(const Tableau& tab, const TableauFinal& final_tab) + void set_tableaus(const Tableau& tab, const TableauFinal& final_tab) { m_tab = tab; m_tab_final = final_tab; @@ -212,9 +214,13 @@ class RKIntegratorCore : public OdeIntegratorCore * @param[in,out] dt current time step size h=dt * @param[out] ytp1 approximated value y(t+1) */ - bool step(const DerivFunction& f, Eigen::Ref yt, double& t, double& dt, - Eigen::Ref ytp1) const override + bool step(const DerivFunction& f, Eigen::Ref> yt, FP& t, FP& dt, + Eigen::Ref> ytp1) const override { + using std::max; + using std::min; + using std::pow; + assert(0 <= this->get_dt_min()); assert(this->get_dt_min() <= this->get_dt_max()); @@ -222,11 +228,10 @@ class RKIntegratorCore : public OdeIntegratorCore mio::log_warning("IntegratorCore: Restricting given step size dt = {} to [{}, {}].", dt, this->get_dt_min(), this->get_dt_max()); } + dt = min(dt, this->get_dt_max()); - dt = std::min(dt, this->get_dt_max()); - - double t_eval; // shifted time for evaluating yt - double dt_new; // updated dt + FP t_eval; // shifted time for evaluating yt + FP dt_new; // updated dt bool converged = false; // carry for convergence criterion bool dt_is_invalid = false; @@ -253,7 +258,7 @@ class RKIntegratorCore : public OdeIntegratorCore dt; // t_eval = t + c_i * h // note: line zero of Butcher tableau not stored in array // use ytp1 as temporary storage for evaluating m_kt_values[i] ytp1 = m_yt_eval; - for (Eigen::VectorXd::Index k = 1; k < m_tab.entries[i - 1].size(); k++) { + for (Eigen::Index k = 1; k < m_tab.entries[i - 1].size(); k++) { ytp1 += (dt * m_tab.entries[i - 1][k]) * m_kt_values.col(k - 1); } // get the derivatives, i.e., compute kt_i for all y in ytp1: kt_i = f(t_eval, ytp1_low) @@ -279,22 +284,22 @@ class RKIntegratorCore : public OdeIntegratorCore // compute new value for dt // converged implies eps/error_estimate >= 1, so dt will be increased for the next step // hence !converged implies 0 < eps/error_estimate < 1, strictly decreasing dt - dt_new = dt * std::pow((m_eps / m_error_estimate).minCoeff(), (1. / (m_tab_final.entries_low.size() - 1))); + dt_new = dt * pow((m_eps / m_error_estimate).minCoeff(), (1. / (m_tab_final.entries_low.size() - 1))); // safety factor for more conservative step increases, // and to avoid dt_new -> dt for step decreases when |error_estimate - eps| -> 0 dt_new *= 0.9; // check if updated dt stays within desired bounds and update dt for next step - dt = std::min(dt_new, this->get_dt_max()); + dt = min(dt_new, this->get_dt_max()); } - dt = std::max(dt, this->get_dt_min()); + dt = max(dt, this->get_dt_min()); // return 'converged' in favor of '!dt_is_invalid', as these values only differ if step sizing failed, // but the step with size dt_min was accepted. return converged; } protected: - Tableau m_tab; - TableauFinal m_tab_final; + Tableau m_tab; + TableauFinal m_tab_final; FP m_abs_tol, m_rel_tol; mutable Eigen::Matrix m_kt_values; mutable Eigen::VectorX m_yt_eval; diff --git a/cpp/memilio/math/eigen_util.h b/cpp/memilio/math/eigen_util.h index 78dadc095f..4dca249cd8 100644 --- a/cpp/memilio/math/eigen_util.h +++ b/cpp/memilio/math/eigen_util.h @@ -178,8 +178,9 @@ using is_matrix_expression = std::is_base_of, M>; template auto max(const Eigen::MatrixBase& a, B&& b) { + using std::max; return a.binaryExpr(std::forward(b), [](auto a_i, auto b_i) { - return std::max(a_i, b_i); + return max(a_i, b_i); }); } diff --git a/cpp/memilio/math/floating_point.h b/cpp/memilio/math/floating_point.h index 5ac7b81ae8..3e589395cd 100644 --- a/cpp/memilio/math/floating_point.h +++ b/cpp/memilio/math/floating_point.h @@ -20,6 +20,8 @@ #ifndef MIO_MATH_FLOATING_POINT_H #define MIO_MATH_FLOATING_POINT_H +#include "memilio/config.h" + #include #include #include @@ -33,12 +35,12 @@ namespace mio * @param v2 second number * @return maximum absolute value between v1 and v2 */ -template -T abs_max(T v1, T v2) +template +FP abs_max(FP v1, FP v2) { using std::abs; using std::max; - return max(abs(v1), abs(v2)); + return max(abs(v1), abs(v2)); } /** @@ -51,12 +53,12 @@ T abs_max(T v1, T v2) * @param rel_tol maximum allowed relative difference, default numeric_limits::min. * @return true if v1 is within the specified relative OR absolute tolerance of v2 */ -template -bool floating_point_equal(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_limits::min()) +template +bool floating_point_equal(FP v1, FP v2, FP abs_tol = 0, FP rel_tol = std::numeric_limits::min()) { using std::abs; auto diff = abs(v1 - v2); - return diff <= abs_tol || diff <= abs_max(v1, v2) * rel_tol; + return diff <= abs_tol || diff <= abs_max(v1, v2) * rel_tol; } /** @@ -72,11 +74,11 @@ bool floating_point_equal(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_li * @param rel_tol maximum allowed relative difference for equality, default numeric_limits::min. * @return true if v1 is less than v2 and not within relative or absolute tolerance of v2. */ -template -bool floating_point_less(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_limits::min()) +template +bool floating_point_less(FP v1, FP v2, FP abs_tol = 0, FP rel_tol = std::numeric_limits::min()) { auto diff = v1 - v2; - return diff < -(abs_tol + rel_tol * abs_max(v1, v2)); + return diff < -(abs_tol + rel_tol * abs_max(v1, v2)); } /** @@ -92,10 +94,10 @@ bool floating_point_less(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_lim * @param rel_tol maximum allowed relative difference, default numeric_limits::min. * @return true if v1 is greater than v2 and not within absolute or relative tolerance of v2. */ -template -bool floating_point_greater(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_limits::min()) +template +bool floating_point_greater(FP v1, FP v2, FP abs_tol = 0, FP rel_tol = std::numeric_limits::min()) { - return floating_point_less(v2, v1, abs_tol, rel_tol); + return floating_point_less(v2, v1, abs_tol, rel_tol); } /** @@ -111,10 +113,10 @@ bool floating_point_greater(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_ * @param rel_tol maximum allowed relative difference, default numeric_limits::min. * @return true if v1 is less than v2 or within relative or absolute tolerances of v2. */ -template -bool floating_point_less_equal(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_limits::min()) +template +bool floating_point_less_equal(FP v1, FP v2, FP abs_tol = 0, FP rel_tol = std::numeric_limits::min()) { - return !floating_point_greater(v1, v2, abs_tol, rel_tol); + return !floating_point_greater(v1, v2, abs_tol, rel_tol); } /** @@ -130,10 +132,10 @@ bool floating_point_less_equal(T v1, T v2, T abs_tol = 0, T rel_tol = std::numer * @param rel_tol maximum allowed relative difference, default numeric_limits::min. * @return true if v1 is greater than v2 or within absolute or relative tolerance of v2. */ -template -bool floating_point_greater_equal(T v1, T v2, T abs_tol = 0, T rel_tol = std::numeric_limits::min()) +template +bool floating_point_greater_equal(FP v1, FP v2, FP abs_tol = 0, FP rel_tol = std::numeric_limits::min()) { - return !floating_point_less(v1, v2, abs_tol, rel_tol); + return !floating_point_less(v1, v2, abs_tol, rel_tol); } } // namespace mio diff --git a/cpp/memilio/math/integrator.h b/cpp/memilio/math/integrator.h index f65eba52e2..fbfa9c677d 100755 --- a/cpp/memilio/math/integrator.h +++ b/cpp/memilio/math/integrator.h @@ -60,7 +60,7 @@ class IntegratorCore { } - virtual ~IntegratorCore(){}; + virtual ~IntegratorCore() {}; virtual std::unique_ptr> clone() const = 0; @@ -163,14 +163,13 @@ class SystemIntegrator { } - SystemIntegrator& operator=(const SystemIntegrator& other) + SystemIntegrator& operator=(const SystemIntegrator& other) { - if(this != &other) - { - m_core = other.m_core->clone(); + if (this != &other) { + m_core = other.m_core->clone(); m_is_adaptive = other.m_is_adaptive; } - return *this; + return *this; } /** @@ -185,6 +184,7 @@ class SystemIntegrator Eigen::Ref> advance(const Integrands&... fs, const FP tmax, FP& dt, TimeSeries& results) { // hint at std functions for ADL + using std::ceil; using std::fabs; using std::max; using std::min; @@ -213,7 +213,7 @@ class SystemIntegrator dt_restore = dt; dt = tmax - t; // if necessary, also reduce minimal step size such that we do not step past tmax - m_core->get_dt_min() = min(tmax - t, m_core->get_dt_min()); + m_core->get_dt_min() = min(tmax - t, m_core->get_dt_min()); // if dt_min was reduced, the following step will be the last due to dt == dt_min (see step method) // dt_min must be restored after this loop } @@ -230,7 +230,7 @@ class SystemIntegrator m_core->get_dt_min() = dt_min_restore; // restore dt_min // if dt was decreased to reach tmax in the last time iteration, // we restore it as it is now probably smaller than required for tolerances - dt = max(dt, dt_restore); + dt = max(dt, dt_restore); if (m_is_adaptive) { if (!step_okay) { diff --git a/cpp/memilio/math/interpolation.h b/cpp/memilio/math/interpolation.h index 8bf18aa86f..20d29cbe61 100644 --- a/cpp/memilio/math/interpolation.h +++ b/cpp/memilio/math/interpolation.h @@ -21,6 +21,7 @@ #define MIO_MATH_INTERPOLATION_H_ #include "memilio/utils/logging.h" #include "memilio/utils/time_series.h" +#include "memilio/math/math_utils.h" #include #include @@ -31,7 +32,7 @@ namespace mio /** * @brief Linear interpolation between two data values. - * + * * @param[in] x_eval Location to evaluate interpolation. * @param[in] x_1 Left node of interpolation. * @param[in] x_2 Right node of interpolation. @@ -42,8 +43,8 @@ namespace mio template auto linear_interpolation(const X& x_eval, const X& x_1, const X& x_2, const V& y1, const V& y2) { - const auto weight = (x_eval - x_1) / (x_2 - x_1); - return y1 + weight * (y2 - y1); + const auto weight = evaluate_intermediate((x_eval - x_1) / (x_2 - x_1)); + return evaluate_intermediate(y1 + weight * (y2 - y1)); } /** @@ -95,7 +96,7 @@ Y linear_interpolation_of_data_set(std::vector> vector, const X& // Find the corresponding upper position of the node in the data set size_t upper_pos = std::upper_bound(vector.begin(), vector.end(), x_eval, - [](double value, const std::pair& node) { + [](X value, const std::pair& node) { return value <= node.first; }) - vector.begin(); diff --git a/cpp/memilio/math/math_utils.h b/cpp/memilio/math/math_utils.h index 930a9fc034..72e8e922a6 100644 --- a/cpp/memilio/math/math_utils.h +++ b/cpp/memilio/math/math_utils.h @@ -64,6 +64,27 @@ IOResult map_to_nonnegative(Eigen::Ref> x, const FP tol return success(); } +/** + * @brief Evaluate an intermediate expression to its underlying type, if necessary. + * @param x An intermediate expression, resulting e.g. from `auto x = y + z`. + * @tparam Type Underlying type of the expression, e.g. an AD type. Usually this is set to `FP`. + * @tparam Intermediate Type of the expression result. Do not specify this template, let the compiler deduce it. + * @return Either casts the intermediate expression to Type, or forwards it. + * + * The main purpose of this function is reconcile the handling of intermediate types from Eigen and AD. + * Eigen wants to return intermediates for optimization, while AD values must be evaluated (due to using references). + */ +template +inline auto evaluate_intermediate(Intermediate&& x) +{ + if constexpr (is_ad_type_v) { + return static_cast(x); + } + else { + return x; + } +} + } // namespace mio #endif // MIO_MATH_MATH_UTILS_H diff --git a/cpp/memilio/math/matrix_shape.h b/cpp/memilio/math/matrix_shape.h index b919ce78f4..36e3b57566 100644 --- a/cpp/memilio/math/matrix_shape.h +++ b/cpp/memilio/math/matrix_shape.h @@ -42,11 +42,11 @@ namespace mio * shape of a rectangular matrix. * variable rows and cols. */ +template class RectMatrixShape { public: - using Matrix = Eigen::MatrixXd; - + using Matrix = Eigen::MatrixX; /** * construct the shape of a rectangular matrix. * @param r number of rows. @@ -138,11 +138,11 @@ class RectMatrixShape * shape of a square matrix. * rows() == cols() */ +template class SquareMatrixShape { public: - using Matrix = Eigen::MatrixXd; - + using Matrix = Eigen::MatrixX; /** * construct a square matrix of dimensions r * @param r number of rows and columns @@ -239,12 +239,12 @@ class SquareMatrixShape * shape of a column vector. * cols() == 1. */ +template class ColumnVectorShape { public: - using Matrix = Eigen::VectorXd; - + using Matrix = Eigen::VectorX; /** * construct the shape of a column vector. * @param r number of rows. diff --git a/cpp/memilio/math/smoother.h b/cpp/memilio/math/smoother.h index 85cb66f87c..e794312b4c 100644 --- a/cpp/memilio/math/smoother.h +++ b/cpp/memilio/math/smoother.h @@ -20,8 +20,10 @@ #ifndef EPI_MATH_SMOOTHER_H #define EPI_MATH_SMOOTHER_H +#include "memilio/config.h" #include "memilio/math/eigen.h" #include +#include namespace mio { @@ -38,10 +40,13 @@ namespace mio * @param xright right boundary of independent variable * @param yleft function value at left boundary * @param yright function value at right boundary - * @return double cosine-smoothed evaluation of discrete step function + * @return Floating point cosine-smoothed evaluation of discrete step function */ -inline double smoother_cosine(double x, double xleft, double xright, double yleft, double yright) +template +inline FP smoother_cosine(FP x, FP xleft, FP xright, FP yleft, FP yright) { + using std::cos; + if (x <= xleft) { return yleft; } @@ -49,7 +54,7 @@ inline double smoother_cosine(double x, double xleft, double xright, double ylef return yright; } - return 0.5 * (yleft - yright) * std::cos(3.14159265358979323846 / (xright - xleft) * (x - xleft)) + + return 0.5 * (yleft - yright) * cos(std::numbers::pi_v / (xright - xleft) * (x - xleft)) + 0.5 * (yleft + yright); } @@ -62,12 +67,12 @@ inline double smoother_cosine(double x, double xleft, double xright, double ylef * @param yright matrix expression, function value at right boundary * @return a matrix expression with yij = smoother_cosine(x, xleft, xright, yleftij, yrightij) */ -template -auto smoother_cosine(double x, double xleft, double xright, const Eigen::MatrixBase& yleft_expr, +template +auto smoother_cosine(FP x, FP xleft, FP xright, const Eigen::MatrixBase& yleft_expr, const Eigen::MatrixBase& yright_expr) { return yleft_expr.binaryExpr(yright_expr, [=](auto yleft, auto yright) { - return smoother_cosine(x, xleft, xright, yleft, yright); + return smoother_cosine(x, xleft, xright, yleft, yright); }); } diff --git a/cpp/memilio/math/stepper_wrapper.h b/cpp/memilio/math/stepper_wrapper.h index d92b67b254..356563cf61 100644 --- a/cpp/memilio/math/stepper_wrapper.h +++ b/cpp/memilio/math/stepper_wrapper.h @@ -37,9 +37,8 @@ namespace mio * @brief This is an adaptive IntegratorCore. It creates and manages an instance of a * boost::numeric::odeint::controlled_runge_kutta integrator, wrapped as mio::IntegratorCore. */ -template - class ControlledStepper> +template class ControlledStepper> class ControlledStepperWrapper : public mio::OdeIntegratorCore { using Stepper = boost::numeric::odeint::controlled_runge_kutta< @@ -59,9 +58,8 @@ class ControlledStepperWrapper : public mio::OdeIntegratorCore * @param dt_min lower bound for time step dt * @param dt_max upper bound for time step dt */ - ControlledStepperWrapper(double abs_tol = 1e-10, double rel_tol = 1e-5, - double dt_min = std::numeric_limits::min(), - double dt_max = std::numeric_limits::max()) + ControlledStepperWrapper(FP abs_tol = 1e-10, FP rel_tol = 1e-5, FP dt_min = std::numeric_limits::min(), + FP dt_max = std::numeric_limits::max()) : OdeIntegratorCore(dt_min, dt_max) , m_abs_tol(abs_tol) , m_rel_tol(rel_tol) @@ -69,7 +67,7 @@ class ControlledStepperWrapper : public mio::OdeIntegratorCore { } - std::unique_ptr> clone() const override + std::unique_ptr> clone() const override { return std::make_unique(*this); } @@ -125,7 +123,7 @@ class ControlledStepperWrapper : public mio::OdeIntegratorCore // bound dt from below // the last adaptive step (successful or not) may have calculated a new step size smaller than m_dt_min - dt = max(dt, this->get_dt_min()); + dt = max(dt, this->get_dt_min()); // check whether the last step failed (which means that m_dt_min was still too large to suffice tolerances) if (step_result == fail) { // adaptive stepping failed, but we still return the result of the last attempt @@ -183,9 +181,8 @@ class ControlledStepperWrapper : public mio::OdeIntegratorCore * @brief This is a non-adaptive IntegratorCore. It creates and manages an instance of an explicit stepper from * boost::numeric::odeint, wrapped as mio::IntegratorCore. */ -template - class ExplicitStepper> +template class ExplicitStepper> class ExplicitStepperWrapper : public mio::OdeIntegratorCore { public: @@ -202,7 +199,7 @@ class ExplicitStepperWrapper : public mio::OdeIntegratorCore { } - std::unique_ptr> clone() const override + std::unique_ptr> clone() const override { return std::make_unique(*this); } diff --git a/cpp/memilio/mobility/graph.h b/cpp/memilio/mobility/graph.h index eb8bd35990..26a8bb5a6a 100644 --- a/cpp/memilio/mobility/graph.h +++ b/cpp/memilio/mobility/graph.h @@ -260,14 +260,14 @@ class Graph * @param[in] export_time_series If true, reads data for each day of simulation and writes it in the same directory as the input files. * @param[in] rki_age_groups Specifies whether rki-age_groups should be used. */ -template +template IOResult set_nodes(const Parameters& params, Date start_date, Date end_date, const fs::path& data_dir, const std::string& population_data_path, bool is_node_for_county, Graph& params_graph, ReadFunction&& read_func, - NodeIdFunction&& node_func, const std::vector& scaling_factor_inf, - double scaling_factor_icu, double tnt_capacity_factor, int num_days = 0, - bool export_time_series = false, bool rki_age_groups = true) + NodeIdFunction&& node_func, const std::vector& scaling_factor_inf, FP scaling_factor_icu, + FP tnt_capacity_factor, int num_days = 0, bool export_time_series = false, + bool rki_age_groups = true) { BOOST_OUTCOME_TRY(auto&& node_ids, node_func(population_data_path, is_node_for_county, rki_age_groups)); @@ -299,11 +299,11 @@ IOResult set_nodes(const Parameters& params, Date start_date, Date end_dat auto holiday_periods = regions::get_holidays(regions::get_state_id(id), start_date, end_date); auto& contacts = nodes[node_idx].parameters.template get(); contacts.get_school_holidays() = - std::vector>(holiday_periods.size()); + std::vector, mio::SimulationTime>>(holiday_periods.size()); std::transform( holiday_periods.begin(), holiday_periods.end(), contacts.get_school_holidays().begin(), [=](auto& period) { - return std::make_pair(mio::SimulationTime(mio::get_offset_in_days(period.first, start_date)), - mio::SimulationTime(mio::get_offset_in_days(period.second, start_date))); + return std::make_pair(mio::SimulationTime(mio::get_offset_in_days(period.first, start_date)), + mio::SimulationTime(mio::get_offset_in_days(period.second, start_date))); }); //uncertainty in populations @@ -311,9 +311,9 @@ IOResult set_nodes(const Parameters& params, Date start_date, Date end_dat for (auto j = Index(0); j < Model::Compartments::Count; ++j) { auto& compartment_value = nodes[node_idx].populations[{i, j}]; compartment_value = - UncertainValue(0.5 * (1.1 * double(compartment_value) + 0.9 * double(compartment_value))); - compartment_value.set_distribution(mio::ParameterDistributionUniform(0.9 * double(compartment_value), - 1.1 * double(compartment_value))); + UncertainValue(0.5 * (1.1 * compartment_value.value() + 0.9 * compartment_value.value())); + compartment_value.set_distribution(mio::ParameterDistributionUniform(0.9 * compartment_value.value(), + 1.1 * compartment_value.value())); } } @@ -333,11 +333,11 @@ IOResult set_nodes(const Parameters& params, Date start_date, Date end_dat * @param[in] commuting_weights Vector with a commuting weight for every AgeGroup. * @param[in] indices_of_saved_edges Vector of vectors with indices of the compartments that should be saved on the edges. */ -template IOResult set_edges(const fs::path& mobility_data_file, Graph& params_graph, std::initializer_list& mobile_compartments, size_t contact_locations_size, - ReadFunction&& read_func, std::vector commuting_weights, + ReadFunction&& read_func, std::vector commuting_weights, std::vector> indices_of_saved_edges = {}) { // mobility between nodes @@ -357,7 +357,7 @@ IOResult set_edges(const fs::path& mobility_data_file, Graph(num_age_groups, 1.0) : commuting_weights); + (commuting_weights.size() == 0 ? std::vector(num_age_groups, 1.0) : commuting_weights); //commuters auto working_population = 0.0; for (auto age = AgeGroup(0); age < populations.template size(); ++age) { diff --git a/cpp/memilio/mobility/graph_simulation.h b/cpp/memilio/mobility/graph_simulation.h index b623541b01..33770f8a47 100644 --- a/cpp/memilio/mobility/graph_simulation.h +++ b/cpp/memilio/mobility/graph_simulation.h @@ -86,14 +86,14 @@ class GraphSimulationBase edge_function m_edge_func; }; -template class GraphSimulation : public GraphSimulationBase { using Base = GraphSimulationBase; - using Base::GraphSimulationBase; + using Base::Base; public: void advance(Timepoint t_max = 1.0) @@ -118,22 +118,23 @@ class GraphSimulation : public GraphSimulationBase +template class GraphSimulationStochastic - : public GraphSimulationBase, - std::function> + std::function> { - using Base = GraphSimulationBase, - std::function>; + std::function>; + using node_function = typename Base::node_function; using edge_function = typename Base::edge_function; public: - GraphSimulationStochastic(double t0, double dt, const Graph& g, const node_function& node_func, + GraphSimulationStochastic(FP t0, FP dt, const Graph& g, const node_function& node_func, const edge_function&& edge_func) : Base(t0, dt, g, node_func, std::move(edge_func)) , m_rates(Base::m_graph.edges().size() * @@ -141,15 +142,14 @@ class GraphSimulationStochastic { } - GraphSimulationStochastic(double t0, double dt, Graph&& g, const node_function& node_func, - const edge_function&& edge_func) + GraphSimulationStochastic(FP t0, FP dt, Graph&& g, const node_function& node_func, const edge_function&& edge_func) : Base(t0, dt, std::forward(g), node_func, std::move(edge_func)) , m_rates(Base::m_graph.edges().size() * Base::m_graph.edges()[0].property.get_parameters().get_coefficients().get_shape().rows()) { } - void advance(double t_max) + void advance(FP t_max) { //draw normalized waiting time ScalarType normalized_waiting_time = ExponentialDistribution::get_instance()(m_rng, 1.0); @@ -224,10 +224,10 @@ class GraphSimulationStochastic } private: - ScalarType get_cumulative_transition_rate() + FP get_cumulative_transition_rate() { //compute current cumulative transition rate - ScalarType cumulative_transition_rate = 0; + FP cumulative_transition_rate = 0.0; for (auto& e : Base::m_graph.edges()) { cumulative_transition_rate += e.property.get_transition_rates(Base::m_graph.nodes()[e.start_node_idx].property).sum(); @@ -235,7 +235,7 @@ class GraphSimulationStochastic return cumulative_transition_rate; } - void get_rates(std::vector& rates) + void get_rates(std::vector& rates) { size_t j = 0; for (auto& e : Base::m_graph.edges()) { @@ -243,28 +243,28 @@ class GraphSimulationStochastic for (Eigen::Index i = 0; i < edge_rates.size(); ++i) { const auto compartment_value = Base::m_graph.nodes()[e.start_node_idx].property.get_result().get_last_value()[i]; - rates[j] = (compartment_value < 1.) ? 0. : edge_rates(i); + rates[j] = (compartment_value < 1.0) ? 0.0 : edge_rates(i); j++; } } } - std::vector m_rates; + std::vector m_rates; RandomNumberGenerator m_rng; }; -template +template auto make_graph_sim(Timepoint t0, Timespan dt, Graph&& g, NodeF&& node_func, EdgeF&& edge_func) { - return GraphSimulation, Timepoint, Timespan, EdgeF, NodeF>( + return GraphSimulation, Timepoint, Timespan, EdgeF, NodeF>( t0, dt, std::forward(g), std::forward(node_func), std::forward(edge_func)); } template auto make_graph_sim_stochastic(FP t0, FP dt, Graph&& g, NodeF&& node_func, EdgeF&& edge_func) { - return GraphSimulationStochastic>( + return GraphSimulationStochastic>( t0, dt, std::forward(g), std::forward(node_func), std::forward(edge_func)); } diff --git a/cpp/memilio/mobility/metapopulation_mobility_instant.cpp b/cpp/memilio/mobility/metapopulation_mobility_instant.cpp index b32ca8f980..13cb8b719a 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_instant.cpp +++ b/cpp/memilio/mobility/metapopulation_mobility_instant.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/memilio/mobility/metapopulation_mobility_instant.h b/cpp/memilio/mobility/metapopulation_mobility_instant.h index ebb1984bf6..4a01e1392e 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_instant.h +++ b/cpp/memilio/mobility/metapopulation_mobility_instant.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -42,7 +42,7 @@ namespace mio /** * represents the simulation in one node of the graph. */ -template +template class SimulationNode { public: @@ -82,17 +82,17 @@ class SimulationNode } /**@}*/ - Eigen::Ref get_last_state() const + Eigen::Ref> get_last_state() const { return m_last_state; } - double get_t0() const + FP get_t0() const { return m_t0; } - void advance(double t, double dt) + void advance(FP t, FP dt) { m_simulation.advance(t + dt); m_last_state = m_simulation.get_result().get_last_value(); @@ -100,26 +100,28 @@ class SimulationNode private: Sim m_simulation; - Eigen::VectorXd m_last_state; - double m_t0; + Eigen::VectorX m_last_state; + FP m_t0; }; /** * time dependent mobility coefficients. */ -using MobilityCoefficients = DampingMatrixExpression; +template +using MobilityCoefficients = DampingMatrixExpression>; /** * sum of time dependent mobility coefficients. * differentiate between sources of mobility. */ -using MobilityCoefficientGroup = DampingMatrixExpressionGroup; +template +using MobilityCoefficientGroup = DampingMatrixExpressionGroup>; /** * parameters that influence mobility. * @tparam FP the underlying floating point type, e.g., double */ -template +template class MobilityParameters { public: @@ -127,7 +129,7 @@ class MobilityParameters * constructor from mobility coefficients. * @param coeffs mobility coefficients */ - MobilityParameters(const MobilityCoefficientGroup& coeffs) + MobilityParameters(const MobilityCoefficientGroup& coeffs) : m_coefficients(coeffs) , m_saved_compartment_indices(0) { @@ -137,8 +139,8 @@ class MobilityParameters * constructor from mobility coefficients. * @param coeffs mobility coefficients */ - MobilityParameters(const Eigen::VectorXd& coeffs) - : m_coefficients({MobilityCoefficients(coeffs)}) + MobilityParameters(const Eigen::VectorX& coeffs) + : m_coefficients({MobilityCoefficients(coeffs)}) , m_saved_compartment_indices(0) { } @@ -147,13 +149,13 @@ class MobilityParameters * @brief Constructor for initializing mobility parameters with coefficients from type `MobilityCoefficientGroup` * and specific save indices. * - * @param[in] coeffs A group of mobility coefficients represented by a `MobilityCoefficientGroup` object, defining + * @param[in] coeffs A group of mobility coefficients represented by a `MobilityCoefficientGroup` object, defining * how individuals move between nodes. - * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments. - * Each inner vector represents a set of compartments whose data will be saved in the member `m_mobility_results` + * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments. + * Each inner vector represents a set of compartments whose data will be saved in the member `m_mobility_results` * of the `MobilityEdge` class during the simulation using the `add_mobility_result_time_point` function. */ - MobilityParameters(const MobilityCoefficientGroup& coeffs, const std::vector>& save_indices) + MobilityParameters(const MobilityCoefficientGroup& coeffs, const std::vector>& save_indices) : m_coefficients(coeffs) , m_saved_compartment_indices(save_indices) { @@ -163,18 +165,18 @@ class MobilityParameters * @brief Constructor for initializing mobility parameters with coefficients from an Eigen Vector * and specific save indices. * - * @param[in] coeffs An `Eigen::VectorXd` containing mobility coefficients. - * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments. - * Each inner vector represents a set of compartments whose data will be saved in the member `m_mobility_results` + * @param[in] coeffs An `Eigen::VectorX` containing mobility coefficients. + * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments. + * Each inner vector represents a set of compartments whose data will be saved in the member `m_mobility_results` * of the `MobilityEdge` class during the simulation using the `add_mobility_result_time_point` function. */ - MobilityParameters(const Eigen::VectorXd& coeffs, const std::vector>& save_indices) - : m_coefficients({MobilityCoefficients(coeffs)}) + MobilityParameters(const Eigen::VectorX& coeffs, const std::vector>& save_indices) + : m_coefficients({MobilityCoefficients(coeffs)}) , m_saved_compartment_indices(save_indices) { } - /** + /** * equality comparison operators */ //@{ @@ -190,25 +192,25 @@ class MobilityParameters /** * Get/Setthe mobility coefficients. - * The coefficients represent the (time-dependent) percentage of people moving - * from one node to another by age and infection compartment. + * The coefficients represent the (time-dependent) percentage of people moving + * from one node to another by age and infection compartment. * @{ */ /** * @return the mobility coefficients. */ - const MobilityCoefficientGroup& get_coefficients() const + const MobilityCoefficientGroup& get_coefficients() const { return m_coefficients; } - MobilityCoefficientGroup& get_coefficients() + MobilityCoefficientGroup& get_coefficients() { return m_coefficients; } /** * @param coeffs the mobility coefficients. */ - void set_coefficients(const MobilityCoefficientGroup& coeffs) + void set_coefficients(const MobilityCoefficientGroup& coeffs) { m_coefficients = coeffs; } @@ -216,11 +218,11 @@ class MobilityParameters /** * @brief Get the indices of compartments to be saved during mobility. * - * This function returns a reference to the vector of `m_saved_compartment_indices`, which specifies the groups of - * compartments that are saved in the member `m_mobility_results` of the `MobilityEdge` class during the simulation + * This function returns a reference to the vector of `m_saved_compartment_indices`, which specifies the groups of + * compartments that are saved in the member `m_mobility_results` of the `MobilityEdge` class during the simulation * using the `add_mobility_result_time_point` function. * - * @return A reference to the 2D vector containing indices of compartments to be saved. The outer vector represents different sets of compartments. + * @return A reference to the 2D vector containing indices of compartments to be saved. The outer vector represents different sets of compartments. * Each inner vector represents a group of compartments defined by indices. */ const auto& get_save_indices() const @@ -254,7 +256,7 @@ class MobilityParameters /** @} */ /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -273,7 +275,7 @@ class MobilityParameters static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("MobilityParameters"); - auto c = obj.expect_element("Coefficients", Tag{}); + auto c = obj.expect_element("Coefficients", Tag>{}); auto d = obj.expect_element("DynamicNPIs", Tag>{}); return apply( io, @@ -286,15 +288,15 @@ class MobilityParameters } private: - MobilityCoefficientGroup m_coefficients; //one per group and compartment + MobilityCoefficientGroup m_coefficients; //one per group and compartment DynamicNPIs m_dynamic_npis; std::vector> m_saved_compartment_indices; // groups of indices from compartments to save }; -/** +/** * represents the mobility between two nodes. */ -template +template class MobilityEdge { public: @@ -314,11 +316,11 @@ class MobilityEdge /** * @brief Create edge with coefficients. - * - * @param[in] coeffs An `Eigen::VectorXd` representing the percentage of people in each group and compartment + * + * @param[in] coeffs An `Eigen::VectorX` representing the percentage of people in each group and compartment * that change nodes in each time step. */ - MobilityEdge(const Eigen::VectorXd& coeffs) + MobilityEdge(const Eigen::VectorX& coeffs) : m_parameters(coeffs) , m_mobile_population(coeffs.rows()) , m_return_times(0) @@ -331,9 +333,9 @@ class MobilityEdge /** * @brief Create edge with coefficients as MobilityParameters object and a 2D vector of indices which determine which compartments are saved. * - * @param[in] params A `MobilityParameters` object representing the percentage of people in each group and compartment + * @param[in] params A `MobilityParameters` object representing the percentage of people in each group and compartment * that change nodes in each time step. - * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments. + * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments. * Each inner vector represents a group of indices to be saved. */ MobilityEdge(const MobilityParameters& params, const std::vector>& save_indices) @@ -349,12 +351,12 @@ class MobilityEdge /** * @brief Create edge with coefficients and a 2D vector of indices which determine which compartments are saved. * - * @param[in] coeffs An `Eigen::VectorXd` representing the percentage of people in each group and compartment that migrate + * @param[in] coeffs An `Eigen::VectorX` representing the percentage of people in each group and compartment that migrate * in each time step. - * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments, while each + * @param[in] save_indices A 2D vector of indices. The outer vector represents different sets of compartments, while each * inner vector represents a group of indices for compartments to be saved. */ - MobilityEdge(const Eigen::VectorXd& coeffs, const std::vector>& save_indices) + MobilityEdge(const Eigen::VectorX& coeffs, const std::vector>& save_indices) : m_parameters(coeffs) , m_mobile_population(coeffs.rows()) , m_return_times(0) @@ -378,11 +380,11 @@ class MobilityEdge * @return A reference to the TimeSeries object representing the mobility results. * @{ */ - TimeSeries& get_mobility_results() + TimeSeries& get_mobility_results() { return m_mobility_results; } - const TimeSeries& get_mobility_results() const + const TimeSeries& get_mobility_results() const { return m_mobility_results; } @@ -399,22 +401,22 @@ class MobilityEdge * @param node_to node that people changed to, return from */ template - void apply_mobility(FP t, FP dt, SimulationNode& node_from, SimulationNode& node_to); + void apply_mobility(FP t, FP dt, SimulationNode& node_from, SimulationNode& node_to); private: MobilityParameters m_parameters; TimeSeries m_mobile_population; TimeSeries m_return_times; bool m_return_mobile_population; - FP m_t_last_dynamic_npi_check = -std::numeric_limits::infinity(); - std::pair m_dynamic_npi = {-std::numeric_limits::max(), SimulationTime(0)}; + FP m_t_last_dynamic_npi_check = -std::numeric_limits::infinity(); + std::pair> m_dynamic_npi = {-std::numeric_limits::max(), SimulationTime(0)}; std::vector> m_saved_compartment_indices; // groups of indices from compartments to save TimeSeries m_mobility_results; // save results from edges + entry for the total number of commuters /** * @brief Computes a condensed version of `m_mobile_population` and stores it in `m_mobility_results`. * - * The `m_mobility_results` then only contains commuters with infection states `InfectedNoSymptoms` and + * The `m_mobility_results` then only contains commuters with infection states `InfectedNoSymptoms` and * `InfectedSymptoms`. Additionally, the total number of commuters is stored in the last entry of `m_mobility_results`. * * @param[in] t The current time. @@ -427,17 +429,18 @@ void MobilityEdge::add_mobility_result_time_point(const FP t) { const size_t save_indices_size = this->m_saved_compartment_indices.size(); if (save_indices_size > 0) { + const auto& last_value = m_mobile_population.get_last_value(); - const auto& last_value = m_mobile_population.get_last_value(); - Eigen::VectorXd condensed_values = Eigen::VectorXd::Zero(save_indices_size + 1); + Eigen::VectorX condensed_values = Eigen::VectorX::Zero(save_indices_size + 1); // sum up the values of m_saved_compartment_indices for each group (e.g. Age groups) - std::transform(this->m_saved_compartment_indices.begin(), this->m_saved_compartment_indices.end(), - condensed_values.data(), [&last_value](const auto& indices) { - return std::accumulate(indices.begin(), indices.end(), 0.0, [&last_value](FP sum, auto i) { - return sum + last_value[i]; - }); - }); + for (size_t i = 0; i < save_indices_size; ++i) { + FP sum = 0.0; + for (auto index : this->m_saved_compartment_indices[i]) { + sum += last_value[index]; + } + condensed_values[i] = sum; + } // the last value is the sum of commuters condensed_values[save_indices_size] = m_mobile_population.get_last_value().sum(); @@ -474,92 +477,100 @@ void calculate_mobility_returns(Eigen::Ref::Vector> mobi /** * detect a get_infections_relative function for the Model type. */ -template +template using get_infections_relative_expr_t = decltype(get_infections_relative( - std::declval(), std::declval(), std::declval&>())); + std::declval(), std::declval(), std::declval>&>())); /** * get the percantage of infected people of the total population in the node * If dynamic NPIs are enabled, there needs to be an overload of get_infections_relative(model, y) - * for the Model type that can be found with argument-dependent lookup. Ideally define get_infections_relative + * for the Model type that can be found with argument-dependent lookup. Ideally define get_infections_relative * in the same namespace as the Model type. * @param node a node of a mobility graph. * @param y the current value of the simulation. * @param t the current simulation time */ -template ::value, void*> = nullptr> -double get_infections_relative(const SimulationNode& /*node*/, double /*t*/, - const Eigen::Ref& /*y*/) +FP get_infections_relative(const SimulationNode& /*node*/, FP /*t*/, + const Eigen::Ref>& /*y*/) { assert(false && "Overload get_infections_relative for your own model/simulation if you want to use dynamic NPIs."); return 0; } -template ::value, void*> = nullptr> -double get_infections_relative(const SimulationNode& node, double t, const Eigen::Ref& y) +template ::value, void*> = nullptr> +FP get_infections_relative(const SimulationNode& node, FP t, const Eigen::Ref>& y) { - return get_infections_relative(node.get_simulation(), t, y); + return get_infections_relative(node.get_simulation(), t, y); } /** * detect a get_mobility_factors function for the Model type. */ -template +template using get_mobility_factors_expr_t = decltype(get_mobility_factors( - std::declval(), std::declval(), std::declval&>())); + std::declval(), std::declval(), std::declval>&>())); /** * Get an additional mobility factor. - * The absolute mobility for each compartment is computed by c_i * y_i * f_i, wher c_i is the coefficient set in + * The absolute mobility for each compartment is computed by c_i * y_i * f_i, wher c_i is the coefficient set in * MobilityParameters, y_i is the current compartment population, f_i is the factor returned by this function. - * This factor is optional, default 1.0. If you need to adjust mobility in that way, overload get_mobility_factors(model, t, y) + * This factor is optional, default 1.0. If you need to adjust mobility in that way, overload get_mobility_factors(model, t, y) * for your Model type so that can be found with argument-dependent lookup. * @param node a node of a mobility graph. * @param y the current value of the simulation. * @param t the current simulation time * @return a vector expression, same size as y, with the factor for each compartment. */ -template ::value, void*> = nullptr> -auto get_mobility_factors(const SimulationNode& /*node*/, double /*t*/, const Eigen::Ref& y) +template ::value, void*> = nullptr> +auto get_mobility_factors(const SimulationNode& /*node*/, FP /*t*/, + const Eigen::Ref>& y) { - return Eigen::VectorXd::Ones(y.rows()); + return Eigen::VectorX::Ones(y.rows()); } -template ::value, void*> = nullptr> -auto get_mobility_factors(const SimulationNode& node, double t, const Eigen::Ref& y) +template ::value, void*> = nullptr> +auto get_mobility_factors(const SimulationNode& node, FP t, const Eigen::Ref>& y) { - return get_mobility_factors(node.get_simulation(), t, y); + return get_mobility_factors(node.get_simulation(), t, y); } /** * detect a get_mobility_factors function for the Model type. */ -template +template using test_commuters_expr_t = decltype(test_commuters( - std::declval(), std::declval&>(), std::declval())); + std::declval(), std::declval>&>(), std::declval())); /** * Test persons when moving from their source node. * May transfer persons between compartments, e.g., if an infection was detected. * This feature is optional, default implementation does nothing. - * In order to support this feature for your model, implement a test_commuters overload + * In order to support this feature for your model, implement a test_commuters overload * that can be found with argument-dependent lookup. * @param node a node of a mobility graph. * @param mobile_population mutable reference to vector of persons per compartment that change nodes. * @param t the current simulation time. */ -template ::value, void*> = nullptr> -void test_commuters(SimulationNode& /*node*/, Eigen::Ref /*mobile_population*/, double /*time*/) +template ::value, void*> = nullptr> +void test_commuters(SimulationNode& /*node*/, Eigen::Ref> /*mobile_population*/, + FP /*time*/) { } -template ::value, void*> = nullptr> -void test_commuters(SimulationNode& node, Eigen::Ref mobile_population, double time) +template ::value, void*> = nullptr> +void test_commuters(SimulationNode& node, Eigen::Ref> mobile_population, FP time) { - return test_commuters(node.get_simulation(), mobile_population, time); + return test_commuters(node.get_simulation(), mobile_population, time); } template template -void MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_from, SimulationNode& node_to) +void mio::MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_from, + SimulationNode& node_to) { //check dynamic npis if (m_t_last_dynamic_npi_check == -std::numeric_limits::infinity()) { @@ -568,18 +579,20 @@ void MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_fro auto& dyn_npis = m_parameters.get_dynamic_npis_infected(); if (dyn_npis.get_thresholds().size() > 0 && - floating_point_greater_equal(t, m_t_last_dynamic_npi_check + dyn_npis.get_interval().get())) { - auto inf_rel = get_infections_relative(node_from, t, node_from.get_last_state()) * dyn_npis.get_base_value(); + floating_point_greater_equal(t, m_t_last_dynamic_npi_check + dyn_npis.get_interval().get())) { + auto inf_rel = + get_infections_relative(node_from, t, node_from.get_last_state()) * dyn_npis.get_base_value(); auto exceeded_threshold = dyn_npis.get_max_exceeded_threshold(inf_rel); if (exceeded_threshold != dyn_npis.get_thresholds().end() && (exceeded_threshold->first > m_dynamic_npi.first || t > FP(m_dynamic_npi.second))) { //old NPI was weaker or is expired - auto t_end = SimulationTime(t + FP(dyn_npis.get_duration())); + auto t_end = SimulationTime(t + dyn_npis.get_duration().get()); m_dynamic_npi = std::make_pair(exceeded_threshold->first, t_end); - implement_dynamic_npis( - m_parameters.get_coefficients(), exceeded_threshold->second, SimulationTime(t), t_end, [this](auto& g) { - return make_mobility_damping_vector(m_parameters.get_coefficients().get_shape(), g); - }); + implement_dynamic_npis(m_parameters.get_coefficients(), exceeded_threshold->second, + SimulationTime(t), t_end, [this](auto& g) { + return make_mobility_damping_vector( + m_parameters.get_coefficients().get_shape(), g); + }); } m_t_last_dynamic_npi_check = t; } @@ -587,7 +600,7 @@ void MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_fro //returns for (Eigen::Index i = m_return_times.get_num_time_points() - 1; i >= 0; --i) { if (m_return_times.get_time(i) <= t) { - auto v0 = find_value_reverse(node_to.get_result(), m_mobile_population.get_time(i), 1e-10, 1e-10); + auto v0 = find_value_reverse(node_to.get_result(), m_mobile_population.get_time(i), 1e-10, 1e-10); assert(v0 != node_to.get_result().rend() && "unexpected error."); calculate_mobility_returns(m_mobile_population[i], node_to.get_simulation(), *v0, m_mobile_population.get_time(i), dt); @@ -595,7 +608,7 @@ void MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_fro //the lower-order return calculation may in rare cases produce negative compartments, //especially at the beginning of the simulation. //fix by subtracting the supernumerous returns from the biggest compartment of the age group. - Eigen::VectorXd remaining_after_return = + Eigen::VectorX remaining_after_return = (node_to.get_result().get_last_value() - m_mobile_population[i]).eval(); for (Eigen::Index j = 0; j < node_to.get_result().get_last_value().size(); ++j) { if (remaining_after_return(j) < 0) { @@ -621,15 +634,17 @@ void MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_fro } } - if (!m_return_mobile_population && (m_parameters.get_coefficients().get_matrix_at(t).array() > 0.0).any()) { + if (!m_return_mobile_population && + (m_parameters.get_coefficients().get_matrix_at(SimulationTime(t)).array() > 0.0).any()) { //normal daily mobility m_mobile_population.add_time_point( - t, (node_from.get_last_state().array() * m_parameters.get_coefficients().get_matrix_at(t).array() * - get_mobility_factors(node_from, t, node_from.get_last_state()).array()) + t, (node_from.get_last_state().array() * + m_parameters.get_coefficients().get_matrix_at(SimulationTime(t)).array() * + get_mobility_factors(node_from, t, node_from.get_last_state()).array()) .matrix()); m_return_times.add_time_point(t + dt); - test_commuters(node_from, m_mobile_population.get_last_value(), t); + test_commuters(node_from, m_mobile_population.get_last_value(), t); node_to.get_result().get_last_value() += m_mobile_population.get_last_value(); node_from.get_result().get_last_value() -= m_mobile_population.get_last_value(); @@ -643,8 +658,8 @@ void MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_fro * edge functor for mobility-based simulation. * @see SimulationNode::advance */ -template -void advance_model(double t, double dt, SimulationNode& node) +template +void advance_model(FP t, FP dt, SimulationNode& node) { node.advance(t, dt); } @@ -654,8 +669,8 @@ void advance_model(double t, double dt, SimulationNode& node) * @see MobilityEdge::apply_mobility */ template -void apply_mobility(FP t, FP dt, MobilityEdge& mobilityEdge, SimulationNode& node_from, - SimulationNode& node_to) +void apply_mobility(FP t, FP dt, MobilityEdge& mobilityEdge, SimulationNode& node_from, + SimulationNode& node_to) { mobilityEdge.apply_mobility(t, dt, node_from, node_to); } @@ -663,42 +678,43 @@ void apply_mobility(FP t, FP dt, MobilityEdge& mobilityEdge, SimulationNode< /** * create a mobility-based simulation. * After every second time step, for each edge a portion of the population corresponding to the coefficients of the edge - * changes from one node to the other. In the next timestep, the mobile population returns to their "home" node. - * Returns are adjusted based on the development in the target node. + * changes from one node to the other. In the next timestep, the mobile population returns to their "home" node. + * Returns are adjusted based on the development in the target node. * @param t0 start time of the simulation * @param dt time step between mobility * @param graph set up for mobility-based simulation * @{ */ template -GraphSimulation, MobilityEdge>, FP, FP, - void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, mio::SimulationNode&), - void (*)(FP, FP, mio::SimulationNode&)> -make_mobility_sim(FP t0, FP dt, const Graph, MobilityEdge>& graph) +GraphSimulation, MobilityEdge>, FP, FP, + void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, mio::SimulationNode&), + void (*)(FP, FP, mio::SimulationNode&)> +make_mobility_sim(FP t0, FP dt, const Graph, MobilityEdge>& graph) { - return make_graph_sim(t0, dt, graph, static_cast&)>(&advance_model), - static_cast&, SimulationNode&, SimulationNode&)>( - &apply_mobility)); + return make_graph_sim( + t0, dt, graph, static_cast&)>(&advance_model), + static_cast&, SimulationNode&, SimulationNode&)>( + &apply_mobility)); } template -GraphSimulation, MobilityEdge>, FP, FP, - void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, mio::SimulationNode&), - void (*)(FP, FP, mio::SimulationNode&)> -make_mobility_sim(FP t0, FP dt, Graph, MobilityEdge>&& graph) +GraphSimulation, MobilityEdge>, FP, FP, + void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, mio::SimulationNode&), + void (*)(FP, FP, mio::SimulationNode&)> +make_mobility_sim(FP t0, FP dt, Graph, MobilityEdge>&& graph) { - return make_graph_sim(t0, dt, std::move(graph), - static_cast&)>(&advance_model), - static_cast&, SimulationNode&, SimulationNode&)>( - &apply_mobility)); + return make_graph_sim( + t0, dt, std::move(graph), static_cast&)>(&advance_model), + static_cast&, SimulationNode&, SimulationNode&)>( + &apply_mobility)); } /** @} */ /** * Create a graph simulation without mobility. - * - * Note that we set the time step of the graph simulation to infinity since we do not require any exchange between the + * + * Note that we set the time step of the graph simulation to infinity since we do not require any exchange between the * nodes. Hence, in each node, the simulation runs until tmax when advancing the simulation without interruption. * * @param t0 Start time of the simulation. @@ -706,25 +722,25 @@ make_mobility_sim(FP t0, FP dt, Graph, MobilityEdge>&& g * @{ */ template -auto make_no_mobility_sim(FP t0, Graph, MobilityEdge>& graph) +auto make_no_mobility_sim(FP t0, Graph, MobilityEdge>& graph) { - using GraphSim = - GraphSimulation, MobilityEdge>, FP, FP, - void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, mio::SimulationNode&), - void (*)(FP, FP, mio::SimulationNode&)>; - return GraphSim(t0, std::numeric_limits::infinity(), graph, &advance_model, - [](FP, FP, MobilityEdge&, SimulationNode&, SimulationNode&) {}); + using GraphSim = GraphSimulation, MobilityEdge>, FP, FP, + void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, + mio::SimulationNode&), + void (*)(FP, FP, mio::SimulationNode&)>; + return GraphSim(t0, std::numeric_limits::infinity(), graph, &advance_model, + [](FP, FP, MobilityEdge&, SimulationNode&, SimulationNode&) {}); } template -auto make_no_mobility_sim(FP t0, Graph, MobilityEdge>&& graph) +auto make_no_mobility_sim(FP t0, Graph, MobilityEdge>&& graph) { - using GraphSim = - GraphSimulation, MobilityEdge>, FP, FP, - void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, mio::SimulationNode&), - void (*)(FP, FP, mio::SimulationNode&)>; - return GraphSim(t0, std::numeric_limits::infinity(), std::move(graph), &advance_model, - [](FP, FP, MobilityEdge&, SimulationNode&, SimulationNode&) {}); + using GraphSim = GraphSimulation, MobilityEdge>, FP, FP, + void (*)(FP, FP, mio::MobilityEdge&, mio::SimulationNode&, + mio::SimulationNode&), + void (*)(FP, FP, mio::SimulationNode&)>; + return GraphSim(t0, std::numeric_limits::infinity(), std::move(graph), &advance_model, + [](FP, FP, MobilityEdge&, SimulationNode&, SimulationNode&) {}); } /** @} */ diff --git a/cpp/memilio/mobility/metapopulation_mobility_stochastic.cpp b/cpp/memilio/mobility/metapopulation_mobility_stochastic.cpp index 922db470be..331d3e1bd2 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_stochastic.cpp +++ b/cpp/memilio/mobility/metapopulation_mobility_stochastic.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Julia Bicker diff --git a/cpp/memilio/mobility/metapopulation_mobility_stochastic.h b/cpp/memilio/mobility/metapopulation_mobility_stochastic.h index d63e8d3209..335d933980 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_stochastic.h +++ b/cpp/memilio/mobility/metapopulation_mobility_stochastic.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -37,11 +37,13 @@ namespace mio /** * status and age dependent mobility coefficients. */ -using MobilityCoefficients = DampingMatrixExpression; +template +using MobilityCoefficients = DampingMatrixExpression>; /** * parameters that influence mobility. */ +template class MobilityParametersStochastic { public: @@ -49,7 +51,7 @@ class MobilityParametersStochastic * constructor from mobility coefficients. * @param coeffs mobility coefficients */ - MobilityParametersStochastic(const MobilityCoefficients& coeffs) + MobilityParametersStochastic(const MobilityCoefficients& coeffs) : m_coefficients(coeffs) { } @@ -58,12 +60,12 @@ class MobilityParametersStochastic * constructor from mobility coefficients. * @param coeffs mobility coefficients */ - MobilityParametersStochastic(const Eigen::VectorXd& coeffs) - : m_coefficients(MobilityCoefficients(coeffs)) + MobilityParametersStochastic(const Eigen::VectorX& coeffs) + : m_coefficients(MobilityCoefficients(coeffs)) { } - /** + /** * equality comparison operators */ //@{ @@ -80,30 +82,30 @@ class MobilityParametersStochastic /** * Get/Set the mobility coefficients. * The coefficients represent the rates for moving - * from one node to another by age and infection compartment. + * from one node to another by age and infection compartment. * @{ */ /** * @return the mobility coefficients. */ - const MobilityCoefficients& get_coefficients() const + const MobilityCoefficients& get_coefficients() const { return m_coefficients; } - MobilityCoefficients& get_coefficients() + MobilityCoefficients& get_coefficients() { return m_coefficients; } /** * @param coeffs the mobility coefficients. */ - void set_coefficients(const MobilityCoefficients& coeffs) + void set_coefficients(const MobilityCoefficients& coeffs) { m_coefficients = coeffs; } /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -121,7 +123,7 @@ class MobilityParametersStochastic static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("MobilityParameters"); - auto c = obj.expect_element("Coefficients", Tag{}); + auto c = obj.expect_element("Coefficients", Tag>{}); return apply( io, [](auto&& c_) { @@ -132,12 +134,13 @@ class MobilityParametersStochastic } private: - MobilityCoefficients m_coefficients; //one per group and compartment + MobilityCoefficients m_coefficients; //one per group and compartment }; -/** +/** * represents the mobility between two nodes. */ +template class MobilityEdgeStochastic { public: @@ -145,7 +148,7 @@ class MobilityEdgeStochastic * create edge with coefficients. * @param coeffs mobility rate for each group and compartment */ - MobilityEdgeStochastic(const MobilityParametersStochastic& params) + MobilityEdgeStochastic(const MobilityParametersStochastic& params) : m_parameters(params) { } @@ -154,7 +157,7 @@ class MobilityEdgeStochastic * create edge with coefficients. * @param coeffs mobility rate for each group and compartment */ - MobilityEdgeStochastic(const Eigen::VectorXd& coeffs) + MobilityEdgeStochastic(const Eigen::VectorX& coeffs) : m_parameters(coeffs) { } @@ -162,7 +165,7 @@ class MobilityEdgeStochastic /** * get the mobility parameters. */ - const MobilityParametersStochastic& get_parameters() const + const MobilityParametersStochastic& get_parameters() const { return m_parameters; } @@ -171,9 +174,9 @@ class MobilityEdgeStochastic * get the cumulative transition rate of the edge. */ template - Eigen::VectorXd get_transition_rates(SimulationNode& node_from) + Eigen::VectorX get_transition_rates(SimulationNode& node_from) { - Eigen::VectorXd transitionRates(node_from.get_last_state().size()); + Eigen::VectorX transitionRates(node_from.get_last_state().size()); for (Eigen::Index i = 0; i < node_from.get_last_state().size(); ++i) { transitionRates[i] = node_from.get_last_state()(i) * m_parameters.get_coefficients().get_baseline()[(size_t)i]; @@ -188,14 +191,16 @@ class MobilityEdgeStochastic * @param node_to node that people changed to */ template - void apply_mobility(size_t event, SimulationNode& node_from, SimulationNode& node_to); + void apply_mobility(size_t event, SimulationNode& node_from, SimulationNode& node_to); private: - MobilityParametersStochastic m_parameters; + MobilityParametersStochastic m_parameters; }; +template template -void MobilityEdgeStochastic::apply_mobility(size_t event, SimulationNode& node_from, SimulationNode& node_to) +void MobilityEdgeStochastic::apply_mobility(size_t event, SimulationNode& node_from, + SimulationNode& node_to) { node_from.get_result().get_last_value()[event] -= 1; node_to.get_result().get_last_value()[event] += 1; @@ -205,9 +210,9 @@ void MobilityEdgeStochastic::apply_mobility(size_t event, SimulationNode& n * edge functor for mobility-based simulation. * @see MobilityEdgeStochastic::apply_mobility */ -template -void apply_mobility(StochasticEdge& mobilityEdge, size_t event, SimulationNode& node_from, - SimulationNode& node_to) +template +void apply_mobility(StochasticEdge& mobilityEdge, size_t event, SimulationNode& node_from, + SimulationNode& node_to) { mobilityEdge.apply_mobility(event, node_from, node_to); } @@ -215,31 +220,31 @@ void apply_mobility(StochasticEdge& mobilityEdge, size_t event, SimulationNode -GraphSimulationStochastic, MobilityEdgeStochastic>> -make_mobility_sim(double t0, double dt, const Graph, MobilityEdgeStochastic>& graph) +template +GraphSimulationStochastic, MobilityEdgeStochastic>> +make_mobility_sim(FP t0, FP dt, const Graph, MobilityEdgeStochastic>& graph) { - return make_graph_sim_stochastic( - t0, dt, graph, &advance_model, - static_cast&, SimulationNode&)>( - &apply_mobility)); + return make_graph_sim_stochastic( + t0, dt, graph, &advance_model, + static_cast&, size_t, SimulationNode&, SimulationNode&)>( + &apply_mobility>)); } -template -GraphSimulationStochastic, MobilityEdgeStochastic>> -make_mobility_sim(double t0, double dt, Graph, MobilityEdgeStochastic>&& graph) +template +GraphSimulationStochastic, MobilityEdgeStochastic>> +make_mobility_sim(FP t0, FP dt, Graph, MobilityEdgeStochastic>&& graph) { - return make_graph_sim_stochastic( - t0, dt, std::move(graph), &advance_model, - static_cast&, SimulationNode&)>( - &apply_mobility)); + return make_graph_sim_stochastic( + t0, dt, std::move(graph), &advance_model, + static_cast&, size_t, SimulationNode&, SimulationNode&)>( + &apply_mobility>)); } /** @} */ diff --git a/cpp/memilio/utils/abstract_parameter_distribution.h b/cpp/memilio/utils/abstract_parameter_distribution.h index 24cb744cb6..bc046e8c9e 100644 --- a/cpp/memilio/utils/abstract_parameter_distribution.h +++ b/cpp/memilio/utils/abstract_parameter_distribution.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Julia Bicker @@ -41,7 +41,7 @@ class AbstractParameterDistribution public: /** - * The implementation handed to the constructor should have get_sample function + * The implementation handed to the constructor should have get_sample function * overloaded with mio::RandomNumberGenerator and mio::abm::PersonalRandomNumberGenerator as input arguments */ template @@ -112,18 +112,18 @@ class AbstractParameterDistribution /** * @brief Returns a value sampled with the given distribution. - * @param[in] rng RandomNumberGenerator used for sampling. + * @param[in] rng RandomNumberGenerator used for sampling. */ - double get(RandomNumberGenerator& rng) const + ScalarType get(RandomNumberGenerator& rng) const { return sample_impl1(m_dist.get(), rng); } /** * @brief Returns a value sampled with the given distribution. - * @param[in] rng abm::PersonalRandomNumberGenerator used for sampling. + * @param[in] rng abm::PersonalRandomNumberGenerator used for sampling. */ - double get(abm::PersonalRandomNumberGenerator& rng) const + ScalarType get(abm::PersonalRandomNumberGenerator& rng) const { return sample_impl2(m_dist.get(), rng); } @@ -131,7 +131,7 @@ class AbstractParameterDistribution /** * @brief Get the parameters of the given distribution. */ - std::vector params() const + std::vector params() const { return static_cast(m_dist.get())->params(); } @@ -148,10 +148,10 @@ class AbstractParameterDistribution private: std::shared_ptr m_dist; ///< Underlying distribtuion. - double (*sample_impl1)( + ScalarType (*sample_impl1)( void*, RandomNumberGenerator&); ///< Sample function of the distribution which gets a RandomNumberGenerator as rng. - double (*sample_impl2)( + ScalarType (*sample_impl2)( void*, abm:: PersonalRandomNumberGenerator&); ///< Sample function of the distribution which gets a abm::PersonalRandomNumberGenerator as rng. diff --git a/cpp/memilio/utils/logging.h b/cpp/memilio/utils/logging.h index 4ea584d499..d7a011c131 100644 --- a/cpp/memilio/utils/logging.h +++ b/cpp/memilio/utils/logging.h @@ -27,7 +27,7 @@ #endif #include "memilio/utils/compiler_diagnostics.h" -#include "ad/ad.hpp" +#include "memilio/ad/ad.h" // C4996: Some stdext functions used in spdlog 1.11 are marked as deprecated in version 19.38.33135.0 of MSVC. Maybe a future version of spdlog will fix this. MSVC_WARNING_DISABLE_PUSH(4996) diff --git a/cpp/memilio/utils/metaprogramming.h b/cpp/memilio/utils/metaprogramming.h index 933ae5cec8..fd2e110a84 100644 --- a/cpp/memilio/utils/metaprogramming.h +++ b/cpp/memilio/utils/metaprogramming.h @@ -23,6 +23,16 @@ #include #include +namespace ad +{ +namespace internal +{ +// Forward declaration of the AD type template +template +struct active_type; +} // namespace internal +} // namespace ad + namespace mio { @@ -120,7 +130,7 @@ template struct disjunction : B1 { //disjunction of one element is identity }; -template +template struct disjunction : std::conditional_t> { //disjunction of mutliple elements is equal to the first element if the first element is true. //otherwise its equal to the disjunction of the remaining elements. @@ -278,6 +288,23 @@ struct has_duplicates { template constexpr bool has_duplicates_v = has_duplicates::value; +/** + * @brief Detects whether a type is an automatic differentiation (AD) type. + * Intermediate result types from AD operations are not considered to be an AD type, as they use their own classes. + * @{ + */ +template +struct is_ad_type : public std::false_type { +}; + +template +struct is_ad_type> : public std::true_type { +}; + +template +constexpr bool is_ad_type_v = is_ad_type::value; +/**@}*/ + } // namespace mio #endif diff --git a/cpp/memilio/utils/parameter_distributions.h b/cpp/memilio/utils/parameter_distributions.h index ff1104a4dd..a84f885ea5 100644 --- a/cpp/memilio/utils/parameter_distributions.h +++ b/cpp/memilio/utils/parameter_distributions.h @@ -81,7 +81,7 @@ class ParameterDistribution virtual ~ParameterDistribution() = default; - void add_predefined_sample(double sample) + void add_predefined_sample(ScalarType sample) { m_predefined_samples.push_back(sample); } @@ -91,7 +91,7 @@ class ParameterDistribution m_predefined_samples.resize(0); } - const std::vector& get_predefined_samples() const + const std::vector& get_predefined_samples() const { return m_predefined_samples; } @@ -103,10 +103,10 @@ class ParameterDistribution * random sample is taken */ template - double get_sample(RNG& rng) + ScalarType get_sample(RNG& rng) { if (m_predefined_samples.size() > 0) { - double rnumb = m_predefined_samples[0]; + ScalarType rnumb = m_predefined_samples[0]; m_predefined_samples.erase(m_predefined_samples.begin()); return rnumb; } @@ -132,10 +132,10 @@ class ParameterDistribution /** * @brief Returns the distribution parameters as vector. */ - virtual std::vector params() const = 0; + virtual std::vector params() const = 0; - virtual double get_rand_sample(RandomNumberGenerator& rng) = 0; - virtual double get_rand_sample(abm::PersonalRandomNumberGenerator&) = 0; + virtual ScalarType get_rand_sample(RandomNumberGenerator& rng) = 0; + virtual ScalarType get_rand_sample(abm::PersonalRandomNumberGenerator&) = 0; virtual ParameterDistribution* clone() const = 0; @@ -149,7 +149,7 @@ class ParameterDistribution virtual void accept(ConstParameterDistributionVisitor& visitor) const = 0; protected: - std::vector + std::vector m_predefined_samples; // if these values are set; no real sample will occur but these values will be taken }; @@ -168,7 +168,7 @@ class ParameterDistributionNormal : public VisitableParameterDistribution() , m_mean(mean) , m_standard_dev(standard_dev) @@ -178,7 +178,7 @@ class ParameterDistributionNormal : public VisitableParameterDistribution() , m_mean(mean) , m_upper_bound(upper_bound) @@ -188,11 +188,11 @@ class ParameterDistributionNormal : public VisitableParameterDistribution::ParamType(m_mean, m_standard_dev); + m_distribution = mio::NormalDistribution::ParamType(m_mean, m_standard_dev); } - ParameterDistributionNormal(double lower_bound, double upper_bound, double mean, double standard_dev, - double quantile) + ParameterDistributionNormal(ScalarType lower_bound, ScalarType upper_bound, ScalarType mean, + ScalarType standard_dev, ScalarType quantile) : VisitableParameterDistribution() , m_mean(mean) , m_standard_dev(standard_dev) @@ -201,10 +201,10 @@ class ParameterDistributionNormal : public VisitableParameterDistribution::ParamType(m_mean, m_standard_dev); + m_distribution = mio::NormalDistribution::ParamType(m_mean, m_standard_dev); } - void set_mean(double mean) + void set_mean(ScalarType mean) { m_mean = mean; } @@ -218,7 +218,7 @@ class ParameterDistributionNormal : public VisitableParameterDistribution m_upper_bound) { @@ -245,7 +245,7 @@ class ParameterDistributionNormal : public VisitableParameterDistribution upper_bound) { @@ -260,17 +260,17 @@ class ParameterDistributionNormal : public VisitableParameterDistribution params() const override + std::vector params() const override { return {m_mean, m_standard_dev}; } @@ -323,7 +323,7 @@ class ParameterDistributionNormal : public VisitableParameterDistribution - double sample(RNG& rng) + ScalarType sample(RNG& rng) { //If ub = lb, sampling can only be succesful if mean = lb and dev = 0. //But this degenerate normal distribution is not allowed by the c++ standard. @@ -333,12 +333,12 @@ class ParameterDistributionNormal : public VisitableParameterDistribution::ParamType{m_mean, m_standard_dev}; + m_distribution = NormalDistribution::ParamType{m_mean, m_standard_dev}; } - int i = 0; - int retries = 10; - double rnumb = m_distribution.get_distribution_instance()(thread_local_rng(), m_distribution.params); + int i = 0; + int retries = 10; + ScalarType rnumb = m_distribution.get_distribution_instance()(thread_local_rng(), m_distribution.params); while ((rnumb > m_upper_bound || rnumb < m_lower_bound) && i < retries) { rnumb = m_distribution.get_distribution_instance()(rng, m_distribution.params); i++; @@ -355,12 +355,12 @@ class ParameterDistributionNormal : public VisitableParameterDistribution static IOResult deserialize_elements(IOContext& io, IOObject& obj) { - auto m = obj.expect_element("Mean", Tag{}); - auto s = obj.expect_element("StandardDev", Tag{}); - auto lb = obj.expect_element("LowerBound", Tag{}); - auto ub = obj.expect_element("UpperBound", Tag{}); - auto qu = obj.expect_element("Quantile", Tag{}); - auto predef = obj.expect_list("PredefinedSamples", Tag{}); + auto m = obj.expect_element("Mean", Tag{}); + auto s = obj.expect_element("StandardDev", Tag{}); + auto lb = obj.expect_element("LowerBound", Tag{}); + auto ub = obj.expect_element("UpperBound", Tag{}); + auto qu = obj.expect_element("Quantile", Tag{}); + auto predef = obj.expect_list("PredefinedSamples", Tag{}); auto p = apply( io, [](auto&& lb_, auto&& ub_, auto&& m_, auto&& s_, auto&& qu_, auto&& predef_) { @@ -423,13 +423,13 @@ class ParameterDistributionNormal : public VisitableParameterDistribution::max(); // upper bound and lower bound can be given to the constructor instead of stddev - double m_lower_bound = std::numeric_limits::min(); - double m_quantile = 2.5758; // default is 0.995 quartile - NormalDistribution::ParamType m_distribution; + ScalarType m_mean; // the mean value of the normal distribution + ScalarType m_standard_dev; // the standard deviation of the normal distribution + ScalarType m_upper_bound = std::numeric_limits< + ScalarType>::max(); // upper bound and lower bound can be given to the constructor instead of stddev + ScalarType m_lower_bound = std::numeric_limits::min(); + ScalarType m_quantile = 2.5758; // default is 0.995 quartile + NormalDistribution::ParamType m_distribution; bool m_log_stddev_change = true; }; @@ -446,7 +446,7 @@ void details::SerializationVisitor::visit(const ParameterDistributionNorm class ParameterDistributionUniform : public VisitableParameterDistribution { public: - ParameterDistributionUniform(double lower_bound, double upper_bound) + ParameterDistributionUniform(ScalarType lower_bound, ScalarType upper_bound) : VisitableParameterDistribution() , m_upper_bound(upper_bound) , m_lower_bound(lower_bound) @@ -465,17 +465,17 @@ class ParameterDistributionUniform : public VisitableParameterDistribution params() const override + std::vector params() const override { return {m_lower_bound, m_upper_bound}; } - double get_lower_bound() const + ScalarType get_lower_bound() const { return m_lower_bound; } - double get_upper_bound() const + ScalarType get_upper_bound() const { return m_upper_bound; } @@ -484,21 +484,21 @@ class ParameterDistributionUniform : public VisitableParameterDistribution - double sample(RNG& rng) + ScalarType sample(RNG& rng) { if (m_distribution.params.b() != m_upper_bound || m_distribution.params.a() != m_lower_bound) { - m_distribution = UniformDistribution::ParamType{m_lower_bound, m_upper_bound}; + m_distribution = UniformDistribution::ParamType{m_lower_bound, m_upper_bound}; } return m_distribution.get_distribution_instance()(rng, m_distribution.params); } - double get_rand_sample(RandomNumberGenerator& rng) override + ScalarType get_rand_sample(RandomNumberGenerator& rng) override { return sample(rng); } - double get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override + ScalarType get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override { return sample(rng); } @@ -526,9 +526,9 @@ class ParameterDistributionUniform : public VisitableParameterDistribution static IOResult deserialize_elements(IOContext& io, IOObject& obj) { - auto lb = obj.expect_element("LowerBound", Tag{}); - auto ub = obj.expect_element("UpperBound", Tag{}); - auto predef = obj.expect_list("PredefinedSamples", Tag{}); + auto lb = obj.expect_element("LowerBound", Tag{}); + auto ub = obj.expect_element("UpperBound", Tag{}); + auto predef = obj.expect_list("PredefinedSamples", Tag{}); auto p = apply( io, [](auto&& lb_, auto&& ub_, auto&& predef_) { @@ -555,9 +555,9 @@ class ParameterDistributionUniform : public VisitableParameterDistribution::ParamType m_distribution; + ScalarType m_upper_bound; + ScalarType m_lower_bound; + UniformDistribution::ParamType m_distribution; }; template @@ -573,7 +573,7 @@ void details::SerializationVisitor::visit(const ParameterDistributionUnif class ParameterDistributionLogNormal : public VisitableParameterDistribution { public: - ParameterDistributionLogNormal(double log_mean, double log_stddev) + ParameterDistributionLogNormal(ScalarType log_mean, ScalarType log_stddev) : VisitableParameterDistribution() , m_log_mean(log_mean) , m_log_stddev(log_stddev) @@ -592,17 +592,17 @@ class ParameterDistributionLogNormal : public VisitableParameterDistribution params() const override + std::vector params() const override { return {m_log_mean, m_log_stddev}; } - double get_log_mean() const + ScalarType get_log_mean() const { return m_log_mean; } - double get_log_stddev() const + ScalarType get_log_stddev() const { return m_log_stddev; } @@ -611,21 +611,21 @@ class ParameterDistributionLogNormal : public VisitableParameterDistribution - double sample(RNG& rng) + ScalarType sample(RNG& rng) { if (m_distribution.params.m() != m_log_mean || m_distribution.params.s() != m_log_stddev) { - m_distribution = LogNormalDistribution::ParamType{m_log_mean, m_log_stddev}; + m_distribution = LogNormalDistribution::ParamType{m_log_mean, m_log_stddev}; } return m_distribution.get_distribution_instance()(rng, m_distribution.params); } - double get_rand_sample(RandomNumberGenerator& rng) override + ScalarType get_rand_sample(RandomNumberGenerator& rng) override { return sample(rng); } - double get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override + ScalarType get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override { return sample(rng); } @@ -653,9 +653,9 @@ class ParameterDistributionLogNormal : public VisitableParameterDistribution static IOResult deserialize_elements(IOContext& io, IOObject& obj) { - auto lm = obj.expect_element("LogMean", Tag{}); - auto ls = obj.expect_element("LogStddev", Tag{}); - auto predef = obj.expect_list("PredefinedSamples", Tag{}); + auto lm = obj.expect_element("LogMean", Tag{}); + auto ls = obj.expect_element("LogStddev", Tag{}); + auto predef = obj.expect_list("PredefinedSamples", Tag{}); auto p = apply( io, [](auto&& lm_, auto&& ls_, auto&& predef_) { @@ -682,9 +682,9 @@ class ParameterDistributionLogNormal : public VisitableParameterDistribution::ParamType m_distribution; + ScalarType m_log_mean; + ScalarType m_log_stddev; + LogNormalDistribution::ParamType m_distribution; }; template @@ -700,7 +700,7 @@ void details::SerializationVisitor::visit(const ParameterDistributionLogN class ParameterDistributionExponential : public VisitableParameterDistribution { public: - ParameterDistributionExponential(double rate) + ParameterDistributionExponential(ScalarType rate) : VisitableParameterDistribution() , m_rate(rate) , m_distribution(rate) @@ -719,12 +719,12 @@ class ParameterDistributionExponential : public VisitableParameterDistribution

params() const override + std::vector params() const override { return {m_rate}; } - double get_rate() const + ScalarType get_rate() const { return m_rate; } @@ -733,21 +733,21 @@ class ParameterDistributionExponential : public VisitableParameterDistribution

- double sample(RNG& rng) + ScalarType sample(RNG& rng) { if (m_distribution.params.lambda() != m_rate) { - m_distribution = ExponentialDistribution::ParamType{m_rate}; + m_distribution = ExponentialDistribution::ParamType{m_rate}; } return m_distribution.get_distribution_instance()(rng, m_distribution.params); } - double get_rand_sample(RandomNumberGenerator& rng) override + ScalarType get_rand_sample(RandomNumberGenerator& rng) override { return sample(rng); } - double get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override + ScalarType get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override { return sample(rng); } @@ -774,8 +774,8 @@ class ParameterDistributionExponential : public VisitableParameterDistribution

static IOResult deserialize_elements(IOContext& io, IOObject& obj) { - auto r = obj.expect_element("Rate", Tag{}); - auto predef = obj.expect_list("PredefinedSamples", Tag{}); + auto r = obj.expect_element("Rate", Tag{}); + auto predef = obj.expect_list("PredefinedSamples", Tag{}); auto p = apply( io, [](auto&& r_, auto&& predef_) { @@ -802,8 +802,8 @@ class ParameterDistributionExponential : public VisitableParameterDistribution

::ParamType m_distribution; + ScalarType m_rate; + ExponentialDistribution::ParamType m_distribution; }; template @@ -819,7 +819,7 @@ void details::SerializationVisitor::visit(const ParameterDistributionExpo class ParameterDistributionConstant : public VisitableParameterDistribution { public: - ParameterDistributionConstant(double constant) + ParameterDistributionConstant(ScalarType constant) : VisitableParameterDistribution() , m_constant(constant) { @@ -836,12 +836,12 @@ class ParameterDistributionConstant : public VisitableParameterDistribution params() const override + std::vector params() const override { return {m_constant}; } - double get_constant() const + ScalarType get_constant() const { return m_constant; } @@ -850,17 +850,17 @@ class ParameterDistributionConstant : public VisitableParameterDistribution - double sample(RNG& /*rng*/) + ScalarType sample(RNG& /*rng*/) { return m_constant; } - double get_rand_sample(RandomNumberGenerator& rng) override + ScalarType get_rand_sample(RandomNumberGenerator& rng) override { return sample(rng); } - double get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override + ScalarType get_rand_sample(abm::PersonalRandomNumberGenerator& rng) override { return sample(rng); } @@ -887,8 +887,8 @@ class ParameterDistributionConstant : public VisitableParameterDistribution static IOResult deserialize_elements(IOContext& io, IOObject& obj) { - auto c = obj.expect_element("Constant", Tag{}); - auto predef = obj.expect_list("PredefinedSamples", Tag{}); + auto c = obj.expect_element("Constant", Tag{}); + auto predef = obj.expect_list("PredefinedSamples", Tag{}); auto p = apply( io, [](auto&& c_, auto&& predef_) { @@ -915,7 +915,7 @@ class ParameterDistributionConstant : public VisitableParameterDistribution diff --git a/cpp/memilio/utils/parameter_set.h b/cpp/memilio/utils/parameter_set.h index 28b48bb1dc..3d0db77af3 100644 --- a/cpp/memilio/utils/parameter_set.h +++ b/cpp/memilio/utils/parameter_set.h @@ -24,6 +24,7 @@ #include #include +#include namespace mio { @@ -225,7 +226,7 @@ class ParameterSet */ template ...>::value, Dummy>> + details::AllOf...>::value, Dummy>> ParameterSet() : m_tup(ParameterTagTraits::get_default()...) { diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index f1bbedc242..6471b75b01 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -540,18 +540,18 @@ class DiscreteDistributionInPlace param_type() = default; - param_type(Span weights) + param_type(Span weights) : m_weights(weights) { } - Span weights() const + Span weights() const { return m_weights; } private: - Span m_weights; + Span m_weights; }; /** @@ -563,7 +563,7 @@ class DiscreteDistributionInPlace /** * distribution with specified weights. */ - DiscreteDistributionInPlace(Span weights) + DiscreteDistributionInPlace(Span weights) : m_params(weights) { } @@ -603,7 +603,7 @@ class DiscreteDistributionInPlace /** * get the weights. */ - Span weights() + Span weights() { return m_params.weights(); } @@ -631,8 +631,8 @@ class DiscreteDistributionInPlace return 0; } auto sum = std::accumulate(weights.begin(), weights.end(), 0.0); - auto u = - std::uniform_real_distribution()(rng, std::uniform_real_distribution::param_type{0.0, sum}); + auto u = std::uniform_real_distribution()( + rng, std::uniform_real_distribution::param_type{0.0, sum}); auto intermediate_sum = 0.0; for (size_t i = 0; i < weights.size(); ++i) { intermediate_sum += weights.get_ptr()[i]; diff --git a/cpp/memilio/utils/stl_util.h b/cpp/memilio/utils/stl_util.h index ad9f46cd96..f1deb46c04 100644 --- a/cpp/memilio/utils/stl_util.h +++ b/cpp/memilio/utils/stl_util.h @@ -303,7 +303,7 @@ bool contains(Iter b, Iter e, Pred p) * @tparam T An enum class that is a valid index. * @return Array of all members of the enum class not including T::Count. */ -template +template constexpr std::array enum_members() { auto enum_members = std::array{}; diff --git a/cpp/memilio/utils/time_series.h b/cpp/memilio/utils/time_series.h index ca579f28b8..e43c3318b2 100644 --- a/cpp/memilio/utils/time_series.h +++ b/cpp/memilio/utils/time_series.h @@ -178,7 +178,7 @@ class TimeSeries } /** move ctor and assignment */ - TimeSeries(TimeSeries&& other) = default; + TimeSeries(TimeSeries&& other) = default; TimeSeries& operator=(TimeSeries&& other) = default; /// Check if the time is strictly monotonic increasing. @@ -660,8 +660,9 @@ struct TimeSeriesIterTraits { } using Matrix = typename TimeSeries::Matrix; using MatrixPtr = std::conditional_t*; - using VectorValue = typename decltype( - std::declval()->col(std::declval()).tail(std::declval()))::PlainObject; + using VectorValue = typename decltype(std::declval() + ->col(std::declval()) + .tail(std::declval()))::PlainObject; using VectorReference = decltype(std::declval()->col(std::declval()).tail(std::declval())); using TimeValue = FP; @@ -901,11 +902,11 @@ class TimeSeriesTimeIterator * @param rel_tol relative floating point tolerance for equality of time values * @return TimeSeries::reverse_iterator that points to ts[t_search] or ts.rend() */ -template +template decltype(std::declval().rend()) find_value_reverse(TS&& ts, FP t_search, FP abs_tol = 0, FP rel_tol = 0) { auto iter_t = find_if(ts.get_reverse_times().begin(), ts.get_reverse_times().end(), [=](auto t) { - return floating_point_equal(t, t_search, abs_tol, rel_tol); + return floating_point_equal(t, t_search, abs_tol, rel_tol); }); if (iter_t != ts.get_reverse_times().end()) { return ts.rbegin() + (iter_t - ts.get_reverse_times().begin()); diff --git a/cpp/memilio/utils/uncertain_value.h b/cpp/memilio/utils/uncertain_value.h index 505587af65..e861e2c6b5 100644 --- a/cpp/memilio/utils/uncertain_value.h +++ b/cpp/memilio/utils/uncertain_value.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Martin J. Kuehn, Martin Siggel, Daniel Abele @@ -31,13 +31,13 @@ namespace mio { /** - * @brief The UncertainValue class consists of a + * @brief The UncertainValue class consists of a * scalar value and a Distribution object - * + * * The UncertainValue class represents a model parameter that * can take a scalar value but that is subjected to a uncertainty. * The uncertainty is represented by a distribution object of kind - * ParameterDistribution and the current scalar value can be + * ParameterDistribution and the current scalar value can be * replaced by drawing a new sample from the the distribution * @tparam FP underlying floating point type, e.g., double */ @@ -51,7 +51,7 @@ class UncertainValue { } - UncertainValue(FP v = static_cast(0.)) + UncertainValue(FP v = static_cast(0.0)) : m_value(v) { } @@ -59,7 +59,7 @@ class UncertainValue UncertainValue(UncertainValue&& other) = default; /** - * @brief Create an UncertainValue by cloning scalar value + * @brief Create an UncertainValue by cloning scalar value * and distribution of another UncertainValue */ UncertainValue(const UncertainValue& other) @@ -82,6 +82,32 @@ class UncertainValue return *this; } + /** + * @brief Create an UncertainValue from a scalar of any type convertible to FP. + * + * This constructor allows initializing UncertainValue with scalars + * or AD (Automatic Differentiation) types that can be cast to FP. + * The distribution remains unset. + */ + template ::value, int> = 0> + UncertainValue(T v) + : m_value(static_cast(v)) + { + } + + /** + * @brief Assign a scalar of any type convertible to FP to this UncertainValue. + * + * The contained scalar is updated, while the distribution remains unchanged. + * Supports scalars and AD (Automatic Differentiation) types that can be cast to FP. + */ + template ::value, int> = 0> + UncertainValue& operator=(T v) + { + m_value = static_cast(v); + return *this; + } + /** * @brief Conversion to scalar by returning the scalar contained in UncertainValue */ @@ -158,7 +184,7 @@ class UncertainValue } /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -240,12 +266,242 @@ const FP& format_as(const UncertainValue& uv) // gtest printer // TODO: should be extended when UncertainValue gets operator== that compares distributions as well -template +template inline void PrintTo(const UncertainValue& uv, std::ostream* os) { (*os) << uv.value(); } +/** + * @defgroup UncertainValueOperators Free-standing operators for UncertainValue + * @{ + * + * These operators enable seamless arithmetic between UncertainValue objects and + * scalar or AD (Automatic Differentiation) types. + * + * Template parameter T: + * - T is any type convertible to FP (the floating-point type of UncertainValue). + * - This includes scalars and AD types, as long as they can be cast to FP. + * + * Why needed: + * - UncertainValue is often used with both plain scalars (e.g., double, float, int) + * and AD types (e.g., active_type, AD expression templates). + * - Without these operators and templated constructors, assigning or operating on + * UncertainValue with AD expressions or scalars would fail, as the compiler cannot + * automatically convert complex AD expressions to UncertainValue. + */ + +/** @name Comparison operators with scalar or AD type on right (UncertainValue T) */ +//@{ +template +inline bool operator>(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() > rhs; +} +template +inline bool operator<(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() < rhs; +} +template +inline bool operator>=(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() >= rhs; +} +template +inline bool operator<=(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() <= rhs; +} +template +inline bool operator==(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() == rhs; +} +template +inline bool operator!=(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() != rhs; +} +//@} + +/** @name Comparison operators with scalar or AD type on left (T UncertainValue) */ +//@{ +template +inline bool operator>(const T& lhs, const UncertainValue& rhs) +{ + return lhs > rhs.value(); +} +template +inline bool operator<(const T& lhs, const UncertainValue& rhs) +{ + return lhs < rhs.value(); +} +template +inline bool operator>=(const T& lhs, const UncertainValue& rhs) +{ + return lhs >= rhs.value(); +} +template +inline bool operator<=(const T& lhs, const UncertainValue& rhs) +{ + return lhs <= rhs.value(); +} +template +inline bool operator==(const T& lhs, const UncertainValue& rhs) +{ + return lhs == rhs.value(); +} +template +inline bool operator!=(const T& lhs, const UncertainValue& rhs) +{ + return lhs != rhs.value(); +} +//@} + +/** @name Comparison operators between two UncertainValue objects */ +//@{ +template +inline bool operator>(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() > rhs.value(); +} +template +inline bool operator<(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() < rhs.value(); +} +template +inline bool operator>=(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() >= rhs.value(); +} +template +inline bool operator<=(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() <= rhs.value(); +} +template +inline bool operator==(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() == rhs.value(); +} +template +inline bool operator!=(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() != rhs.value(); +} +//@} + +/** @name Arithmetic operators between two UncertainValue objects (returns FP) */ +//@{ +template +inline FP operator*(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() * rhs.value(); +} +template +inline FP operator/(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() / rhs.value(); +} +template +inline FP operator+(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() + rhs.value(); +} +template +inline FP operator-(const UncertainValue& lhs, const UncertainValue& rhs) +{ + return lhs.value() - rhs.value(); +} +//@} + +/** @name Arithmetic operators with scalar or AD type on right (UncertainValue T) */ +//@{ +template +inline FP operator*(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() * rhs; +} +template +inline FP operator/(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() / rhs; +} +template +inline FP operator+(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() + rhs; +} +template +inline FP operator-(const UncertainValue& lhs, const T& rhs) +{ + return lhs.value() - rhs; +} +//@} + +/** @name Arithmetic operators with scalar or AD type on left (T UncertainValue) */ +//@{ +template +inline FP operator*(const T& lhs, const UncertainValue& rhs) +{ + return lhs * rhs.value(); +} +template +inline FP operator/(const T& lhs, const UncertainValue& rhs) +{ + return lhs / rhs.value(); +} +template +inline FP operator+(const T& lhs, const UncertainValue& rhs) +{ + return lhs + rhs.value(); +} +template +inline FP operator-(const T& lhs, const UncertainValue& rhs) +{ + return lhs - rhs.value(); +} +//@} + +/** @name Compound assignment operators for FP and UncertainValue */ +//@{ +template +FP& operator+=(FP& lhs, const UncertainValue& rhs) +{ + lhs += rhs.value(); + return lhs; +} +template +FP& operator-=(FP& lhs, const UncertainValue& rhs) +{ + lhs -= rhs.value(); + return lhs; +} +template +FP& operator*=(FP& lhs, const UncertainValue& rhs) +{ + lhs *= rhs.value(); + return lhs; +} +template +FP& operator/=(FP& lhs, const UncertainValue& rhs) +{ + lhs /= rhs.value(); + return lhs; +} +//@} + +/** + * @brief Print statement for UncertainValue (required for AD types) + */ +template +inline std::ostream& operator<<(std::ostream& os, const UncertainValue& uv) +{ + return os << uv.value(); +} + } // namespace mio #endif // UNCERTAINVALUE_H diff --git a/cpp/models/abm/analyze_result.h b/cpp/models/abm/analyze_result.h index 1f01619a1f..2e870f006f 100644 --- a/cpp/models/abm/analyze_result.h +++ b/cpp/models/abm/analyze_result.h @@ -37,19 +37,19 @@ namespace abm * @return p percentile of the parameters over all runs */ template -std::vector ensemble_params_percentile(const std::vector>& ensemble_params, double p) +std::vector ensemble_params_percentile(const std::vector>& ensemble_params, ScalarType p) { assert(p > 0.0 && p < 1.0 && "Invalid percentile value."); auto num_runs = ensemble_params.size(); auto num_nodes = ensemble_params[0].size(); - std::vector single_element_ensemble(num_runs); + std::vector single_element_ensemble(num_runs); auto num_groups = (size_t)ensemble_params[0][0].parameters.get_num_groups(); // Lambda function that calculates the percentile of a single parameter std::vector percentile(num_nodes, Model((int)num_groups)); auto param_percentile = [&ensemble_params, p, num_runs, &percentile](auto n, auto get_param) mutable { - std::vector single_element(num_runs); + std::vector single_element(num_runs); for (size_t run = 0; run < num_runs; run++) { auto const& params = ensemble_params[run][n]; single_element[run] = get_param(params); diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index a453d9554b..0d490437f0 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -166,7 +166,7 @@ struct LogDataForMobility : mio::LogAlways { * @brief Logger to log the TimeSeries of the number of Person%s in an #InfectionState. */ struct LogInfectionState : mio::LogAlways { - using Type = std::pair; + using Type = std::pair>; /** * @brief Log the TimeSeries of the number of Person%s in an #InfectionState. * @param[in] sim The simulation of the abm. @@ -175,8 +175,9 @@ struct LogInfectionState : mio::LogAlways { static Type log(const mio::abm::Simulation<>& sim) { - Eigen::VectorXd sum = Eigen::VectorXd::Zero(Eigen::Index(mio::abm::InfectionState::Count)); - auto curr_time = sim.get_time(); + Eigen::VectorX sum = + Eigen::VectorX::Zero(Eigen::Index(mio::abm::InfectionState::Count)); + auto curr_time = sim.get_time(); PRAGMA_OMP(for) for (auto& location : sim.get_model().get_locations()) { for (uint32_t inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { @@ -197,7 +198,7 @@ struct TimeSeriesWriter { using Data = std::tuple>; template /** - * @brief This function adds an entry to the TimeSeries consisting of the TimePoint and the value. The Loggers must return a touple with a TimePoint and a value of return type Eigen::VectorXd. + * @brief This function adds an entry to the TimeSeries consisting of the TimePoint and the value. The Loggers must return a touple with a TimePoint and a value of return type Eigen::VectorX. * @param[in] t The data from the logger. * @param[in,out] data The data tuple. */ diff --git a/cpp/models/abm/config.h b/cpp/models/abm/config.h index c830ad725a..7956cd27b4 100644 --- a/cpp/models/abm/config.h +++ b/cpp/models/abm/config.h @@ -30,7 +30,7 @@ namespace abm */ const constexpr int MAX_NUM_AGE_GROUPS = 64; -} +} // namespace abm } // namespace mio #endif diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index ba10750543..b9f9149710 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -38,7 +38,7 @@ namespace AgeGroup pick_age_group_from_age_distribution(RandomNumberGenerator& rng, const CustomIndexArray& age_groups) { - auto age_group_weights = age_groups.array().cast().eval(); + auto age_group_weights = age_groups.array().cast().eval(); size_t age_group = DiscreteDistribution::get_instance()(rng, age_group_weights); return (AgeGroup)age_group; } diff --git a/cpp/models/abm/infection.cpp b/cpp/models/abm/infection.cpp index 0dfd5dca09..6d80d144a3 100644 --- a/cpp/models/abm/infection.cpp +++ b/cpp/models/abm/infection.cpp @@ -133,7 +133,7 @@ void Infection::draw_infection_course_forward(PersonalRandomNumberGenerator& rng m_virus_variant, age}]; // time distribution parameters for current infection state InfectionState next_state{start_state}; // next state to enter m_infection_course.push_back(std::pair(t, next_state)); - auto& uniform_dist = UniformDistribution::get_instance(); + auto& uniform_dist = UniformDistribution::get_instance(); ScalarType p; // uniform random draws from [0, 1] while ((next_state != InfectionState::Recovered && next_state != InfectionState::Dead)) { switch (next_state) { @@ -237,7 +237,7 @@ TimePoint Infection::draw_infection_course_backward(PersonalRandomNumberGenerato auto time_in_state = params.get()[{ m_virus_variant, age}]; // time distribution parameters for current infection state InfectionState previous_state{init_state}; // previous state to enter - auto& uniform_dist = UniformDistribution::get_instance(); + auto& uniform_dist = UniformDistribution::get_instance(); ScalarType p; // uniform random draws from [0, 1] while ((previous_state != InfectionState::Exposed)) { diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index cd18d1b361..e0d0aa1b2a 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -34,8 +34,8 @@ namespace abm { struct GeographicalLocation { - double latitude; - double longitude; + ScalarType latitude; + ScalarType longitude; /** * @brief Compare two GeographicalLocation%s. diff --git a/cpp/models/abm/lockdown_rules.cpp b/cpp/models/abm/lockdown_rules.cpp index e3ec4276db..95c4847993 100644 --- a/cpp/models/abm/lockdown_rules.cpp +++ b/cpp/models/abm/lockdown_rules.cpp @@ -27,22 +27,22 @@ namespace mio namespace abm { -void set_home_office(TimePoint t_begin, double p, Parameters& params) +void set_home_office(TimePoint t_begin, ScalarType p, Parameters& params) { - auto damping1 = Eigen::VectorXd::Constant(1, p); - params.get().add_damping(damping1, SimulationTime(t_begin.days())); + auto damping1 = Eigen::VectorX::Constant(1, p); + params.get().add_damping(damping1, SimulationTime(t_begin.days())); } -void set_school_closure(TimePoint t_begin, double p, Parameters& params) +void set_school_closure(TimePoint t_begin, ScalarType p, Parameters& params) { - auto damping1 = Eigen::VectorXd::Constant(1, p); - params.get().add_damping(damping1, SimulationTime(t_begin.days())); + auto damping1 = Eigen::VectorX::Constant(1, p); + params.get().add_damping(damping1, SimulationTime(t_begin.days())); } -void close_social_events(TimePoint t_begin, double p, Parameters& params) +void close_social_events(TimePoint t_begin, ScalarType p, Parameters& params) { - auto damping1 = Eigen::VectorXd::Constant(params.get_num_groups(), p); - params.get().add_damping(damping1, SimulationTime(t_begin.days())); + auto damping1 = Eigen::VectorX::Constant(params.get_num_groups(), p); + params.get().add_damping(damping1, SimulationTime(t_begin.days())); } } // namespace abm diff --git a/cpp/models/abm/lockdown_rules.h b/cpp/models/abm/lockdown_rules.h index 086c696950..3b5170eb5c 100644 --- a/cpp/models/abm/lockdown_rules.h +++ b/cpp/models/abm/lockdown_rules.h @@ -47,7 +47,7 @@ namespace abm * @param[in] p Percentage of Person%s that work in home office. * @param[in, out] params Simulation parameters that include Damping. */ -void set_home_office(TimePoint t_begin, double p, Parameters& params); +void set_home_office(TimePoint t_begin, ScalarType p, Parameters& params); /** * @brief If schools are closed, students stay at home instead of going to school. @@ -55,7 +55,7 @@ void set_home_office(TimePoint t_begin, double p, Parameters& params); * @param[in] p Percentage of Person%s that are homeschooled. * @param[in,out] params Simulation parameters. */ -void set_school_closure(TimePoint t_begin, double p, Parameters& params); +void set_school_closure(TimePoint t_begin, ScalarType p, Parameters& params); /** * @brief During lockdown Person%s join social events less often. @@ -66,7 +66,7 @@ void set_school_closure(TimePoint t_begin, double p, Parameters& params); * @param[in] p Damping between 0 and 1 that changes the parameter of the exponential distribution. * @param[in,out] params Simulation parameters that include Damping. */ -void close_social_events(TimePoint t_begin, double p, Parameters& params); +void close_social_events(TimePoint t_begin, ScalarType p, Parameters& params); } // namespace abm } //namespace mio diff --git a/cpp/models/abm/mobility_rules.cpp b/cpp/models/abm/mobility_rules.cpp index 13b74740cd..b86855d7db 100644 --- a/cpp/models/abm/mobility_rules.cpp +++ b/cpp/models/abm/mobility_rules.cpp @@ -110,9 +110,10 @@ LocationType go_to_event(PersonalRandomNumberGenerator& rng, const Person& perso ((t.day_of_week() <= 4 && t.hour_of_day() >= 19 && t.hour_of_day() < 22) || (t.day_of_week() >= 5 && t.hour_of_day() >= 10 && t.hour_of_day() < 22)) && !person.is_in_quarantine(t, params)) { - return random_transition(rng, current_loc, dt, - {{LocationType::SocialEvent, - params.get().get_matrix_at(t.days())[(size_t)person.get_age()]}}); + return random_transition( + rng, current_loc, dt, + {{LocationType::SocialEvent, params.get().get_matrix_at( + SimulationTime(t.days()))[(size_t)person.get_age()]}}); } //return home diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index e2289e4f9a..be5bd886da 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -205,7 +205,7 @@ struct TimeInfectedCriticalToRecovered { * @brief the percentage of symptomatic cases */ struct SymptomsPerInfectedNoSymptoms { - using Type = CustomIndexArray, VirusVariant, AgeGroup>; + using Type = CustomIndexArray, VirusVariant, AgeGroup>; static Type get_default(AgeGroup size) { return Type({VirusVariant::Count, size}, .5); @@ -220,7 +220,7 @@ struct SymptomsPerInfectedNoSymptoms { * @brief the percentage of hospitalized cases per infected cases */ struct SeverePerInfectedSymptoms { - using Type = CustomIndexArray, VirusVariant, AgeGroup>; + using Type = CustomIndexArray, VirusVariant, AgeGroup>; static Type get_default(AgeGroup size) { return Type({VirusVariant::Count, size}, .5); @@ -235,7 +235,7 @@ struct SeverePerInfectedSymptoms { * @brief the percentage of ICU cases per hospitalized cases */ struct CriticalPerInfectedSevere { - using Type = CustomIndexArray, VirusVariant, AgeGroup>; + using Type = CustomIndexArray, VirusVariant, AgeGroup>; static Type get_default(AgeGroup size) { return Type({VirusVariant::Count, size}, .5); @@ -250,7 +250,7 @@ struct CriticalPerInfectedSevere { * @brief the percentage of dead cases per hospitalized cases */ struct DeathsPerInfectedSevere { - using Type = CustomIndexArray, VirusVariant, AgeGroup>; + using Type = CustomIndexArray, VirusVariant, AgeGroup>; static Type get_default(AgeGroup size) { return Type({VirusVariant::Count, size}, .1); @@ -265,7 +265,7 @@ struct DeathsPerInfectedSevere { * @brief the percentage of dead cases per ICU cases */ struct DeathsPerInfectedCritical { - using Type = CustomIndexArray, VirusVariant, AgeGroup>; + using Type = CustomIndexArray, VirusVariant, AgeGroup>; static Type get_default(AgeGroup size) { return Type({VirusVariant::Count, size}, .5); @@ -367,7 +367,7 @@ struct VirusShedFactor { * @brief Probability that an Infection is detected. */ struct DetectInfection { - using Type = CustomIndexArray, VirusVariant, AgeGroup>; + using Type = CustomIndexArray, VirusVariant, AgeGroup>; static Type get_default(AgeGroup size) { return Type({VirusVariant::Count, size}, 1.); @@ -382,7 +382,7 @@ struct DetectInfection { * @brief Effectiveness of a Mask of a certain MaskType% against an Infection%. */ struct MaskProtection { - using Type = CustomIndexArray, MaskType>; + using Type = CustomIndexArray, MaskType>; static Type get_default(AgeGroup /*size*/) { Type defaut_value = Type(MaskType::Count, 0.0); @@ -465,8 +465,8 @@ struct HighViralLoadProtectionFactor { * @brief Parameters that describe the reliability of a test. */ struct TestParameters { - UncertainValue<> sensitivity; - UncertainValue<> specificity; + UncertainValue sensitivity; + UncertainValue specificity; TimeSpan required_time; TestType type; @@ -549,7 +549,7 @@ struct QuarantineEffectiveness { * @brief Parameter for the exponential distribution to decide if a Person goes shopping. */ struct BasicShoppingRate { - using Type = CustomIndexArray, AgeGroup>; + using Type = CustomIndexArray, AgeGroup>; static auto get_default(AgeGroup size) { return Type({size}, 1.0); @@ -564,10 +564,11 @@ struct BasicShoppingRate { * @brief Percentage of Person%s of the respective age going to work. */ struct WorkRatio { - using Type = DampingMatrixExpression>>; + using Type = + DampingMatrixExpression>>>; static auto get_default(AgeGroup /*size*/) { - return Type(Eigen::VectorXd::Constant(1, 1.0)); + return Type(Eigen::VectorX::Constant(1, 1.0)); } static std::string name() { @@ -579,10 +580,11 @@ struct WorkRatio { * @brief Percentage of Person%s of the respective age going to school. */ struct SchoolRatio { - using Type = DampingMatrixExpression>>; + using Type = + DampingMatrixExpression>>>; static auto get_default(AgeGroup /*size*/) { - return Type(Eigen::VectorXd::Constant(1, 1.0)); + return Type(Eigen::VectorX::Constant(1, 1.0)); } static std::string name() { @@ -594,10 +596,11 @@ struct SchoolRatio { * @brief Parameter for the exponential distribution to decide if a Person goes to a social event. */ struct SocialEventRate { - using Type = DampingMatrixExpression>>; + using Type = + DampingMatrixExpression>>>; static auto get_default(AgeGroup size) { - return Type(Eigen::VectorXd::Constant((size_t)size, 1.0)); + return Type(Eigen::VectorX::Constant((size_t)size, 1.0)); } static std::string name() { diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index a0e450a64a..faff3aa73c 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -52,10 +52,10 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_rng_key(rng.get_key()) , m_rng_index(static_cast(person_id.get())) { - m_random_workgroup = UniformDistribution::get_instance()(rng); - m_random_schoolgroup = UniformDistribution::get_instance()(rng); - m_random_goto_work_hour = UniformDistribution::get_instance()(rng); - m_random_goto_school_hour = UniformDistribution::get_instance()(rng); + m_random_workgroup = UniformDistribution::get_instance()(rng); + m_random_schoolgroup = UniformDistribution::get_instance()(rng); + m_random_goto_work_hour = UniformDistribution::get_instance()(rng); + m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } Person::Person(const Person& other, PersonId person_id) @@ -134,7 +134,7 @@ int Person::get_assigned_location_model_id(LocationType type) const bool Person::goes_to_work(TimePoint t, const Parameters& params) const { - return m_random_workgroup < params.get().get_matrix_at(t.days())[0]; + return m_random_workgroup < params.get().get_matrix_at(SimulationTime(t.days()))[0]; } TimeSpan Person::get_go_to_work_time(const Parameters& params) const @@ -157,7 +157,7 @@ TimeSpan Person::get_go_to_school_time(const Parameters& params) const bool Person::goes_to_school(TimePoint t, const Parameters& params) const { - return m_random_schoolgroup < params.get().get_matrix_at(t.days())[0]; + return m_random_schoolgroup < params.get().get_matrix_at(SimulationTime(t.days()))[0]; } void Person::remove_quarantine() @@ -167,7 +167,7 @@ void Person::remove_quarantine() bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const TestParameters& params) { - ScalarType random = UniformDistribution::get_instance()(rng); + ScalarType random = UniformDistribution::get_instance()(rng); if (is_infected(t)) { // true positive if (random < params.sensitivity) { @@ -221,7 +221,7 @@ ScalarType Person::get_mask_protective_factor(const Parameters& params) const bool Person::is_compliant(PersonalRandomNumberGenerator& rng, InterventionType intervention) const { - ScalarType compliance_check = UniformDistribution::get_instance()(rng); + ScalarType compliance_check = UniformDistribution::get_instance()(rng); return compliance_check <= get_compliance(intervention); } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 8933e85775..a4e0c8da81 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -474,10 +474,12 @@ class Person TimePoint m_home_isolation_start; ///< TimePoint when the Person started isolation at home. AgeGroup m_age; ///< AgeGroup the Person belongs to. TimeSpan m_time_at_location; ///< Time the Person has spent at its current Location so far. - double m_random_workgroup; ///< Value to determine if the Person goes to work or works from home during lockdown. - double m_random_schoolgroup; ///< Value to determine if the Person goes to school or stays at home during lockdown. - double m_random_goto_work_hour; ///< Value to determine at what time the Person goes to work. - double m_random_goto_school_hour; ///< Value to determine at what time the Person goes to school. + ScalarType + m_random_workgroup; ///< Value to determine if the Person goes to work or works from home during lockdown. + ScalarType + m_random_schoolgroup; ///< Value to determine if the Person goes to school or stays at home during lockdown. + ScalarType m_random_goto_work_hour; ///< Value to determine at what time the Person goes to work. + ScalarType m_random_goto_school_hour; ///< Value to determine at what time the Person goes to school. Mask m_mask; ///< The Mask of the Person. std::vector m_compliance; ///< Vector of compliance values for all #InterventionType%s. Values from 0 to 1. diff --git a/cpp/models/abm/random_events.h b/cpp/models/abm/random_events.h index 0dfac26b9a..96b3c8f283 100644 --- a/cpp/models/abm/random_events.h +++ b/cpp/models/abm/random_events.h @@ -47,7 +47,8 @@ namespace abm * @return New state from the list if transition happens, current_state otherwise. */ template -T random_transition(RNG& rng, T current_state, TimeSpan dt, const std::pair (&transitions)[NumTransitions]) +T random_transition(RNG& rng, T current_state, TimeSpan dt, + const std::pair (&transitions)[NumTransitions]) { assert(std::all_of(std::begin(transitions), std::end(transitions), [](auto& p) { @@ -62,10 +63,10 @@ T random_transition(RNG& rng, T current_state, TimeSpan dt, const std::pair::get_instance()(rng, sum); + auto v = ExponentialDistribution::get_instance()(rng, sum); if (v < dt.days()) { //pick one of the possible transitions using discrete distribution - std::array rates; + std::array rates; std::transform(std::begin(transitions), std::end(transitions), rates.begin(), [](auto&& t) { return t.second; }); diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index 4816c03663..4a04acd1fd 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -51,7 +51,7 @@ bool TestingCriteria::evaluate(const Person& p, TimePoint t) const } TestingScheme::TestingScheme(const TestingCriteria& testing_criteria, TimeSpan validity_period, TimePoint start_date, - TimePoint end_date, TestParameters test_parameters, double probability) + TimePoint end_date, TestParameters test_parameters, ScalarType probability) : m_testing_criteria(testing_criteria) , m_validity_period(validity_period) , m_start_date(start_date) @@ -97,7 +97,7 @@ bool TestingScheme::run_and_test(PersonalRandomNumberGenerator& rng, Person& per } // Otherwise, the time_of_testing in the past (i.e. the agent has already performed it). if (m_testing_criteria.evaluate(person, t - m_test_parameters.required_time)) { - double random = UniformDistribution::get_instance()(rng); + ScalarType random = UniformDistribution::get_instance()(rng); if (random < m_probability) { bool result = person.get_tested(rng, t - m_test_parameters.required_time, m_test_parameters); person.add_test_result(t, m_test_parameters.type, result); diff --git a/cpp/models/abm/time.h b/cpp/models/abm/time.h index a1d50d6f76..942cf1a3e7 100644 --- a/cpp/models/abm/time.h +++ b/cpp/models/abm/time.h @@ -20,6 +20,7 @@ #ifndef MIO_ABM_TIME_H #define MIO_ABM_TIME_H +#include "memilio/config.h" #include "memilio/io/default_serialize.h" namespace mio @@ -51,17 +52,17 @@ class TimeSpan /** * @brief Length of time in days. */ - double days() const + ScalarType days() const { - return double(m_seconds) / (24 * 60 * 60); + return ScalarType(m_seconds) / (24 * 60 * 60); } /** * @brief Length of time in hours. */ - double hours() const + ScalarType hours() const { - return double(m_seconds) / (60 * 60); + return ScalarType(m_seconds) / (60 * 60); }; /** @@ -178,16 +179,16 @@ class TimePoint /** * @brief Time since the epoch in days. */ - double days() const + ScalarType days() const { - return double(m_seconds) / (24 * 60 * 60); + return ScalarType(m_seconds) / (24 * 60 * 60); } /** * @brief Time since the epoch in hours. */ - double hours() const + ScalarType hours() const { - return double(m_seconds) / (60 * 60); + return ScalarType(m_seconds) / (60 * 60); }; /** * @brief Time since the epoch in seconds. @@ -338,7 +339,7 @@ inline TimeSpan days(int days) return TimeSpan(days * 24 * 60 * 60); } -inline TimeSpan days(double days) +inline TimeSpan days(ScalarType days) { return TimeSpan((int)(days * 24 * 60 * 60)); } diff --git a/cpp/models/d_abm/model.cpp b/cpp/models/d_abm/model.cpp index 766fcbe35c..f52bd1c298 100644 --- a/cpp/models/d_abm/model.cpp +++ b/cpp/models/d_abm/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 German Aerospace Center (DLR-SC) * * Authors: René Schmieding diff --git a/cpp/models/d_abm/model.h b/cpp/models/d_abm/model.h index ea0c782a07..b0983c5496 100644 --- a/cpp/models/d_abm/model.h +++ b/cpp/models/d_abm/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 German Aerospace Center (DLR-SC) * * Authors: René Schmieding, Julia Bicker @@ -47,13 +47,13 @@ class Model : public Implementation /** * @brief Calculate the current adoption rate of an agent from its status to the given one. - * Expected signature: `double adoption_rate(const Agent&, const Status&)` + * Expected signature: `ScalarType adoption_rate(const Agent&, const Status&)` */ using Implementation::adoption_rate; /** * @brief Change the Position of an Agent, depending on its state, the current time and step size. - * Expected signature: `void move(const double, const double, Agent&)` + * Expected signature: `void move(const ScalarType, const ScalarType, Agent&)` * The first argument is time, the second step size. */ using Implementation::move; @@ -66,7 +66,7 @@ class Model : public Implementation /** * @brief Aggregate the population by their Status for the simulation result. - * Expected signature: `Eigen::VectorXd time_point()` + * Expected signature: `Eigen::VectorX time_point()` */ using Implementation::time_point; diff --git a/cpp/models/d_abm/parameters.h b/cpp/models/d_abm/parameters.h index 9293344330..bd02befd9b 100644 --- a/cpp/models/d_abm/parameters.h +++ b/cpp/models/d_abm/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2024 German Aerospace Center (DLR-SC) * * Authors: René Schmieding, Julia Bicker diff --git a/cpp/models/d_abm/quad_well.h b/cpp/models/d_abm/quad_well.h index 8f7617bd34..305617304e 100644 --- a/cpp/models/d_abm/quad_well.h +++ b/cpp/models/d_abm/quad_well.h @@ -40,7 +40,7 @@ inline size_t well_index(const Position& p) } /** - * @brief Implementation of diffusive ABM, see dabm::Model. + * @brief Implementation of diffusive ABM, see dabm::Model. * This implementation defines a diffusion process for the potential F(x,y) = (x^2 -1)^2+(y^2-1)^2. * @tparam InfectionState An infection state enum. */ @@ -67,13 +67,13 @@ class QuadWell * @param[in] sigma Noise term for the diffusion process. * @param[in] non_moving_state InfectionStates that are excluded from movement e.g. Dead. */ - QuadWell(const std::vector& agents, const std::vector>& rates, - double contact_radius = 0.4, double sigma = 0.4, std::vector non_moving_states = {}) + QuadWell(const std::vector& agents, const std::vector>& rates, + ScalarType contact_radius = 0.4, ScalarType sigma = 0.4, std::vector non_moving_states = {}) : populations(agents) , m_contact_radius(contact_radius) , m_sigma(sigma) , m_non_moving_states(non_moving_states) - , m_number_transitions(static_cast(Status::Count), Eigen::MatrixXd::Zero(4, 4)) + , m_number_transitions(static_cast(Status::Count), Eigen::MatrixX::Zero(4, 4)) { for (auto& agent : populations) { mio::unused(agent); @@ -100,9 +100,9 @@ class QuadWell * @param[in] new_status Target infection state of the adoption rate, see mio::AdoptionRate.to. * @return Value of agent-dependent AdoptionRate. */ - double adoption_rate(const Agent& agent, const Status& new_status) const + ScalarType adoption_rate(const Agent& agent, const Status& new_status) const { - double rate = 0; + ScalarType rate = 0; // get the correct adoption rate const size_t well = well_index(agent.position); auto map_itr = m_adoption_rates.find({well, agent.status, new_status}); @@ -143,15 +143,16 @@ class QuadWell * @param[in] dt Step size. * @param[in] agent Agent to be moved. */ - void move(const double /*t*/, const double dt, Agent& agent) + void move(const ScalarType /*t*/, const ScalarType dt, Agent& agent) { const auto old_well = well_index(agent.position); if (std::find(m_non_moving_states.begin(), m_non_moving_states.end(), agent.status) == m_non_moving_states.end() && std::find(m_non_moving_regions.begin(), m_non_moving_regions.end(), old_well) == m_non_moving_regions.end()) { - Position p = {mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0), - mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0)}; + Position p = { + mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0), + mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0)}; agent.position = agent.position - dt * grad_U(agent.position) + (m_sigma * std::sqrt(dt)) * p; const auto new_well = well_index(agent.position); @@ -166,9 +167,9 @@ class QuadWell * @brief Calculate the current system state i.e. the populations for each region and infection state. * @return Vector containing the number of agents per infection state for each region. */ - Eigen::VectorXd time_point() const + Eigen::VectorX time_point() const { - Eigen::VectorXd val = Eigen::VectorXd::Zero(4 * static_cast(Status::Count)); + Eigen::VectorX val = Eigen::VectorX::Zero(4 * static_cast(Status::Count)); for (auto& agent : populations) { // split population into the wells given by grad_U auto position = @@ -182,12 +183,12 @@ class QuadWell * @brief Get the number of spatial transitions that happened until the current system state. * @return Matrix with entries (from_well, to_well) for every infection state. */ - const std::vector& number_transitions() const + const std::vector>& number_transitions() const { return m_number_transitions; } - std::vector& number_transitions() + std::vector>& number_transitions() { return m_number_transitions; } @@ -196,7 +197,8 @@ class QuadWell * @brief Get AdoptionRate mapping. * @return Map of AdoptionRates based on their region index and source and target infection state. */ - std::map, mio::AdoptionRate>& get_adoption_rates() + std::map, mio::AdoptionRate>& + get_adoption_rates() { return m_adoption_rates; } @@ -246,7 +248,7 @@ class QuadWell well_index(agent.position) == well_index(contact.position); } - /** + /** * @brief Restrict domain to [-2, 2]^2 where "escaping" is impossible. * @param[in] p Position to check. * @return Boolean specifying whether p is in [-2, 2]^2. @@ -256,13 +258,13 @@ class QuadWell return -2 <= p[0] && p[0] <= 2 && -2 <= p[1] && p[1] <= 2; } - std::map, mio::AdoptionRate> + std::map, mio::AdoptionRate> m_adoption_rates; ///< Map of AdoptionRates according to their region index and their from -> to infection states. - double m_contact_radius; ///< Agents' interaction radius. Within this radius agents are considered as contacts. - double m_sigma; ///< Noise term of the diffusion process. + ScalarType m_contact_radius; ///< Agents' interaction radius. Within this radius agents are considered as contacts. + ScalarType m_sigma; ///< Noise term of the diffusion process. std::vector m_non_moving_states; ///< Infection states within which agents do not change their location. std::vector m_non_moving_regions{}; ///< Regions without movement. - std::vector + std::vector> m_number_transitions; ///< Vector that contains for every infection state a matrix with entry (k,l) the number of spatial transitions from Region k to Regionl. mio::RandomNumberGenerator m_rng; ///< Model's random number generator. }; diff --git a/cpp/models/d_abm/simulation.cpp b/cpp/models/d_abm/simulation.cpp index 22c4784b50..3b538490b8 100644 --- a/cpp/models/d_abm/simulation.cpp +++ b/cpp/models/d_abm/simulation.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2024 MEmilio * * Authors: René Schmieding, Julia Bicker diff --git a/cpp/models/d_abm/simulation.h b/cpp/models/d_abm/simulation.h index 752e617a4d..54ccad8b65 100644 --- a/cpp/models/d_abm/simulation.h +++ b/cpp/models/d_abm/simulation.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 German Aerospace Center (DLR-SC) * * Authors: René Schmieding, Julia Bicker diff --git a/cpp/models/d_abm/single_well.h b/cpp/models/d_abm/single_well.h index 8bccd1aec2..62ef7671c6 100644 --- a/cpp/models/d_abm/single_well.h +++ b/cpp/models/d_abm/single_well.h @@ -41,7 +41,7 @@ class SWPositionSampler * @param[in] top_right Coordinates of the top right corner of the range in which should be samples. * @param[in] margin Margin defining the distance that should be kept from the borders defined by bottom_left and top_right when sampling a position. */ - SWPositionSampler(const Position& bottom_left, const Position& top_right, double margin) + SWPositionSampler(const Position& bottom_left, const Position& top_right, ScalarType margin) { auto assign_range = [&](Position range_x, Position range_y) { range_x += Position{margin, -margin}; @@ -57,10 +57,10 @@ class SWPositionSampler */ Position operator()() const { - return {mio::UniformDistribution::get_instance()(mio::thread_local_rng(), m_range.first[0], - m_range.first[1]), - mio::UniformDistribution::get_instance()(mio::thread_local_rng(), m_range.second[0], - m_range.second[1])}; + return {mio::UniformDistribution::get_instance()(mio::thread_local_rng(), m_range.first[0], + m_range.first[1]), + mio::UniformDistribution::get_instance()(mio::thread_local_rng(), m_range.second[0], + m_range.second[1])}; } private: @@ -93,7 +93,7 @@ class SingleWell * @param[in] sigma Noise term for the diffusion process. * @param[in] non_moving_state InfectionStates that are excluded from movement e.g. Dead. */ - SingleWell(const std::vector& agents, const std::vector>& rates, + SingleWell(const std::vector& agents, const std::vector>& rates, ScalarType contact_radius = 0.4, ScalarType sigma = 0.4, std::vector non_moving_states = {}) : populations(agents) , m_contact_radius(contact_radius) @@ -172,8 +172,9 @@ class SingleWell { if (std::find(m_non_moving_states.begin(), m_non_moving_states.end(), agent.status) == m_non_moving_states.end()) { - Position p = {mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0), - mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0)}; + Position p = { + mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0), + mio::DistributionAdapter>::get_instance()(m_rng, 0.0, 1.0)}; agent.position = agent.position - dt * grad_U(agent.position) + (m_sigma * std::sqrt(dt)) * p; } @@ -184,16 +185,17 @@ class SingleWell * @brief Calculate the current system state i.e. the populations for each infection state. * @return Vector containing the number of agents per infection state. */ - Eigen::VectorXd time_point() const + Eigen::VectorX time_point() const { - Eigen::VectorXd val = Eigen::VectorXd::Zero(static_cast(Status::Count)); + Eigen::VectorX val = Eigen::VectorX::Zero(static_cast(Status::Count)); for (auto& agent : populations) { val[static_cast(agent.status)] += 1; } return val; } - std::map, mio::AdoptionRate>& get_adoption_rates() + std::map, mio::AdoptionRate>& + get_adoption_rates() { return m_adoption_rates; } @@ -239,14 +241,15 @@ class SingleWell * @param[in] p Position to check. * @return Boolean specifying whether p is in [-2, 2]^2. */ - bool is_in_domain(const Position& p, const double lower_domain_border = -2, const double upper_domain_border = 2) const + bool is_in_domain(const Position& p, const ScalarType lower_domain_border = -2, + const ScalarType upper_domain_border = 2) const { // restrict domain to [lower_domain_border, upper_domain_border]^2 where "escaping" is impossible, i.e. it holds x <= grad_U(x) for dt <= 0.1 return lower_domain_border <= p[0] && p[0] <= upper_domain_border && lower_domain_border <= p[1] && p[1] <= upper_domain_border; } - std::map, mio::AdoptionRate> + std::map, mio::AdoptionRate> m_adoption_rates; ///< Map of AdoptionRates according to their region index and their from -> to infection states. ScalarType m_contact_radius; ///< Agents' interaction radius. Within this radius agents are considered as contacts. ScalarType m_sigma; ///< Noise term of the diffusion process. diff --git a/cpp/models/glct_secir/infection_state.h b/cpp/models/glct_secir/infection_state.h index 9a4d741706..033e57bce0 100644 --- a/cpp/models/glct_secir/infection_state.h +++ b/cpp/models/glct_secir/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke diff --git a/cpp/models/glct_secir/model.cpp b/cpp/models/glct_secir/model.cpp index 435cab6d82..f19d3619d7 100644 --- a/cpp/models/glct_secir/model.cpp +++ b/cpp/models/glct_secir/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke diff --git a/cpp/models/glct_secir/model.h b/cpp/models/glct_secir/model.h index 5b925b87f3..f65f015e8d 100644 --- a/cpp/models/glct_secir/model.h +++ b/cpp/models/glct_secir/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -31,6 +31,8 @@ #include "memilio/utils/time_series.h" #include "memilio/math/eigen.h" +#include + namespace mio { namespace glsecir @@ -40,29 +42,28 @@ namespace glsecir * @brief Class that defines an GLCT-SECIR model. * * @tparam NumExposed The number of subcompartments used for the Exposed compartment. - * @tparam NumInfectedNoSymptoms The number of subcompartments used for the InfectedNoSymptoms compartment. + * @tparam NumInfectedNoSymptoms The number of subcompartments used for the InfectedNoSymptoms compartment. * @tparam NumInfectedSymptoms The number of subcompartments used for the InfectedSymptoms compartment. * @tparam NumInfectedSevere The number of subcompartments used for the InfectedSevere compartment. * @tparam NumInfectedCritical The number of subcompartments used for the InfectedCritical compartment. */ -template +template class Model : public CompartmentalModel< - ScalarType, - LctInfectionState, - mio::Populations>, - Parameters> + mio::Populations>, + Parameters> { public: - using LctState = - LctInfectionState; ///< This class specifies the number of subcompartments. - using Base = CompartmentalModel, Parameters>; + using LctState = LctInfectionState; ///< This class specifies the number of subcompartments. + using Base = CompartmentalModel, Parameters>; using typename Base::ParameterSet; using typename Base::Populations; @@ -73,7 +74,7 @@ class Model } /** - * @brief Checks that the model satisfies all constraints (e.g. parameter or population constraints), and + * @brief Checks that the model satisfies all constraints (e.g. parameter or population constraints), and * logs an error if constraints are not satisfied. * * @return Returns true if one or more constraints are not satisfied, false otherwise. @@ -84,35 +85,35 @@ class Model // --- Check that the dimensions are consistent. --- if ((Eigen::Index)LctState::template get_num_subcompartments() != - params.template get().rows()) { + params.template get>().rows()) { log_error("Constraint check: Dimension of the parameters does not match the number of subcompartments for " "the Exposed " "compartment."); return true; } if ((Eigen::Index)LctState::template get_num_subcompartments() != - params.template get().rows()) { + params.template get>().rows()) { log_error( "Constraint check: Dimension of the parameters does not match the number of subcompartments for the " "InfectedNoSymptoms compartment."); return true; } if ((Eigen::Index)LctState::template get_num_subcompartments() != - params.template get().rows()) { + params.template get>().rows()) { log_error( "Constraint check: Dimension of the parameters does not match the number of subcompartments for the " "InfectedSymptoms compartment."); return true; } if ((Eigen::Index)LctState::template get_num_subcompartments() != - params.template get().rows()) { + params.template get>().rows()) { log_error("Constraint check: Dimension of the parameters does not match the number of subcompartments for " "the InfectedSevere " "compartment."); return true; } if ((Eigen::Index)LctState::template get_num_subcompartments() != - params.template get().rows()) { + params.template get>().rows()) { log_error( "Constraint check: Dimension of the parameters does not match the number of subcompartments for the " "InfectedCritical compartment."); @@ -125,7 +126,7 @@ class Model /** * @brief Evaluates the right-hand-side f of the GLCT dydt = f(y, t). * - * The GLCT-SECIR model is defined through ordinary differential equations of the form dydt = f(y, t). + * The GLCT-SECIR model is defined through ordinary differential equations of the form dydt = f(y, t). * y is a vector containing number of individuals for each (sub-) compartment. * This function evaluates the right-hand-side f of the ODE and can be used in an ODE solver. * @@ -134,41 +135,44 @@ class Model * @param[in] t The current time. * @param[out] dydt A reference to the calculated output. */ - void get_derivatives(Eigen::Ref> pop, - Eigen::Ref> y, ScalarType t, - Eigen::Ref> dydt) const override + void get_derivatives(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> dydt) const override { + using std::sin; + dydt.setZero(); auto params = this->parameters; auto total_population = pop.sum() - pop[LctState::template get_first_index()]; // Calculate sum of all subcompartments for InfectedNoSymptoms. - ScalarType InfectedNoSymptoms_sum = + FP InfectedNoSymptoms_sum = pop.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) .sum(); // Calculate sum of all subcompartments for InfectedSymptoms. - ScalarType InfectedSymptoms_sum = + FP InfectedSymptoms_sum = pop.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) .sum(); // --- Susceptibles. --- - ScalarType season_val = 1 + params.template get() * - sin(3.141592653589793 * ((params.template get() + t) / 182.5 + 0.5)); - dydt[0] = -y[0] / total_population * season_val * params.template get() * - params.template get().get_cont_freq_mat().get_matrix_at(t)(0, 0) * - (params.template get() * InfectedNoSymptoms_sum + - params.template get() * InfectedSymptoms_sum); + FP season_val = + 1 + params.template get>() * + sin(std::numbers::pi_v * ((params.template get>() + t) / 182.5 + 0.5)); + dydt[0] = + -y[0] / total_population * season_val * params.template get>() * + params.template get>().get_cont_freq_mat().get_matrix_at(SimulationTime(t))(0, 0) * + (params.template get>() * InfectedNoSymptoms_sum + + params.template get>() * InfectedSymptoms_sum); // --- Exposed. --- dydt.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) -= - dydt[0] * params.template get(); + dydt[0] * params.template get>(); dydt.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()); @@ -176,34 +180,34 @@ class Model // Flow from Exposed To InfectedNoSymptoms. dydt.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) = - -(params.template get() * - Eigen::VectorX::Ones(LctState::template get_num_subcompartments())) + -(params.template get>() * + Eigen::VectorX::Ones(LctState::template get_num_subcompartments())) .transpose() * y.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) * - params.template get(); + params.template get>(); // Flow from InfectedNoSymptoms To InfectedSymptoms. size_t dimensionInfectedNoSymptomsToInfectedSymptoms = - params.template get().rows(); + params.template get>().rows(); dydt.segment(LctState::template get_first_index(), dimensionInfectedNoSymptomsToInfectedSymptoms) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedNoSymptomsToInfectedSymptoms); // Flow from InfectedNoSymptoms To Recovered. size_t dimensionInfectedNoSymptomsToRecovered = - params.template get().rows(); + params.template get>().rows(); dydt.segment(LctState::template get_first_index() + dimensionInfectedNoSymptomsToInfectedSymptoms, dimensionInfectedNoSymptomsToRecovered) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedNoSymptomsToInfectedSymptoms, dimensionInfectedNoSymptomsToRecovered); // Add flow directly to Recovered compartment. dydt[LctState::template get_first_index()] += - -(params.template get() * - Eigen::VectorX::Ones(dimensionInfectedNoSymptomsToRecovered)) + -(params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedNoSymptomsToRecovered)) .transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedNoSymptomsToInfectedSymptoms, @@ -213,34 +217,34 @@ class Model // Flow from InfectedNoSymptoms To InfectedSymptoms. dydt.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) = - -params.template get() * - (params.template get() * - Eigen::VectorX::Ones(dimensionInfectedNoSymptomsToInfectedSymptoms)) + -params.template get>() * + (params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedNoSymptomsToInfectedSymptoms)) .transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedNoSymptomsToInfectedSymptoms); // Flow from InfectedSymptoms To InfectedSevere. size_t dimensionInfectedSymptomsToInfectedSevere = - params.template get().rows(); + params.template get>().rows(); dydt.segment(LctState::template get_first_index(), dimensionInfectedSymptomsToInfectedSevere) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedSymptomsToInfectedSevere); // Flow from InfectedSymptoms To Recovered. size_t dimensionInfectedSymptomsToRecovered = - params.template get().rows(); + params.template get>().rows(); dydt.segment(LctState::template get_first_index() + dimensionInfectedSymptomsToInfectedSevere, dimensionInfectedSymptomsToRecovered) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedSymptomsToInfectedSevere, dimensionInfectedSymptomsToRecovered); // Add flow directly to Recovered compartment. dydt[LctState::template get_first_index()] += - -(params.template get() * - Eigen::VectorX::Ones(dimensionInfectedSymptomsToRecovered)) + -(params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedSymptomsToRecovered)) .transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedSymptomsToInfectedSevere, @@ -250,34 +254,34 @@ class Model // Flow from InfectedSymptoms To InfectedSevere. dydt.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) = - -params.template get() * - (params.template get() * - Eigen::VectorX::Ones(dimensionInfectedSymptomsToInfectedSevere)) + -params.template get>() * + (params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedSymptomsToInfectedSevere)) .transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedSymptomsToInfectedSevere); // Flow from InfectedSevere To InfectedCritical. size_t dimensionInfectedSevereToInfectedCritical = - params.template get().rows(); + params.template get>().rows(); dydt.segment(LctState::template get_first_index(), dimensionInfectedSevereToInfectedCritical) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedSevereToInfectedCritical); // Flow from InfectedSevere To Recovered. size_t dimensionInfectedSevereToRecovered = - params.template get().rows(); + params.template get>().rows(); dydt.segment(LctState::template get_first_index() + dimensionInfectedSevereToInfectedCritical, dimensionInfectedSevereToRecovered) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedSevereToInfectedCritical, dimensionInfectedSevereToRecovered); // Add flow directly to Recovered compartment. dydt[LctState::template get_first_index()] += - -(params.template get() * - Eigen::VectorX::Ones(dimensionInfectedSevereToRecovered)) + -(params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedSevereToRecovered)) .transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedSevereToInfectedCritical, @@ -287,33 +291,34 @@ class Model // Flow from InfectedSevere To InfectedCritical. dydt.segment(LctState::template get_first_index(), LctState::template get_num_subcompartments()) = - -params.template get() * - (params.template get() * - Eigen::VectorX::Ones(dimensionInfectedSevereToInfectedCritical)) + -params.template get>() * + (params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedSevereToInfectedCritical)) .transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedSevereToInfectedCritical); // Flow from InfectedCritical To Dead. - size_t dimensionInfectedCriticalToDead = params.template get().rows(); + size_t dimensionInfectedCriticalToDead = + params.template get>().rows(); dydt.segment(LctState::template get_first_index(), dimensionInfectedCriticalToDead) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedCriticalToDead); // Flow from InfectedCritical To Recovered. size_t dimensionInfectedCriticalToRecovered = - params.template get().rows(); + params.template get>().rows(); dydt.segment(LctState::template get_first_index() + dimensionInfectedCriticalToDead, dimensionInfectedCriticalToRecovered) += - params.template get().transpose() * + params.template get>().transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedCriticalToDead, dimensionInfectedCriticalToRecovered); // Add flow directly to Recovered compartment. dydt[LctState::template get_first_index()] += - -(params.template get() * - Eigen::VectorX::Ones(dimensionInfectedCriticalToRecovered)) + -(params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedCriticalToRecovered)) .transpose() * y.segment(LctState::template get_first_index() + dimensionInfectedCriticalToDead, @@ -321,8 +326,8 @@ class Model // --- Dead. --- dydt[LctState::template get_first_index()] = - -(params.template get() * - Eigen::VectorX::Ones(dimensionInfectedCriticalToDead)) + -(params.template get>() * + Eigen::VectorX::Ones(dimensionInfectedCriticalToDead)) .transpose() * y.segment(LctState::template get_first_index(), dimensionInfectedCriticalToDead); @@ -332,26 +337,25 @@ class Model * @brief Cumulates a simulation result with subcompartments to produce a result that divides the population only * into the infection states defined in InfectionState. * - * If the model is used for simulation, we will get a result in form of a TimeSeries with infection states divided + * If the model is used for simulation, we will get a result in form of a TimeSeries with infection states divided * in subcompartments. - * The function calculates a TimeSeries without subcompartments from another TimeSeries with subcompartments. + * The function calculates a TimeSeries without subcompartments from another TimeSeries with subcompartments. * This is done by summing up the corresponding subcompartments. * @param[in] subcompartments_ts Result of a simulation with the model. - * @return Result of the simulation divided in infection states without subcompartments. + * @return Result of the simulation divided in infection states without subcompartments. * Returns TimeSeries with values -1 if calculation is not possible. */ - TimeSeries calculate_compartments(const TimeSeries& subcompartments_ts) const + TimeSeries calculate_compartments(const TimeSeries& subcompartments_ts) const { - TimeSeries compartments_ts((Eigen::Index)InfectionState::Count); + TimeSeries compartments_ts((Eigen::Index)InfectionState::Count); if (!(LctState::Count == subcompartments_ts.get_num_elements())) { log_error("Result does not match InfectionStates of the model."); // Return a TimeSeries with values -1. - Eigen::VectorX error_output = - Eigen::VectorX::Constant((Eigen::Index)InfectionState::Count, -1); + Eigen::VectorX error_output = Eigen::VectorX::Constant((Eigen::Index)InfectionState::Count, -1); compartments_ts.add_time_point(-1, error_output); return compartments_ts; } - Eigen::VectorX compartments((Eigen::Index)InfectionState::Count); + Eigen::VectorX compartments((Eigen::Index)InfectionState::Count); for (Eigen::Index i = 0; i < subcompartments_ts.get_num_time_points(); ++i) { compartments_ts.add_time_point(subcompartments_ts.get_time(i), LctState::calculate_compartments(subcompartments_ts[i])); diff --git a/cpp/models/glct_secir/parameters.h b/cpp/models/glct_secir/parameters.h index 3e3dc872cf..adb7932072 100644 --- a/cpp/models/glct_secir/parameters.h +++ b/cpp/models/glct_secir/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -38,16 +38,17 @@ namespace glsecir ***********************************************/ /// @brief Vector with the probability to start in any of the subcompartments of the Exposed compartment. +template struct StartingProbabilitiesExposed { - using Type = Eigen::VectorX; - /** + using Type = Eigen::VectorX; + /** * @brief Default parameters can be used to get an Erlang distributed stay time in the Exposed compartment. * @param[in] numExposed Number of subcompartments of the Exposed compartment. */ static Type get_default(size_t numExposed) { - Eigen::VectorX def = Eigen::VectorX::Zero(numExposed); - def[0] = 1.; + Eigen::VectorX def = Eigen::VectorX::Zero(numExposed); + def[0] = 1.; return def; } static std::string name() @@ -57,18 +58,18 @@ struct StartingProbabilitiesExposed { }; /// @brief Transition matrix of the Exposed compartment. +template struct TransitionMatrixExposedToInfectedNoSymptoms { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in the Exposed compartment. * @param[in] numExposed Number of subcompartments of the Exposed compartment. * @param[in] timeExposed Average time spent in Exposed compartment in day unit. */ - static Type get_default(size_t numExposed, ScalarType timeExposed = 1.) + static Type get_default(size_t numExposed, FP timeExposed = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(numExposed, -(ScalarType)numExposed / timeExposed).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)numExposed / timeExposed); + Eigen::MatrixX def = Eigen::VectorX::Constant(numExposed, -(FP)numExposed / timeExposed).asDiagonal(); + def.diagonal(1).setConstant((FP)numExposed / timeExposed); return def; } static std::string name() @@ -78,16 +79,17 @@ struct TransitionMatrixExposedToInfectedNoSymptoms { }; /// @brief Vector with the probability to start in any of the subcompartments of the InfectedNoSymptoms compartment. +template struct StartingProbabilitiesInfectedNoSymptoms { - using Type = Eigen::VectorX; + using Type = Eigen::VectorX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedNoSymptoms compartment. * @param[in] numInfectedNoSymptoms Number of subcompartments of the InfectedNoSymptoms compartment. */ static Type get_default(size_t numInfectedNoSymptoms) { - Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedNoSymptoms); - def[0] = 1.; + Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedNoSymptoms); + def[0] = 1.; return def; } static std::string name() @@ -97,22 +99,22 @@ struct StartingProbabilitiesInfectedNoSymptoms { }; /** - * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedNoSymptoms + * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedNoSymptoms * compartment before developing symptoms. */ +template struct TransitionMatrixInfectedNoSymptomsToInfectedSymptoms { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedNoSymptoms compartment * before developing symptoms. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time spent in InfectedNoSymptoms before developing symptoms in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -122,22 +124,22 @@ struct TransitionMatrixInfectedNoSymptomsToInfectedSymptoms { }; /** - * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedNoSymptoms + * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedNoSymptoms * compartment before recovery. */ +template struct TransitionMatrixInfectedNoSymptomsToRecovered { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedNoSymptoms compartment * before recovery. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time spent in InfectedNoSymptoms before recovery in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -147,16 +149,17 @@ struct TransitionMatrixInfectedNoSymptomsToRecovered { }; /// @brief Vector with the probability to start in any of the subcompartments of the InfectedSymptoms compartment. +template struct StartingProbabilitiesInfectedSymptoms { - using Type = Eigen::VectorX; + using Type = Eigen::VectorX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedSymptoms compartment. * @param[in] numInfectedSymptoms Number of subcompartments of the InfectedSymptoms compartment. */ static Type get_default(size_t numInfectedSymptoms) { - Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedSymptoms); - def[0] = 1.; + Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedSymptoms); + def[0] = 1.; return def; } static std::string name() @@ -166,22 +169,22 @@ struct StartingProbabilitiesInfectedSymptoms { }; /** - * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedNoSymptoms + * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedNoSymptoms * compartment before going to hospital. */ +template struct TransitionMatrixInfectedSymptomsToInfectedSevere { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in the InfectedSymptoms compartment * before going to hospital. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time spent in InfectedSymptoms before going to hospital in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -191,22 +194,22 @@ struct TransitionMatrixInfectedSymptomsToInfectedSevere { }; /** - * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedSymptoms + * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedSymptoms * compartment before recovery. */ +template struct TransitionMatrixInfectedSymptomsToRecovered { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in the InfectedSymptoms compartment * before recovery. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time spent in InfectedSymptoms before recovery in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -216,16 +219,17 @@ struct TransitionMatrixInfectedSymptomsToRecovered { }; /// @brief Vector with the probability to start in any of the subcompartments of the InfectedSevere compartment. +template struct StartingProbabilitiesInfectedSevere { - using Type = Eigen::VectorX; + using Type = Eigen::VectorX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedSevere compartment. * @param[in] numInfectedSevere Number of subcompartments of the InfectedSevere compartment. */ static Type get_default(size_t numInfectedSevere) { - Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedSevere); - def[0] = 1.; + Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedSevere); + def[0] = 1.; return def; } static std::string name() @@ -238,19 +242,19 @@ struct StartingProbabilitiesInfectedSevere { * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedSevere * compartment before treated by ICU. */ +template struct TransitionMatrixInfectedSevereToInfectedCritical { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedSevere compartment * before treated by ICU. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time spent in InfectedSevere before treated by ICU in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -263,19 +267,19 @@ struct TransitionMatrixInfectedSevereToInfectedCritical { * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedSevere * compartment before recovery. */ +template struct TransitionMatrixInfectedSevereToRecovered { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedSevere compartment * before recovery. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time spent in InfectedSevere before recovery in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -285,16 +289,17 @@ struct TransitionMatrixInfectedSevereToRecovered { }; /// @brief Vector with the probability to start in any of the subcompartments of the InfectedCritical compartment. +template struct StartingProbabilitiesInfectedCritical { - using Type = Eigen::VectorX; + using Type = Eigen::VectorX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedCritical compartment. * @param[in] numInfectedCritical Number of subcompartments of the InfectedCritical compartment. */ static Type get_default(size_t numInfectedCritical) { - Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedCritical); - def[0] = 1.; + Eigen::VectorX def = Eigen::VectorX::Zero(numInfectedCritical); + def[0] = 1.; return def; } static std::string name() @@ -307,19 +312,19 @@ struct StartingProbabilitiesInfectedCritical { * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedCritical * compartment before death. */ +template struct TransitionMatrixInfectedCriticalToDead { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedCritical compartment * before death. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time treated by ICU before dying in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -332,19 +337,19 @@ struct TransitionMatrixInfectedCriticalToDead { * @brief Transition matrix of the phase-type distribution describing the stay time in the InfectedCritical * compartment before recovery. */ +template struct TransitionMatrixInfectedCriticalToRecovered { - using Type = Eigen::MatrixXd; + using Type = Eigen::MatrixX; /** * @brief Default parameters can be used to get an Erlang distributed stay time in InfectedCritical compartment * before recovery. * @param[in] dimension Number of rows/columns of the transition matrix. * @param[in] time Average time treated by ICU before recovery in day unit. */ - static Type get_default(size_t dimension, ScalarType time = 1.) + static Type get_default(size_t dimension, FP time = 1.) { - Eigen::MatrixXd def = - Eigen::VectorX::Constant(dimension, -(ScalarType)dimension / time).asDiagonal(); - def.diagonal(1).setConstant((ScalarType)dimension / time); + Eigen::MatrixX def = Eigen::VectorX::Constant(dimension, -(FP)dimension / time).asDiagonal(); + def.diagonal(1).setConstant((FP)dimension / time); return def; } static std::string name() @@ -354,11 +359,12 @@ struct TransitionMatrixInfectedCriticalToRecovered { }; /// @brief Probability of getting infected from a contact. +template struct TransmissionProbabilityOnContact { - using Type = ScalarType; + using Type = FP; static Type get_default() { - return 1.0; + return Type(1.0); } static std::string name() { @@ -367,13 +373,14 @@ struct TransmissionProbabilityOnContact { }; /// @brief The contact patterns within the society are modelled using an UncertainContactMatrix. +template struct ContactPatterns { - using Type = UncertainContactMatrix; + using Type = UncertainContactMatrix; static Type get_default() { - ContactMatrixGroup contact_matrix = ContactMatrixGroup(1, 1); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10.)); + ContactMatrixGroup contact_matrix = ContactMatrixGroup(1, 1); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10.)); return Type(contact_matrix); } static std::string name() @@ -383,11 +390,12 @@ struct ContactPatterns { }; /// @brief The relative InfectedNoSymptoms infectability. +template struct RelativeTransmissionNoSymptoms { - using Type = ScalarType; + using Type = FP; static Type get_default() { - return 0.5; + return Type(0.5); } static std::string name() { @@ -396,11 +404,12 @@ struct RelativeTransmissionNoSymptoms { }; /// @brief The risk of infection from symptomatic cases in the GLCT-SECIR model. +template struct RiskOfInfectionFromSymptomatic { - using Type = ScalarType; + using Type = FP; static Type get_default() { - return 0.5; + return Type(0.5); } static std::string name() { @@ -414,11 +423,12 @@ struct RiskOfInfectionFromSymptomatic { * If the start day is 180 and simulation takes place from t0=0 to * tmax=100 the days 180 to 280 of the year are simulated. */ +template struct StartDay { - using Type = ScalarType; - static Type get_default() + using Type = FP; + static Type get_default(size_t) { - return 0.; + return Type(0.0); } static std::string name() { @@ -431,8 +441,9 @@ struct StartDay { * The seasonality is given as (1+k*sin()) where the sine * curve is below one in summer and above one in winter. */ +template struct Seasonality { - using Type = ScalarType; + using Type = FP; static Type get_default() { return Type(0.); @@ -443,24 +454,26 @@ struct Seasonality { } }; +template using ParametersBase = - ParameterSet; + ParameterSet, TransitionMatrixExposedToInfectedNoSymptoms, + StartingProbabilitiesInfectedNoSymptoms, TransitionMatrixInfectedNoSymptomsToInfectedSymptoms, + TransitionMatrixInfectedNoSymptomsToRecovered, StartingProbabilitiesInfectedSymptoms, + TransitionMatrixInfectedSymptomsToInfectedSevere, TransitionMatrixInfectedSymptomsToRecovered, + StartingProbabilitiesInfectedSevere, TransitionMatrixInfectedSevereToInfectedCritical, + TransitionMatrixInfectedSevereToRecovered, StartingProbabilitiesInfectedCritical, + TransitionMatrixInfectedCriticalToDead, TransitionMatrixInfectedCriticalToRecovered, + TransmissionProbabilityOnContact, ContactPatterns, RelativeTransmissionNoSymptoms, + RiskOfInfectionFromSymptomatic, StartDay, Seasonality>; /// @brief Parameters of an GLCT-SECIR model. -class Parameters : public ParametersBase +template +class Parameters : public ParametersBase { public: /// @brief Default constructor. Parameters() - : ParametersBase() + : ParametersBase() { } @@ -473,107 +486,109 @@ class Parameters : public ParametersBase bool check_constraints() const { // --- Parameters affecting the transmission of the virus. --- - if (this->get() < 0.0 || - this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_error("Constraint check: Parameter TransmissionProbabilityOnContact smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get() < 0.0 || this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_error("Constraint check: Parameter RelativeTransmissionNoSymptoms smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get() < 0.0 || this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_error("Constraint check: Parameter RiskOfInfectionFromSymptomatic smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get() < 0.0 || this->get() > 0.5) { + if (this->template get>() < 0.0 || this->template get>() > 0.5) { log_warning("Constraint check: Parameter Seasonality should lie between {:0.4f} and {:.4f}", 0.0, 0.5); return true; } // --- Parameters affecting the phase-type distributions. --- // --- Check that the dimensions are consistent. --- - if ((this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows()) || - (this->get().cols() != - this->get().rows())) { + if ((this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows()) || + (this->template get>().cols() != + this->template get>().rows())) { log_error("Constraint check: At least one of the matrices used for the TransitionMatrix parameters is not " "quadratic."); return true; } - if (this->get().rows() != - this->get().rows()) { + if (this->template get>().rows() != + this->template get>().rows()) { log_error("Constraint check: Dimensions of StartingProbabilitiesExposed and " "TransitionMatrixExposedToInfectedNoSymptoms are not matching."); return true; } - if (this->get().rows() != - this->get().rows() + - this->get().rows()) { + if (this->template get>().rows() != + this->template get>().rows() + + this->template get>().rows()) { log_error("Constraint check: Dimensions of StartingProbabilitiesInfectedNoSymptoms and " "TransitionMatrices of InfectedNoSymptoms compartment are not matching."); return true; } - if (this->get().rows() != - this->get().rows() + - this->get().rows()) { + if (this->template get>().rows() != + this->template get>().rows() + + this->template get>().rows()) { log_error("Constraint check: Dimensions of StartingProbabilitiesInfectedSymptoms and " "TransitionMatrices of InfectedSymptoms compartment are not matching."); return true; } - if (this->get().rows() != - this->get().rows() + - this->get().rows()) { + if (this->template get>().rows() != + this->template get>().rows() + + this->template get>().rows()) { log_error("Constraint check: Dimensions of StartingProbabilitiesInfectedSevere and " "TransitionMatrices of InfectedSevere compartment are not matching."); return true; } - if (this->get().rows() != - this->get().rows() + - this->get().rows()) { + if (this->template get>().rows() != + this->template get>().rows() + + this->template get>().rows()) { log_error("Constraint check: Dimensions of StartingProbabilitiesInfectedCritical and " "TransitionMatrices of InfectedCritical compartment are not matching."); return true; } // --- Check constraints of the starting probability vectors. --- - if ((!floating_point_equal(1., this->get().sum())) || - (!floating_point_equal(1., this->get().sum())) || - (!floating_point_equal(1., this->get().sum())) || - (!floating_point_equal(1., this->get().sum())) || - (!floating_point_equal(1., this->get().sum()))) { + if ((!floating_point_equal(1., this->template get>().sum())) || + (!floating_point_equal(1., this->template get>().sum())) || + (!floating_point_equal(1., this->template get>().sum())) || + (!floating_point_equal(1., this->template get>().sum())) || + (!floating_point_equal(1., this->template get>().sum()))) { log_warning( "Constraint check: At least one of the vectors for the starting probabilities does not sum to one."); return true; } - if ((this->get().array() < -1e-10).any() || - (this->get().array() < -1e-10).any() || - (this->get().array() < -1e-10).any() || - (this->get().array() < -1e-10).any() || - (this->get().array() < -1e-10).any()) { + if ((this->template get>().array() < -1e-10).any() || + (this->template get>().array() < -1e-10).any() || + (this->template get>().array() < -1e-10).any() || + (this->template get>().array() < -1e-10).any() || + (this->template get>().array() < -1e-10).any()) { log_warning("Constraint check: At least one of the vectors for the starting probabilities has at least one " "negative entry."); return true; @@ -581,8 +596,8 @@ class Parameters : public ParametersBase // --- Check that we have no flows back from one compartment to the previous one // (only in between of the subcompartments). --- - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones(this->template get>().rows())) .array() > 1e-10) .any()) { log_warning( @@ -590,9 +605,9 @@ class Parameters : public ParametersBase "flow ExposedToInfectedNoSymptoms."); return true; } - if (((this->get() * - Eigen::VectorX::Ones( - this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones( + this->template get>().rows())) .array() > 1e-10) .any()) { log_warning("Constraint check: The entries of TransitionMatrixInfectedNoSymptomsToInfectedSymptoms lead to " @@ -600,8 +615,8 @@ class Parameters : public ParametersBase "flow InfectedNoSymptomsToInfectedSymptoms."); return true; } - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones(this->template get>().rows())) .array() > 1e-10) .any()) { log_warning( @@ -609,8 +624,9 @@ class Parameters : public ParametersBase "flow InfectedNoSymptomsToRecovered."); return true; } - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones( + this->template get>().rows())) .array() > 1e-10) .any()) { log_warning( @@ -618,8 +634,8 @@ class Parameters : public ParametersBase "flow InfectedSymptomsToInfectedSevere."); return true; } - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones(this->template get>().rows())) .array() > 1e-10) .any()) { log_warning( @@ -627,8 +643,9 @@ class Parameters : public ParametersBase "flow InfectedSymptomsToRecovered."); return true; } - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones( + this->template get>().rows())) .array() > 1e-10) .any()) { log_warning( @@ -636,24 +653,24 @@ class Parameters : public ParametersBase "flow InfectedSevereToInfectedCritical."); return true; } - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones(this->template get>().rows())) .array() > 1e-10) .any()) { log_warning("Constraint check: The entries of TransitionMatrixInfectedSevereToRecovered lead to a negative " "flow InfectedSevereToRecovered."); return true; } - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones(this->template get>().rows())) .array() > 1e-10) .any()) { log_warning("Constraint check: The entries of TransitionMatrixInfectedCriticalToDead lead to a negative " "flow InfectedCriticalToDead."); return true; } - if (((this->get() * - Eigen::VectorX::Ones(this->get().rows())) + if (((this->template get>() * + Eigen::VectorX::Ones(this->template get>().rows())) .array() > 1e-10) .any()) { log_warning( @@ -666,8 +683,8 @@ class Parameters : public ParametersBase } private: - Parameters(ParametersBase&& base) - : ParametersBase(std::move(base)) + Parameters(ParametersBase&& base) + : ParametersBase(std::move(base)) { } @@ -679,7 +696,7 @@ class Parameters : public ParametersBase template static IOResult deserialize(IOContext& io) { - BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); return success(Parameters(std::move(base))); } }; diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 1dd95c4c4f..2d7a749edf 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -107,7 +107,7 @@ class ABMMobilityEdge public: /** - * @brief Exchanges persons via the edge. + * @brief Exchanges persons via the edge. * Commuters are given by the person buffer of node_from. * @param[in] node_from Commuters home node * @param[in] node_to Node commuters (temporarily) move to @@ -151,7 +151,7 @@ class ABMMobilityEdge }; /** - * @brief Edge functor for abm graph simulation. + * @brief Edge functor for abm graph simulation. * @see ABMMobilityEdge::apply_mobility * The attribute dt is required by the GraphSimulation class and therefore an input argument of the function. * However it is not used in ABMMobilityEdge::apply_mobility. @@ -182,21 +182,23 @@ void advance_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode -GraphSimulation, ABMMobilityEdge>, abm::TimePoint, abm::TimeSpan, +GraphSimulation, ABMMobilityEdge>, abm::TimePoint, + abm::TimeSpan, void (*)(mio::abm::TimePoint, mio::abm::TimeSpan, mio::ABMMobilityEdge&, mio::ABMSimulationNode&, mio::ABMSimulationNode&), void (*)(mio::abm::TimePoint, mio::abm::TimeSpan, mio::ABMSimulationNode&)> make_abm_graph_sim(abm::TimePoint t0, abm::TimeSpan dt, Graph, ABMMobilityEdge>&& graph) { - return make_graph_sim(t0, dt, std::move(graph), &advance_model, &apply_mobility); + return make_graph_sim(t0, dt, std::move(graph), &advance_model, + &apply_mobility); } } // namespace mio diff --git a/cpp/models/graph_abm/graph_abmodel.h b/cpp/models/graph_abm/graph_abmodel.h index 5b9b901512..0be1a7f222 100644 --- a/cpp/models/graph_abm/graph_abmodel.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -55,14 +55,14 @@ class GraphABModel : public abm::Model } /** - * @brief Get person buffer. + * @brief Get person buffer. */ std::vector& get_person_buffer() { return m_person_buffer; } - /** + /** * @brief Removes person from the model. * @param[in] pos Index of person in m_persons vector. */ @@ -73,7 +73,7 @@ class GraphABModel : public abm::Model Base::m_person_ids_equal_index = false; } - /** + /** * @brief Evolve the Graph Model one time step. * @param[in] t Current time. * @param[in] dt Length of the time step. diff --git a/cpp/models/hybrid/conversion_functions.cpp b/cpp/models/hybrid/conversion_functions.cpp index e053c678c2..8fa4b047b4 100644 --- a/cpp/models/hybrid/conversion_functions.cpp +++ b/cpp/models/hybrid/conversion_functions.cpp @@ -36,7 +36,7 @@ namespace hybrid { template <> void convert_model(const dabm::Simulation>& current_model, - smm::Simulation<1, mio::osecir::InfectionState>& target_model) + smm::Simulation& target_model) { auto& current_result = current_model.get_result(); auto& target_result = target_model.get_result(); @@ -58,7 +58,7 @@ void convert_model(const dabm::Simulation -void convert_model(const smm::Simulation<1, mio::osecir::InfectionState>& current_model, +void convert_model(const smm::Simulation& current_model, dabm::Simulation>& target_model) { auto& current_result = current_model.get_result(); @@ -74,8 +74,8 @@ void convert_model(const smm::Simulation<1, mio::osecir::InfectionState>& curren target_result.get_last_value() = current_result.get_last_value(); // Update agents' infection state and sample agents position - auto current_pop = current_result.get_last_value().eval(); - double total_pop = std::accumulate(current_pop.begin(), current_pop.end(), 0.0); + auto current_pop = current_result.get_last_value().eval(); + ScalarType total_pop = std::accumulate(current_pop.begin(), current_pop.end(), 0.0); SWPositionSampler pos_rng{{-1, -1}, {1, 1}, 0.1}; auto& state_rng = DiscreteDistribution::get_instance(); auto& abm_pop = target_model.get_model().populations; @@ -104,7 +104,7 @@ void convert_model(const smm::Simulation<1, mio::osecir::InfectionState>& curren template <> void convert_model(const dabm::Simulation>& current_model, - mio::Simulation>& target_model) + mio::Simulation>& target_model) { auto& current_result = current_model.get_result(); auto& target_result = target_model.get_result(); @@ -119,7 +119,8 @@ void convert_model(const dabm::Simulation -void convert_model(const mio::Simulation>& current_model, +void convert_model(const mio::Simulation>& current_model, dabm::Simulation>& target_model) { auto& current_result = current_model.get_result(); @@ -157,8 +158,8 @@ void convert_model(const mio::Simulation>& cu } // Update agents' infection state and sample agents position - auto current_pop = target_result.get_last_value().eval(); - double total_pop = std::accumulate(current_pop.begin(), current_pop.end(), 0.0); + auto current_pop = target_result.get_last_value().eval(); + ScalarType total_pop = std::accumulate(current_pop.begin(), current_pop.end(), 0.0); SWPositionSampler pos_rng{{-1, -1}, {1, 1}, 0.1}; auto& state_rng = DiscreteDistribution::get_instance(); auto& abm_pop = target_model.get_model().populations; diff --git a/cpp/models/hybrid/conversion_functions.h b/cpp/models/hybrid/conversion_functions.h index a664593f4c..9d0e316543 100644 --- a/cpp/models/hybrid/conversion_functions.h +++ b/cpp/models/hybrid/conversion_functions.h @@ -37,18 +37,18 @@ namespace hybrid template <> void convert_model(const dabm::Simulation>& current_model, - smm::Simulation<1, mio::osecir::InfectionState>& target_model); + smm::Simulation& target_model); template <> -void convert_model(const smm::Simulation<1, mio::osecir::InfectionState>& current_model, +void convert_model(const smm::Simulation& current_model, dabm::Simulation>& target_model); template <> void convert_model(const dabm::Simulation>& current_model, - mio::Simulation>& target_model); + mio::Simulation>& target_model); template <> -void convert_model(const mio::Simulation>& current_model, +void convert_model(const mio::Simulation>& current_model, dabm::Simulation>& target_model); } //namespace hybrid diff --git a/cpp/models/hybrid/temporal_hybrid_model.h b/cpp/models/hybrid/temporal_hybrid_model.h index 0816a44f42..25ea35d8d0 100644 --- a/cpp/models/hybrid/temporal_hybrid_model.h +++ b/cpp/models/hybrid/temporal_hybrid_model.h @@ -24,6 +24,9 @@ #include #include #include + +#include "memilio/config.h" + namespace mio { namespace hybrid @@ -54,8 +57,8 @@ class TemporalHybridSimulation public: //Functions returning the result/current state of both models - using result1_function = std::function; - using result2_function = std::function; + using result1_function = std::function; + using result2_function = std::function; //Should return true when the simulation should be continued with the model that is not used currently i.e. a switch needs to be applied using switching_condition = @@ -72,7 +75,8 @@ class TemporalHybridSimulation * @param[in] dt Timestep with which the switching is checked. */ TemporalHybridSimulation(Model1&& model1, Model2&& model2, const result1_function& result1, - const result2_function& result2, bool initially_use_model1, double t0 = 0, double dt = 0.1) + const result2_function& result2, bool initially_use_model1, ScalarType t0 = 0, + ScalarType dt = 0.1) : m_model1(std::move(model1)) , m_model2(std::move(model2)) , m_result1(result1) @@ -88,7 +92,7 @@ class TemporalHybridSimulation * @param[in] tmax End time point of the simulation * @param[in] switch_model Switching condition that is checked every m_dt step. */ - void advance(double tmax, const switching_condition& switch_model) + void advance(ScalarType tmax, const switching_condition& switch_model) { while (m_t < tmax) { bool condition = switch_model(get_result_model1(), get_result_model2(), m_using_model1); @@ -104,7 +108,7 @@ class TemporalHybridSimulation m_using_model1 = true; } //else{Switching condition is not fulfilled and currently used model is just advanced} - double next_step = std::min(m_dt, tmax - m_t); + ScalarType next_step = std::min(m_dt, tmax - m_t); if (m_using_model1) { m_model1.advance(m_t + next_step); } @@ -175,8 +179,8 @@ class TemporalHybridSimulation result1_function m_result1; ///< Result function of first model. result2_function m_result2; ///< Result function of second model. bool m_using_model1; ///< Boolean specifying whether model 1 is currently used for simulation. - double m_t; ///< Current time step. - double m_dt; ///< Step size with which the switching condition is checked. + ScalarType m_t; ///< Current time step. + ScalarType m_dt; ///< Step size with which the switching condition is checked. }; } //namespace hybrid diff --git a/cpp/models/ide_secir/model.cpp b/cpp/models/ide_secir/model.cpp index b1a1b87738..7c45c640ba 100644 --- a/cpp/models/ide_secir/model.cpp +++ b/cpp/models/ide_secir/model.cpp @@ -29,6 +29,7 @@ #include "vector" #include #include +#include namespace mio { @@ -589,9 +590,9 @@ void Model::compute_forceofinfection(ScalarType dt, bool initialization) } //We compute the Season Value. ScalarType season_val = - 1 + - parameters.get() * - sin(3.141592653589793 * (std::fmod((parameters.get() + current_time), 365.0) / 182.5 + 0.5)); + 1 + parameters.get() * + sin(std::numbers::pi_v * + (std::fmod((parameters.get() + current_time), 365.0) / 182.5 + 0.5)); // To include contacts between all age groups we sum over all age groups. for (AgeGroup j = AgeGroup(0); j < AgeGroup(m_num_agegroups); ++j) { // Determine the relevant calculation area = union of the supports of the relevant transition distributions. @@ -630,8 +631,8 @@ void Model::compute_forceofinfection(ScalarType dt, bool initialization) Eigen::Index state_age_index = num_time_points - 1 - l; ScalarType state_age = state_age_index * dt; sum += season_val * parameters.get()[i].eval(state_age) * - parameters.get().get_cont_freq_mat().get_matrix_at(current_time)( - static_cast((size_t)i), static_cast((size_t)j)) * + parameters.get().get_cont_freq_mat().get_matrix_at(SimulationTime( + current_time))(static_cast((size_t)i), static_cast((size_t)j)) * (m_transitiondistributions_in_forceofinfection[j][0][num_time_points - l - 1] * transitions[l + 1][EtINSj] * parameters.get()[j].eval(state_age) + @@ -639,7 +640,7 @@ void Model::compute_forceofinfection(ScalarType dt, bool initialization) transitions[l + 1][INStISyj] * parameters.get()[j].eval(state_age)); } - const double divNj = + const ScalarType divNj = (m_N[j] - deaths_j < Limits::zero_tolerance()) ? 0.0 : 1.0 / (m_N[j] - deaths_j); m_forceofinfection[i] += divNj * sum; } diff --git a/cpp/models/ide_secir/model.h b/cpp/models/ide_secir/model.h index 98cff9ea7b..b5f5b4b243 100644 --- a/cpp/models/ide_secir/model.h +++ b/cpp/models/ide_secir/model.h @@ -166,7 +166,7 @@ class Model transitions; ///< TimeSeries containing points of time and the corresponding number of individuals transitioning from // one #InfectionState to another as defined in #InfectionTransition%s for every AgeGroup. TimeSeries populations; ///< TimeSeries containing points of time and the corresponding number of - // people in defined #InfectionState%s for every AgeGroup. + // people in defined #InfectionState%s for every AgeGroup. CustomIndexArray total_confirmed_cases; ///< CustomIndexArray that contains the total number of confirmed cases at time t0 for every AgeGroup. @@ -385,4 +385,4 @@ class Model } // namespace isecir } // namespace mio -#endif // IDESECIR_MODEL_H +#endif // IDESECIR_MODEL_H \ No newline at end of file diff --git a/cpp/models/ide_secir/parameters.h b/cpp/models/ide_secir/parameters.h index 3d67b5eb24..de0aa5c85e 100644 --- a/cpp/models/ide_secir/parameters.h +++ b/cpp/models/ide_secir/parameters.h @@ -55,13 +55,13 @@ namespace isecir */ struct TransitionDistributions { - using Type = CustomIndexArray, AgeGroup>; + using Type = CustomIndexArray>, AgeGroup>; static Type get_default(AgeGroup size) { SmootherCosine smoothcos(2.0); - StateAgeFunctionWrapper delaydistribution(smoothcos); - std::vector state_age_function_vector((int)InfectionTransition::Count, - delaydistribution); + StateAgeFunctionWrapper delaydistribution(smoothcos); + std::vector> state_age_function_vector((int)InfectionTransition::Count, + delaydistribution); return Type(size, state_age_function_vector); } @@ -97,13 +97,14 @@ struct TransitionProbabilities { * @brief The contact patterns within the society are modelled using an UncertainContactMatrix. */ struct ContactPatterns { - using Type = UncertainContactMatrix; + using Type = UncertainContactMatrix; static Type get_default(AgeGroup size) { - ContactMatrixGroup contact_matrix = ContactMatrixGroup(1, static_cast((size_t)size)); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(static_cast((size_t)size), - static_cast((size_t)size), 10.)); + ContactMatrixGroup contact_matrix = + ContactMatrixGroup(1, static_cast((size_t)size)); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant( + static_cast((size_t)size), static_cast((size_t)size), 10.)); return Type(contact_matrix); } static std::string name() @@ -116,7 +117,7 @@ struct ContactPatterns { * @brief Probability of getting infected from a contact. */ struct TransmissionProbabilityOnContact { - using Type = CustomIndexArray; + using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) { ConstantFunction constfunc(1.0); @@ -133,7 +134,7 @@ struct TransmissionProbabilityOnContact { */ struct RelativeTransmissionNoSymptoms { - using Type = CustomIndexArray; + using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) { ConstantFunction constfunc(1.0); @@ -149,7 +150,7 @@ struct RelativeTransmissionNoSymptoms { * @brief The risk of infection from symptomatic cases in the SECIR model. */ struct RiskOfInfectionFromSymptomatic { - using Type = CustomIndexArray; + using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) { ConstantFunction constfunc(1.0); @@ -373,4 +374,4 @@ class Parameters : public ParametersBase } // namespace mio -#endif // IDE_SECIR_PARAMS_H +#endif // IDE_SECIR_PARAMS_H \ No newline at end of file diff --git a/cpp/models/ide_secir/parameters_io.h b/cpp/models/ide_secir/parameters_io.h index 1af71855af..d0f3f23036 100644 --- a/cpp/models/ide_secir/parameters_io.h +++ b/cpp/models/ide_secir/parameters_io.h @@ -222,7 +222,7 @@ IOResult set_initial_flows(Model& model, const ScalarType dt, const std::v Eigen::Index(std::max(std::floor((offset - model.transitions.get_time(0) - 1) / dt), 0.)); // Biggest index for which the entry is needed. idx_needed_last = Eigen::Index(std::min(std::ceil((offset - model.transitions.get_time(0) + 1) / dt), - double(model.transitions.get_num_time_points() - 1))); + ScalarType(model.transitions.get_num_time_points() - 1))); int INStISyi = model.get_transition_flat_index( Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms), group); diff --git a/cpp/models/ide_secir/simulation.cpp b/cpp/models/ide_secir/simulation.cpp index 8f9e62a6b3..98eb75fea0 100644 --- a/cpp/models/ide_secir/simulation.cpp +++ b/cpp/models/ide_secir/simulation.cpp @@ -64,4 +64,4 @@ TimeSeries simulate(ScalarType tmax, ScalarType dt, Model const& m_m } } // namespace isecir -} // namespace mio +} // namespace mio \ No newline at end of file diff --git a/cpp/models/ide_secir/simulation.h b/cpp/models/ide_secir/simulation.h index 5fef3548f8..c0c7b0e213 100644 --- a/cpp/models/ide_secir/simulation.h +++ b/cpp/models/ide_secir/simulation.h @@ -127,7 +127,7 @@ class Simulation * @param[in] model An instance of an IDE-SECIR model. * @return A TimeSeries to represent the final simulation result. */ -TimeSeries simulate(double tmax, double dt, Model const& model); +TimeSeries simulate(ScalarType tmax, ScalarType dt, Model const& model); } // namespace isecir } // namespace mio diff --git a/cpp/models/ide_seir/infection_state.h b/cpp/models/ide_seir/infection_state.h index f2c5ec15a1..9a8e6790fe 100755 --- a/cpp/models/ide_seir/infection_state.h +++ b/cpp/models/ide_seir/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -26,7 +26,7 @@ namespace mio namespace iseir { /** - * @brief The InfectionState enum describes the possible categories for the infectious state of persons + * @brief The InfectionState enum describes the possible categories for the infectious state of persons * in ide_seir model. */ enum class InfectionState diff --git a/cpp/models/ide_seir/model.cpp b/cpp/models/ide_seir/model.cpp index 6df4d40cbe..c4caaa35b8 100755 --- a/cpp/models/ide_seir/model.cpp +++ b/cpp/models/ide_seir/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke diff --git a/cpp/models/ide_seir/model.h b/cpp/models/ide_seir/model.h index 470266a2dd..681712e753 100755 --- a/cpp/models/ide_seir/model.h +++ b/cpp/models/ide_seir/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -35,30 +35,29 @@ namespace mio { namespace iseir { -template class Model { - using Pa = ParametersBase; - using Vec = TimeSeries::Vector; + using Pa = ParametersBase; + using Vec = Eigen::VectorX; public: /** * @brief Create an IDE SEIR model. * * @param[in, out] init TimeSeries with the initial values of the number of susceptibles at associated initial times. - * The time steps in this vector should be equidistant and equal to the time step used for the simulation. - * A certain history of time steps and values for susceptibles is needed. + * The time steps in this vector should be equidistant and equal to the time step used for the simulation. + * A certain history of time steps and values for susceptibles is needed. * A warning is displayed if the condition is violated. - * Co be more precise, the first time point needs to be smaller than -(k-1)*TimeStep with + * Co be more precise, the first time point needs to be smaller than -(k-1)*TimeStep with * k=ceil((InfectiousTime + LatencyTime)/TimeStep). * The last time point in this vector should be a time 0. * @param[in] dt_init The size of the time step used for numerical simulation. - * @param[in] N_init The population of the considered region. + * @param[in] N_init The population of the considered region. */ - Model(TimeSeries&& init, double dt_init, int N_init, const Pa& Parameterset_init = Pa()) + Model(TimeSeries&& init, ScalarType dt_init, int N_init, const Pa& Parameterset_init = Pa()) : parameters{Parameterset_init} , m_result{std::move(init)} - , m_result_SEIR{TimeSeries(4)} + , m_result_SEIR{TimeSeries(4)} , m_dt{dt_init} , m_N{N_init} { @@ -67,17 +66,16 @@ class Model /** * @brief Simulate the evolution of infection numbers with the given IDE SEIR model. * - * The simulation is performed by solving the underlying model equation numerically. + * The simulation is performed by solving the underlying model equation numerically. * Here, an integro-differential equation is to be solved. The model parameters and the initial data are used. * - * @param[in] t_max Last simulation day. + * @param[in] t_max Last simulation day. * If the last point of time of the initial TimeSeries was 0, the simulation will be executed for t_max days. - * @return The result of the simulation, stored in a TimeSeries with simulation time and + * @return The result of the simulation, stored in a TimeSeries with simulation time and * associated number of susceptibles. */ - TimeSeries const& simulate(int t_max) + TimeSeries const& simulate(int t_max) { - m_l = (int)std::floor(parameters.template get() / m_dt); m_k = (int)std::ceil((parameters.template get() + parameters.template get()) / m_dt); @@ -92,11 +90,11 @@ class Model Eigen::Index idx = m_result.get_num_time_points(); // R0t is the effective reproduction number at time t - auto R0t1 = parameters.template get>().get_cont_freq_mat().get_matrix_at( - m_result.get_time(idx - 2))(0, 0) * + auto R0t1 = parameters.template get().get_cont_freq_mat().get_matrix_at( + SimulationTime(m_result.get_time(idx - 2)))(0, 0) * parameters.template get() * parameters.template get(); - auto R0t2 = parameters.template get>().get_cont_freq_mat().get_matrix_at( - m_result.get_last_time())(0, 0) * + auto R0t2 = parameters.template get().get_cont_freq_mat().get_matrix_at( + SimulationTime(m_result.get_last_time()))(0, 0) * parameters.template get() * parameters.template get(); m_result.get_last_value() = @@ -110,16 +108,16 @@ class Model /** * @brief Calculate the distribution of the population in E, I and, R based on the calculated values for S. * - * The values are calculated using the average latency and infection time, not using model equations. + * The values are calculated using the average latency and infection time, not using model equations. * The simulated values of S are used for this purpose, so the simulate() function should be called beforehand. - * + * * @return The result of the calculation stored in an TimeSeries. The TimeSeries contains the simulation time and an * associated Vector with values for S, E, I, and R. */ - TimeSeries const& calculate_EIR() + TimeSeries const& calculate_EIR() { Eigen::Index num_points = m_result.get_num_time_points(); - double S, E, I, R; + ScalarType S, E, I, R; for (Eigen::Index i = m_k; i < num_points; ++i) { S = m_result[i][Eigen::Index(InfectionState::S)]; E = m_result[i - m_l][Eigen::Index(InfectionState::S)] - S; @@ -142,29 +140,29 @@ class Model * @param[in] q parameter q of the generalized Beta distribution. * @result Evaluation of the generalized beta distribution at the given evaluation point. */ - double generalized_beta_distribution(double tau, double p = 3.0, double q = 10.0) const + ScalarType generalized_beta_distribution(ScalarType tau, ScalarType p = 3.0, ScalarType q = 10.0) const { if ((parameters.template get() < tau) && (parameters.template get() + parameters.template get() > tau)) { - return tgamma(p + q) * pow(tau - parameters.template get(), p - 1) * + return std::tgamma(p + q) * pow(tau - parameters.template get(), p - 1) * pow(parameters.template get() + parameters.template get() - tau, q - 1) / - (tgamma(p) * tgamma(q) * pow(parameters.template get(), p + q - 1)); + (std::tgamma(p) * std::tgamma(q) * std::pow(parameters.template get(), p + q - 1)); } return 0.0; } /** * @brief Numerical differentiation of one compartment using a central difference quotient. - * - * @param[in] ts_ide TimeSeries with the time steps already calculated. + * + * @param[in] ts_ide TimeSeries with the time steps already calculated. * Used as function values in numerical differentiation. * @param[in] idx Time index at which the numerical differentiation should be performed. * @param[in] compartment Compartment for which the numerical differentiation is to be performed. * @return Numerically approximated derivative of the function belonging to the compartment at the point t[idx]. */ - double central_difference_quotient(TimeSeries const& ts_ide, InfectionState compartment, - Eigen::Index idx) const + ScalarType central_difference_quotient(TimeSeries const& ts_ide, InfectionState compartment, + Eigen::Index idx) const { return (ts_ide[idx + 1][Eigen::Index(compartment)] - ts_ide[idx - 1][Eigen::Index(compartment)]) / (2 * m_dt); } @@ -172,16 +170,16 @@ class Model /** * @brief Numerical integration of the inner integral of the integro-differential equation for the group S using * a trapezoidal sum. - * + * * @param[in] idx Index of the point of time used in the inner integral. * @return Result of the numerical integration. */ - double num_integration_inner_integral(Eigen::Index idx) const + ScalarType num_integration_inner_integral(Eigen::Index idx) const { - double res = 0.5 * (generalized_beta_distribution(m_result.get_time(idx) - m_result.get_time(idx - m_k)) * - central_difference_quotient(m_result, InfectionState::S, m_k) + - generalized_beta_distribution(m_result.get_time(idx) - m_result.get_time(idx - m_l)) * - central_difference_quotient(m_result, InfectionState::S, idx - m_l)); + ScalarType res = 0.5 * (generalized_beta_distribution(m_result.get_time(idx) - m_result.get_time(idx - m_k)) * + central_difference_quotient(m_result, InfectionState::S, m_k) + + generalized_beta_distribution(m_result.get_time(idx) - m_result.get_time(idx - m_l)) * + central_difference_quotient(m_result, InfectionState::S, idx - m_l)); Eigen::Index i = idx - m_k + 1; while (i <= idx - m_l - 2) { res += (generalized_beta_distribution(m_result.get_time(idx) - m_result.get_time(i)) * @@ -192,13 +190,13 @@ class Model } // TimeSeries containing points of time and the corresponding number of susceptibles. - TimeSeries m_result; + TimeSeries m_result; // TimeSeries containing points of time and the corresponding number of susceptibles, exposed, // infected and recovered. - TimeSeries m_result_SEIR; + TimeSeries m_result_SEIR; // Timestep used for simulation. - double m_dt{0}; + ScalarType m_dt{0}; // Population of the considered region. int m_N{0}; diff --git a/cpp/models/ide_seir/parameters.h b/cpp/models/ide_seir/parameters.h index 9ca02683ab..4b354fec46 100644 --- a/cpp/models/ide_seir/parameters.h +++ b/cpp/models/ide_seir/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -35,67 +35,65 @@ namespace iseir /** * @brief The time of latency used in the IDE SEIR model. - * + * * Latency time is the average time from infection to the onset of infectivity used in the model. * Latency time is measured in days. */ struct LatencyTime { - using Type = double; + using Type = ScalarType; static constexpr Type get_default() { - return 3.3; + return Type(3.3); } }; /** * @brief The infectious time used in the IDE SEIR model. - * + * * Infectious time is the average time from onset of infectivity to recovery used in the model. * Infectious time is measured in days. */ struct InfectiousTime { - using Type = double; + using Type = ScalarType; static constexpr Type get_default() { - return 8.2; + return Type(8.2); } }; /** * @brief The risk of transmission in the event of a contact used in the IDE SEIR model. - * - * The transmission risk is the average risk to get infected in the event of a contact, + * + * The transmission risk is the average risk to get infected in the event of a contact, * given that the contact takes place between a susceptible and an infected person. */ struct TransmissionRisk { - using Type = double; + using Type = ScalarType; static constexpr Type get_default() { - return 0.1; + return Type(0.1); } }; /** * @brief The contact frequency is modeled using an UncertainContactMatrix. - * + * * The contact frequency is the average number of contact of an individual per day. * We use the type UncertainContactMatrix, because of the Randomness in this variable. * Via this parameter, dampings can be included to simulate non-pharmaceutical interventions. */ -template struct ContactFrequency { - using Type = UncertainContactMatrix; + using Type = UncertainContactMatrix; static Type get_default() { - ContactMatrixGroup contact_matrix = ContactMatrixGroup(1, 1); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10.)); + ContactMatrixGroup contact_matrix = ContactMatrixGroup(1, 1); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10.)); return Type(contact_matrix); } }; // Define Parameterset for IDE SEIR model. -template -using ParametersBase = ParameterSet>; +using ParametersBase = ParameterSet; } // namespace iseir } // namespace mio diff --git a/cpp/models/lct_secir/infection_state.h b/cpp/models/lct_secir/infection_state.h index 2df4ff310e..30e8aa8831 100644 --- a/cpp/models/lct_secir/infection_state.h +++ b/cpp/models/lct_secir/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke diff --git a/cpp/models/lct_secir/initializer_flows.h b/cpp/models/lct_secir/initializer_flows.h index b4260765f6..dd6cc9dfd2 100644 --- a/cpp/models/lct_secir/initializer_flows.h +++ b/cpp/models/lct_secir/initializer_flows.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -37,11 +37,11 @@ namespace lsecir { /** - * @brief Class that can be used to compute an initialization vector out of flows for an LCT Model + * @brief Class that can be used to compute an initialization vector out of flows for an LCT Model * with division in groups. - * - * The initialization method is based on the fact that the LCT model is a special case of an IDE model with special - * choices of stay time distributions (so-called Erlang distributions). Accordingly, the method for calculating + * + * The initialization method is based on the fact that the LCT model is a special case of an IDE model with special + * choices of stay time distributions (so-called Erlang distributions). Accordingly, the method for calculating * initial values for the compartments from given flows/transitions is taken from the IDE-SECIR model. * We cannot use the functionality of the IDE model directly, as we have to calculate a division into sub-compartments * and not only the sizes of the compartments. @@ -49,7 +49,7 @@ namespace lsecir * * @tparam Model is expected to be an LCT-SECIR model defined in models/lct_secir/model.h. */ -template +template class Initializer { public: @@ -58,14 +58,14 @@ class Initializer /** * @brief Constructs a new Initializer object. * - * @param[in] flows Initializing TimeSeries with flows fitting to these defined in InfectionTransition. + * @param[in] flows Initializing TimeSeries with flows fitting to these defined in InfectionTransition. * For each group of m_model, InfectionTransition::Count entries are required. - * Timesteps should be equidistant and the values should be non-negative. - * The time history has to be long enough so that it is possible to calculate the initial vector. + * Timesteps should be equidistant and the values should be non-negative. + * The time history has to be long enough so that it is possible to calculate the initial vector. * The length of the required time history depends on the Erlang densities used to compute the initial vector. * @param[in, out] model The LCT-SECIR model for which the initialization should be performed. */ - Initializer(TimeSeries&& flows, Model& model) + Initializer(TimeSeries&& flows, Model& model) : m_flows(std::move(flows)) , m_model(model) { @@ -75,22 +75,21 @@ class Initializer /** * @brief Core function of Initializer. * - * Computes a vector that can be used for the initialization of an LCT model stratified by groups with the number + * Computes a vector that can be used for the initialization of an LCT model stratified by groups with the number * of persons for each subcompartment for each group. * The initial value vector is updated in the model. * * @param[in] total_population The total size of the considered population. * @param[in] deaths Number of deceased people from the disease at time 0. * @param[in] total_confirmed_cases Total number of confirmed cases at time 0. - * @return Returns true if one (or more) constraint(s) of the model, the initial flows - * or the computed initial value vector are not satisfied, otherwise false. + * @return Returns true if one (or more) constraint(s) of the model, the initial flows + * or the computed initial value vector are not satisfied, otherwise false. */ - bool compute_initialization_vector(Eigen::VectorX const& total_population, - Eigen::VectorX const& deaths, - Eigen::VectorX const& total_confirmed_cases) + bool compute_initialization_vector(Eigen::VectorX const& total_population, Eigen::VectorX const& deaths, + Eigen::VectorX const& total_confirmed_cases) { - Eigen::VectorX init(m_model.populations.get_compartments()); + Eigen::VectorX init(m_model.populations.get_compartments()); bool error = compute_initialization_vector_impl(init, total_population, deaths, total_confirmed_cases); if (error) { @@ -111,7 +110,7 @@ class Initializer * * @param[in] new_tol New tolerance. */ - void set_tol_for_support_max(ScalarType new_tol) + void set_tol_for_support_max(FP new_tol) { m_tol = new_tol; } @@ -120,7 +119,7 @@ class Initializer /** * @brief Implementation of the calculation of the initial value vector slice that corresponds to a specified group. * - * Computes a vector that can be used for the initialization of an LCT model stratified by groups with the number + * Computes a vector that can be used for the initialization of an LCT model stratified by groups with the number * of persons for each subcompartment. The groups are calculated recursively. * * @tparam Group The group for which the corresponding slice of the initial value vector is calculated. @@ -129,13 +128,12 @@ class Initializer * @param[in] deaths Number of deceased people from the disease at time 0. * @param[in] total_confirmed_cases Total number of confirmed cases at time 0. * @return Returns true if one (or more) constraint(s) of the computed initial value vector are not satisfied, - * otherwise false. + * otherwise false. */ template - bool compute_initialization_vector_impl(Eigen::VectorX& init, - Eigen::VectorX const& total_population, - Eigen::VectorX const& deaths, - Eigen::VectorX const& total_confirmed_cases) + bool compute_initialization_vector_impl(Eigen::VectorX& init, Eigen::VectorX const& total_population, + Eigen::VectorX const& deaths, + Eigen::VectorX const& total_confirmed_cases) { static_assert((Group < Model::num_groups) && (Group >= 0), "The template parameter Group should be valid."); using LctStateGroup = type_at_index_t; @@ -146,23 +144,23 @@ class Initializer // Exposed. (compute_compartment( init, first_index_flows + (Eigen::Index)InfectionTransition::SusceptibleToExposed, - 1 / m_model.parameters.template get()[Group])) || + 1 / m_model.parameters.template get>()[Group])) || // InfectedNoSymptoms. (compute_compartment( init, first_index_flows + (Eigen::Index)InfectionTransition::ExposedToInfectedNoSymptoms, - 1 / m_model.parameters.template get()[Group])) || + 1 / m_model.parameters.template get>()[Group])) || // InfectedSymptoms. (compute_compartment( init, first_index_flows + (Eigen::Index)InfectionTransition::InfectedNoSymptomsToInfectedSymptoms, - 1 / m_model.parameters.template get()[Group])) || + 1 / m_model.parameters.template get>()[Group])) || // InfectedSevere. (compute_compartment( init, first_index_flows + (Eigen::Index)InfectionTransition::InfectedSymptomsToInfectedSevere, - 1 / m_model.parameters.template get()[Group])) || + 1 / m_model.parameters.template get>()[Group])) || // InfectedCritical. (compute_compartment( init, first_index_flows + (Eigen::Index)InfectionTransition::InfectedSevereToInfectedCritical, - 1 / m_model.parameters.template get()[Group])); + 1 / m_model.parameters.template get>()[Group])); if (error) { return true; } @@ -193,7 +191,7 @@ class Initializer for (size_t state_idx : {LctStateGroup::template get_first_index(), LctStateGroup::template get_first_index(), LctStateGroup::template get_first_index()}) { - if (floating_point_less(init[first_index + state_idx], 0.0, 1e-8)) { + if (floating_point_less(init[first_index + state_idx], 0.0, 1e-8)) { log_error("Initialization failed. Values of total_confirmed_cases, deaths and total_population do not " "match the specified values for the transitions, leading to at least one negative result."); return true; @@ -210,7 +208,7 @@ class Initializer /** * @brief Checks constraints of the Initializer including checks for the model. - * @return Returns true if one (or more) constraint(s) are not satisfied, otherwise false. + * @return Returns true if one (or more) constraint(s) are not satisfied, otherwise false. */ bool check_constraints() const { @@ -219,13 +217,13 @@ class Initializer return true; } - if (!(floating_point_equal(0., m_flows.get_last_time(), 1e-8, 1e-14))) { + if (!(floating_point_equal(0., m_flows.get_last_time(), 1e-8, 1e-14))) { log_error("Last time point in flows has to be 0."); return true; } for (int i = 1; i < m_flows.get_num_time_points(); i++) { - if (!(floating_point_equal(m_dt, m_flows.get_time(i) - m_flows.get_time(i - 1), 1e-8, 1e-14))) { + if (!(floating_point_equal(m_dt, m_flows.get_time(i) - m_flows.get_time(i - 1), 1e-8, 1e-14))) { log_error("Time points in flows have to be equidistant."); return true; } @@ -241,24 +239,25 @@ class Initializer } /** - * @brief Computes a slice of the initial value vector for each subcompartment of one InfectionState + * @brief Computes a slice of the initial value vector for each subcompartment of one InfectionState * for a specified group. * * @tparam Group The group for which the corresponding slice of the initial value vector is calculated. * @tparam State The InfectionState for which the corresponding slice of the initial value vector is calculated * @param[out] init The initial value vector under consideration. - * @param[in] idx_incoming_flow Index of the flow which is relevant for the calculation, so the flow + * @param[in] idx_incoming_flow Index of the flow which is relevant for the calculation, so the flow * to the InfectionState. - * @param[in] transition_rate Specifies the transition rate of the InfectionState. + * @param[in] transition_rate Specifies the transition rate of the InfectionState. * 'This is equal to 1 / (expected time in the InfectionState). - + * @return Returns true if one (or more) constraint(s) of the computed slice of the initial value vector - * are not satisfied, otherwise false. + * are not satisfied, otherwise false. */ template - bool compute_compartment(Eigen::VectorX& init, Eigen::Index idx_incoming_flow, - ScalarType transition_rate) const + bool compute_compartment(Eigen::VectorX& init, Eigen::Index idx_incoming_flow, FP transition_rate) const { + using std::ceil; + size_t first_index = m_model.populations.template get_first_index_of_group() + type_at_index_t::template get_first_index(); size_t num_subcompartments = type_at_index_t::template get_num_subcompartments(); @@ -268,20 +267,20 @@ class Initializer ErlangDensity erlang(1, 1. / (num_subcompartments * transition_rate)); // Initialize other relevant parameters. - ScalarType calc_time{0}; + FP calc_time{0}; Eigen::Index calc_time_index{0}; - ScalarType state_age{0}; - ScalarType sum{0}; + FP state_age{0}; + FP sum{0}; Eigen::Index num_time_points = m_flows.get_num_time_points(); // Calculate number of people in each subcompartment. for (size_t j = 0; j < num_subcompartments; j++) { // For subcompartment number j+1, shape parameter j+1 is needed. - erlang.set_distribution_parameter((ScalarType)j + 1); + erlang.set_distribution_parameter((FP)j + 1); // Determine relevant calculation area and corresponding index. calc_time = erlang.get_support_max(m_dt, m_tol); - calc_time_index = (Eigen::Index)std::ceil(calc_time / m_dt) - 1; + calc_time_index = (Eigen::Index)ceil(calc_time / m_dt) - 1; if (num_time_points < calc_time_index) { log_error("Initialization failed. Not enough time points for the transitions are given. More than {} " @@ -308,10 +307,10 @@ class Initializer return false; } - TimeSeries m_flows; ///< TimeSeries with the flows which are used to calculate the initial vector. + TimeSeries m_flows; ///< TimeSeries with the flows which are used to calculate the initial vector. Model& m_model; ///< The LCT-SECIR model for which the initialization should be performed. - ScalarType m_dt{}; ///< Step size of the times in m_flows and time step for the approximation of the integral. - ScalarType m_tol{1e-10}; ///< Tolerance used to calculate the maximum support of the ErlangDensity%s. + FP m_dt{}; ///< Step size of the times in m_flows and time step for the approximation of the integral. + FP m_tol{1e-10}; ///< Tolerance used to calculate the maximum support of the ErlangDensity%s. }; } // namespace lsecir diff --git a/cpp/models/lct_secir/model.cpp b/cpp/models/lct_secir/model.cpp index 4e5ffb94c1..bcbaf40046 100644 --- a/cpp/models/lct_secir/model.cpp +++ b/cpp/models/lct_secir/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke diff --git a/cpp/models/lct_secir/model.h b/cpp/models/lct_secir/model.h index 9329bd70d8..6d1dc4cd85 100644 --- a/cpp/models/lct_secir/model.h +++ b/cpp/models/lct_secir/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -32,6 +32,9 @@ #include "memilio/math/eigen.h" #include "memilio/utils/type_list.h" #include "memilio/utils/metaprogramming.h" + +#include + namespace mio { namespace lsecir @@ -42,18 +45,17 @@ namespace lsecir * @tparam LctStates The LCT model can work with any number of LctStates, where each LctState corresponds to a group, * e.g. one AgeGroup. The purpose of the LctStates is to define the number of subcompartments for each InfectionState. * If you do not want to divide the population into groups, just use one LctState. - * If you want to divide the population according to more than one category, e.g. sex and age, - * you have to specify one LctState for each pair of groups, e.g. for (female, A00-A04), (female, A05-A14) etc. + * If you want to divide the population according to more than one category, e.g. sex and age, + * you have to specify one LctState for each pair of groups, e.g. for (female, A00-A04), (female, A05-A14) etc. * This is because the number of subcompartments can be different for each group. - * Therefore, the number of LctStates also determines the number of groups. + * Therefore, the number of LctStates also determines the number of groups. */ -template -class Model - : public CompartmentalModel, Parameters> +template +class Model : public CompartmentalModel, Parameters> { public: using LctStatesGroups = TypeList; - using Base = CompartmentalModel, Parameters>; + using Base = CompartmentalModel, Parameters>; using typename Base::ParameterSet; using typename Base::Populations; static size_t constexpr num_groups = sizeof...(LctStates); @@ -77,7 +79,7 @@ class Model /** * @brief Evaluates the right-hand-side f of the ODE dydt = f(y, t). * - * The LCT-SECIR model is defined through ordinary differential equations of the form dydt = f(y, t). + * The LCT-SECIR model is defined through ordinary differential equations of the form dydt = f(y, t). * y is a vector containing the number of individuals for each (sub-) compartment. * This function evaluates the right-hand-side f of the ODE and can be used in an ODE solver. * @param[in] pop The current state of the population in the geographic unit we are considering. @@ -85,9 +87,8 @@ class Model * @param[in] t The current time. * @param[out] dydt A reference to the calculated output. */ - void get_derivatives(Eigen::Ref> pop, - Eigen::Ref> y, ScalarType t, - Eigen::Ref> dydt) const override + void get_derivatives(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> dydt) const override { // Vectors are sorted such that we first have all InfectionState%s for AgeGroup 0, // afterwards all for AgeGroup 1 and so on. @@ -99,26 +100,26 @@ class Model * @brief Cumulates a simulation result with subcompartments to produce a result that divides the population only * into the infection states defined in InfectionState. * - * If the model is used for simulation, we will get a result in form of a TimeSeries with infection states divided + * If the model is used for simulation, we will get a result in form of a TimeSeries with infection states divided * in subcompartments. - * The function calculates a TimeSeries without subcompartments from another TimeSeries with subcompartments. + * The function calculates a TimeSeries without subcompartments from another TimeSeries with subcompartments. * This is done by summing up the numbers in the subcompartments. * @param[in] subcompartments_ts Result of a simulation with the model. - * @return Result of the simulation divided in infection states without subcompartments. + * @return Result of the simulation divided in infection states without subcompartments. * Returns TimeSeries with values -1 if calculation is not possible. */ - TimeSeries calculate_compartments(const TimeSeries& subcompartments_ts) const + TimeSeries calculate_compartments(const TimeSeries& subcompartments_ts) const { Eigen::Index count_InfStates = (Eigen::Index)InfectionState::Count; Eigen::Index num_compartments = count_InfStates * num_groups; - TimeSeries compartments_ts(num_compartments); + TimeSeries compartments_ts(num_compartments); if (!(this->populations.get_num_compartments() == (size_t)subcompartments_ts.get_num_elements())) { log_error("Result does not match InfectionState of the Model."); - Eigen::VectorX wrong_size = Eigen::VectorX::Constant(num_compartments, -1); + Eigen::VectorX wrong_size = Eigen::VectorX::Constant(num_compartments, -1); compartments_ts.add_time_point(-1, wrong_size); return compartments_ts; } - Eigen::VectorX compartments(num_compartments); + Eigen::VectorX compartments(num_compartments); for (Eigen::Index timepoint = 0; timepoint < subcompartments_ts.get_num_time_points(); ++timepoint) { compress_vector(subcompartments_ts[timepoint], compartments); compartments_ts.add_time_point(subcompartments_ts.get_time(timepoint), compartments); @@ -140,17 +141,16 @@ class Model private: /** - * @brief Converts a vector with subcompartments in a vector without subcompartments + * @brief Converts a vector with subcompartments in a vector without subcompartments * by summing up subcompartment values. * This is done recursively for each group which corresponds to a slice of the vector. * - * @tparam group The group specifying the slice of the vector being considered. + * @tparam group The group specifying the slice of the vector being considered. * @param[in] subcompartments The vector that should be converted. * @param[out] compartments Reference to the vector where the output is stored. */ template - void compress_vector(const Eigen::VectorX& subcompartments, - Eigen::VectorX& compartments) const + void compress_vector(const Eigen::VectorX& subcompartments, Eigen::VectorX& compartments) const { static_assert((Group < num_groups) && (Group >= 0), "The template parameter Group should be valid."); using LctStateGroup = type_at_index_t; @@ -177,23 +177,22 @@ class Model * * See also the function get_derivative. * For each group, one slice of the output vector is calculated. - * @tparam group The group specifying the slice of the vector being considered. + * @tparam group The group specifying the slice of the vector being considered. * @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. */ template - void get_derivatives_impl(Eigen::Ref> pop, - Eigen::Ref> y, ScalarType t, - Eigen::Ref> dydt) const + void get_derivatives_impl(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> dydt) const { static_assert((Group < num_groups) && (Group >= 0), "The template parameter Group should be valid."); using LctStateGroup = type_at_index_t; size_t first_index_group = this->populations.template get_first_index_of_group(); - auto params = this->parameters; - ScalarType flow = 0; + const auto& params = this->parameters; + FP flow = 0; // Indices of first subcompartment of the InfectionState for the group in the vectors. size_t Ei_first_index = first_index_group + LctStateGroup::template get_first_index(); @@ -218,8 +217,8 @@ class Model // Variable flow stores the value of the flow from one subcompartment to the next one. // Ei_first_index + subcomp is always the index of a (sub-)compartment of Exposed and Ei_first_index // + subcomp + 1 can also be the index of the first (sub-)compartment of InfectedNoSymptoms. - flow = (ScalarType)LctStateGroup::template get_num_subcompartments() * - (1 / params.template get()[Group]) * y[Ei_first_index + subcomp]; + flow = (FP)LctStateGroup::template get_num_subcompartments() * + (1 / params.template get>()[Group]) * y[Ei_first_index + subcomp]; // Subtract flow from dydt[Ei_first_index + subcomp] and add to next subcompartment. dydt[Ei_first_index + subcomp] -= flow; dydt[Ei_first_index + subcomp + 1] = flow; @@ -229,8 +228,8 @@ class Model for (size_t subcomp = 0; subcomp < LctStateGroup::template get_num_subcompartments(); subcomp++) { - flow = (ScalarType)LctStateGroup::template get_num_subcompartments() * - (1 / params.template get()[Group]) * y[INSi_first_index + subcomp]; + flow = (FP)LctStateGroup::template get_num_subcompartments() * + (1 / params.template get>()[Group]) * y[INSi_first_index + subcomp]; dydt[INSi_first_index + subcomp] -= flow; dydt[INSi_first_index + subcomp + 1] = flow; } @@ -238,46 +237,46 @@ class Model // Calculate derivative of the InfectedSymptoms compartment. // Flow from last (sub-) compartment of C must be split between // the first subcompartment of InfectedSymptoms and Recovered. - dydt[Ri] = dydt[ISyi_first_index] * params.template get()[Group]; + dydt[Ri] = dydt[ISyi_first_index] * params.template get>()[Group]; dydt[ISyi_first_index] = - dydt[ISyi_first_index] * (1 - params.template get()[Group]); + dydt[ISyi_first_index] * (1 - params.template get>()[Group]); for (size_t subcomp = 0; subcomp < LctStateGroup::template get_num_subcompartments(); subcomp++) { - flow = (ScalarType)LctStateGroup::template get_num_subcompartments() * - (1 / params.template get()[Group]) * y[ISyi_first_index + subcomp]; + flow = (FP)LctStateGroup::template get_num_subcompartments() * + (1 / params.template get>()[Group]) * y[ISyi_first_index + subcomp]; dydt[ISyi_first_index + subcomp] -= flow; dydt[ISyi_first_index + subcomp + 1] = flow; } // Calculate derivative of the InfectedSevere compartment. - dydt[Ri] += dydt[ISevi_first_index] * (1 - params.template get()[Group]); - dydt[ISevi_first_index] = dydt[ISevi_first_index] * params.template get()[Group]; + dydt[Ri] += dydt[ISevi_first_index] * (1 - params.template get>()[Group]); + dydt[ISevi_first_index] = dydt[ISevi_first_index] * params.template get>()[Group]; for (size_t subcomp = 0; subcomp < LctStateGroup::template get_num_subcompartments(); subcomp++) { - flow = (ScalarType)LctStateGroup::template get_num_subcompartments() * - (1 / params.template get()[Group]) * y[ISevi_first_index + subcomp]; + flow = (FP)LctStateGroup::template get_num_subcompartments() * + (1 / params.template get>()[Group]) * y[ISevi_first_index + subcomp]; dydt[ISevi_first_index + subcomp] -= flow; dydt[ISevi_first_index + subcomp + 1] = flow; } // Calculate derivative of the InfectedCritical compartment. - dydt[Ri] += dydt[ICri_first_index] * (1 - params.template get()[Group]); - dydt[ICri_first_index] = dydt[ICri_first_index] * params.template get()[Group]; + dydt[Ri] += dydt[ICri_first_index] * (1 - params.template get>()[Group]); + dydt[ICri_first_index] = dydt[ICri_first_index] * params.template get>()[Group]; for (size_t subcomp = 0; subcomp < LctStateGroup::template get_num_subcompartments() - 1; subcomp++) { - flow = (ScalarType)LctStateGroup::template get_num_subcompartments() * - (1 / params.template get()[Group]) * y[ICri_first_index + subcomp]; + flow = (FP)LctStateGroup::template get_num_subcompartments() * + (1 / params.template get>()[Group]) * y[ICri_first_index + subcomp]; dydt[ICri_first_index + subcomp] -= flow; dydt[ICri_first_index + subcomp + 1] = flow; } // Last flow from InfectedCritical has to be divided between Recovered and Dead. // Must be calculated separately in order not to overwrite the already calculated values ​​for Recovered. - flow = (ScalarType)LctStateGroup::template get_num_subcompartments() * - (1 / params.template get()[Group]) * y[Ri - 1]; + flow = (FP)LctStateGroup::template get_num_subcompartments() * + (1 / params.template get>()[Group]) * y[Ri - 1]; dydt[Ri - 1] -= flow; - dydt[Ri] = dydt[Ri] + (1 - params.template get()[Group]) * flow; - dydt[Di] = params.template get()[Group] * flow; + dydt[Ri] = dydt[Ri] + (1 - params.template get>()[Group]) * flow; + dydt[Di] = params.template get>()[Group] * flow; // Function call for next group if applicable. if constexpr (Group + 1 < num_groups) { @@ -289,24 +288,24 @@ class Model * @brief Calculates the derivative of the Susceptible compartment for Group1. * * This is done recursively by calculating the interaction terms with each group. - * @tparam Group1 The group for which the derivative of the Susceptible compartment should be calculated. - * @tparam Group2 The group that Group1 interacts with. + * @tparam Group1 The group for which the derivative of the Susceptible compartment should be calculated. + * @tparam Group2 The group that Group1 interacts with. * @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. */ template - void interact(Eigen::Ref> pop, Eigen::Ref> y, - ScalarType t, Eigen::Ref> dydt) const + void interact(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> dydt) const { static_assert((Group1 < num_groups) && (Group1 >= 0) && (Group2 < num_groups) && (Group2 >= 0), "The template parameters Group1 & Group2 should be valid."); - using LctStateGroup2 = type_at_index_t; - size_t Si_1 = this->populations.template get_first_index_of_group(); - ScalarType infectedNoSymptoms_2 = 0; - ScalarType infectedSymptoms_2 = 0; - auto params = this->parameters; + using LctStateGroup2 = type_at_index_t; + size_t Si_1 = this->populations.template get_first_index_of_group(); + FP infectedNoSymptoms_2 = 0; + FP infectedSymptoms_2 = 0; + auto params = this->parameters; size_t first_index_group2 = this->populations.template get_first_index_of_group(); @@ -323,15 +322,17 @@ class Model LctStateGroup2::template get_num_subcompartments()) .sum(); // Size of the Subpopulation Group2 without dead people. - double N_2 = pop.segment(first_index_group2, LctStateGroup2::Count - 1).sum(); - const double divN_2 = (N_2 < Limits::zero_tolerance()) ? 0.0 : 1.0 / N_2; - ScalarType season_val = 1 + params.template get() * - sin(3.141592653589793 * ((params.template get() + t) / 182.5 + 0.5)); - dydt[Si_1] += -y[Si_1] * divN_2 * season_val * params.template get()[Group1] * - params.template get().get_cont_freq_mat().get_matrix_at(t)( - static_cast(Group1), static_cast(Group2)) * - (params.template get()[Group2] * infectedNoSymptoms_2 + - params.template get()[Group2] * infectedSymptoms_2); + FP N_2 = pop.segment(first_index_group2, LctStateGroup2::Count - 1).sum(); + const FP divN_2 = (N_2 < Limits::zero_tolerance()) ? 0.0 : 1.0 / N_2; + FP season_val = 1 + params.template get>() * + std::sin(std::numbers::pi_v * + ((params.template get>() + t) / 182.5 + 0.5)); + dydt[Si_1] += -y[Si_1] * divN_2 * season_val * + params.template get>()[Group1] * + params.template get>().get_cont_freq_mat().get_matrix_at( + SimulationTime(t))(static_cast(Group1), static_cast(Group2)) * + (params.template get>()[Group2] * infectedNoSymptoms_2 + + params.template get>()[Group2] * infectedSymptoms_2); // Function call for next interacting group if applicable. if constexpr (Group2 + 1 < num_groups) { interact(pop, y, t, dydt); @@ -339,7 +340,7 @@ class Model } /** - * @brief Checks whether LctState of a group satisfies all constraints. + * @brief Checks whether LctState of a group satisfies all constraints. * Recursively, it checks that all groups satisfy the constraints. * * @tparam group The group for which the constraints should be checked. diff --git a/cpp/models/lct_secir/parameters.h b/cpp/models/lct_secir/parameters.h index 1cae4d4a61..956fccf219 100644 --- a/cpp/models/lct_secir/parameters.h +++ b/cpp/models/lct_secir/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -40,8 +40,9 @@ namespace lsecir /** * @brief Average time spent in the Exposed compartment for each group. */ +template struct TimeExposed { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -53,11 +54,12 @@ struct TimeExposed { }; /** - * @brief Average time spent in the TimeInfectedNoSymptoms before developing + * @brief Average time spent in the TimeInfectedNoSymptoms before developing * symptoms or recover for each group in the SECIR model in day unit. */ +template struct TimeInfectedNoSymptoms { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -69,11 +71,12 @@ struct TimeInfectedNoSymptoms { }; /** - * @brief Average time spent in the TimeInfectedSymptoms before going to hospital + * @brief Average time spent in the TimeInfectedSymptoms before going to hospital * or recover for each group in the SECIR model in day unit. */ +template struct TimeInfectedSymptoms { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -85,11 +88,12 @@ struct TimeInfectedSymptoms { }; /** - * @brief Average time being in the Hospital before treated by ICU or recover for each group in the + * @brief Average time being in the Hospital before treated by ICU or recover for each group in the * SECIR model in day unit. */ +template struct TimeInfectedSevere { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -103,8 +107,9 @@ struct TimeInfectedSevere { /** * @brief Average time treated by ICU before dead or recover for each group in the SECIR model in day unit. */ +template struct TimeInfectedCritical { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -118,8 +123,9 @@ struct TimeInfectedCritical { /** * @brief Probability of getting infected from a contact for each group. */ +template struct TransmissionProbabilityOnContact { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -133,13 +139,15 @@ struct TransmissionProbabilityOnContact { /** * @brief The contact patterns within the society are modelled using an UncertainContactMatrix. */ +template struct ContactPatterns { - using Type = UncertainContactMatrix; + using Type = UncertainContactMatrix; static Type get_default(size_t size) { - mio::ContactMatrixGroup contact_matrix(1, (Eigen::Index)size); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant((Eigen::Index)size, (Eigen::Index)size, 10.)); + mio::ContactMatrixGroup contact_matrix(1, (Eigen::Index)size); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant((Eigen::Index)size, (Eigen::Index)size, 10.)); return Type(contact_matrix); } static std::string name() @@ -151,8 +159,9 @@ struct ContactPatterns { /** * @brief The relative InfectedNoSymptoms infectability for each group. */ +template struct RelativeTransmissionNoSymptoms { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -166,8 +175,9 @@ struct RelativeTransmissionNoSymptoms { /** * @brief The risk of infection from symptomatic cases for each group in the SECIR model. */ +template struct RiskOfInfectionFromSymptomatic { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 1.); @@ -181,8 +191,9 @@ struct RiskOfInfectionFromSymptomatic { /** * @brief The percentage of asymptomatic cases for each group in the SECIR model. */ +template struct RecoveredPerInfectedNoSymptoms { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 0.5); @@ -196,8 +207,9 @@ struct RecoveredPerInfectedNoSymptoms { /** * @brief The percentage of hospitalized patients per infected patients for each group in the SECIR model. */ +template struct SeverePerInfectedSymptoms { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 0.5); @@ -211,8 +223,9 @@ struct SeverePerInfectedSymptoms { /** * @brief The percentage of ICU patients per hospitalized patients for each group in the SECIR model. */ +template struct CriticalPerSevere { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 0.5); @@ -226,8 +239,9 @@ struct CriticalPerSevere { /** * @brief The percentage of dead patients per ICU patients for each group in the SECIR model. */ +template struct DeathsPerCritical { - using Type = Eigen::VectorX>; + using Type = Eigen::VectorX>; static Type get_default(size_t size) { return Type::Constant(size, 1, 0.1); @@ -244,11 +258,12 @@ struct DeathsPerCritical { * If the start day is 180 and simulation takes place from t0=0 to * tmax=100 the days 180 to 280 of the year are simulated. */ +template struct StartDay { - using Type = ScalarType; + using Type = FP; static Type get_default(size_t) { - return 0.; + return Type(0.0); } static std::string name() { @@ -261,11 +276,12 @@ struct StartDay { * The seasonality is given as (1+k*sin()) where the sine * curve is below one in summer and above one in winter. */ +template struct Seasonality { using Type = ScalarType; static Type get_default(size_t) { - return 0.; + return Type(0.0); } static std::string name() { @@ -273,16 +289,19 @@ struct Seasonality { } }; +template using ParametersBase = - ParameterSet; + ParameterSet, TimeInfectedNoSymptoms, TimeInfectedSymptoms, TimeInfectedSevere, + TimeInfectedCritical, TransmissionProbabilityOnContact, ContactPatterns, + RelativeTransmissionNoSymptoms, RiskOfInfectionFromSymptomatic, + RecoveredPerInfectedNoSymptoms, SeverePerInfectedSymptoms, CriticalPerSevere, + DeathsPerCritical, StartDay, Seasonality>; /** * @brief Parameters of an LCT-SECIR model. */ -class Parameters : public ParametersBase +template +class Parameters : public ParametersBase { public: /** @@ -290,7 +309,7 @@ class Parameters : public ParametersBase * @param num_groups The number of groups considered in the LCT model. */ Parameters(size_t num_groups) - : ParametersBase(num_groups) + : ParametersBase(num_groups) , m_num_groups{num_groups} { } @@ -302,80 +321,83 @@ class Parameters : public ParametersBase /** * @brief Checks whether all parameters satisfy their corresponding constraints and throws errors, if they do not. - * @return Returns true if one (or more) constraint(s) are not satisfied, otherwise false. + * @return Returns true if one (or more) constraint(s) are not satisfied, otherwise false. */ bool check_constraints() const { - if (this->get() < 0.0 || this->get() > 0.5) { + if (this->template get>() < 0.0 || this->template get>() > 0.5) { log_warning("Constraint check: Parameter Seasonality should lie between {:0.4f} and {:.4f}", 0.0, 0.5); return true; } for (size_t i = 0; i < m_num_groups; ++i) { - if (this->get()[i] < 1.0) { + if (this->template get>()[i] < 1.0) { log_error("Constraint check: Parameter TimeExposed is smaller than {:.4f}", 1.0); return true; } - if (this->get()[i] < 1.0) { + if (this->template get>()[i] < 1.0) { log_error("Constraint check: Parameter TimeInfectedNoSymptoms is smaller than {:.4f}", 1.0); return true; } - if (this->get()[i] < 1.0) { + if (this->template get>()[i] < 1.0) { log_error("Constraint check: Parameter TimeInfectedSymptoms is smaller than {:.4f}", 1.0); return true; } - if (this->get()[i] < 1.0) { + if (this->template get>()[i] < 1.0) { log_error("Constraint check: Parameter TimeInfectedSevere is smaller than {:.4f}", 1.0); return true; } - if (this->get()[i] < 1.0) { + if (this->template get>()[i] < 1.0) { log_error("Constraint check: Parameter TimeInfectedCritical is smaller than {:.4f}", 1.0); return true; } - if (this->get()[i] < 0.0 || - this->get()[i] > 1.0) { + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { log_error("Constraint check: Parameter TransmissionProbabilityOnContact smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get()[i] < 0.0 || - this->get()[i] > 1.0) { + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { log_error("Constraint check: Parameter RelativeTransmissionNoSymptoms smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get()[i] < 0.0 || - this->get()[i] > 1.0) { + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { log_error("Constraint check: Parameter RiskOfInfectionFromSymptomatic smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get()[i] < 0.0 || - this->get()[i] > 1.0) { + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { log_error("Constraint check: Parameter RecoveredPerInfectedNoSymptoms smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get()[i] < 0.0 || this->get()[i] > 1.0) { + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { log_error("Constraint check: Parameter SeverePerInfectedSymptoms smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get()[i] < 0.0 || this->get()[i] > 1.0) { + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { log_error("Constraint check: Parameter CriticalPerSevere smaller {:d} or larger {:d}", 0, 1); return true; } - if (this->get()[i] < 0.0 || this->get()[i] > 1.0) { + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { log_error("Constraint check: Parameter DeathsPerCritical smaller {:d} or larger {:d}", 0, 1); return true; } @@ -385,9 +407,9 @@ class Parameters : public ParametersBase } private: - Parameters(ParametersBase&& base) - : ParametersBase(std::move(base)) - , m_num_groups(this->template get().get_cont_freq_mat().get_num_groups()) + Parameters(ParametersBase&& base) + : ParametersBase(std::move(base)) + , m_num_groups(this->template get>().get_cont_freq_mat().get_num_groups()) { } @@ -401,7 +423,7 @@ class Parameters : public ParametersBase template static IOResult deserialize(IOContext& io) { - BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); return success(Parameters(std::move(base))); } }; diff --git a/cpp/models/lct_secir/parameters_io.h b/cpp/models/lct_secir/parameters_io.h index a071902645..ba659a35af 100644 --- a/cpp/models/lct_secir/parameters_io.h +++ b/cpp/models/lct_secir/parameters_io.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -50,13 +50,13 @@ namespace details /** * @brief Processes one entry of an RKI data set for the definition of an initial value vector for an LCT population. -* -* Takes one entry of an RKI data vector and changes the value in populations accordingly. +* +* Takes one entry of an RKI data vector and changes the value in populations accordingly. * This function provides sub-functionality of the set_initial_values_from_confirmed_cases() function. * * @param[out] populations The populations for which the inital data should be computed and set. * @param[in] entry The entry of the RKI data set. -* @param[in] offset The offset between the date of the entry and the date for which the +* @param[in] offset The offset between the date of the entry and the date for which the * initial value vector is calculated. * @param[in] staytimes A vector of the average time spent in each compartment defined in InfectionState * (for the group under consideration) for which the initial value vector is calculated. @@ -67,7 +67,7 @@ namespace details * @param[in] criticalPerSevere Probability (for the group under consideration) * for which the initial value vector is calculated. * @param[in] scale_confirmed_cases Factor by which to scale the confirmed cases of RKI data to consider unreported cases. -* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. +* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. * This defines the number of age groups and the numbers of subcompartments. * @tparam EntryType The type of the data entry of the RKI data. * @tparam Group The age group of the entry the should be processed. @@ -342,31 +342,31 @@ void process_entry(Populations& populations, const EntryType& entry, int offset, /** * @brief Computes an initialization vector for an LCT population with case data from RKI recursively for each age group * (or for one age group in the case without age resolution). -* +* * Please use the set_initial_values_from_reported_data() function, which calls this function automatically! -* This function calculates a segment referring to the defined age group of the initial value vector with +* This function calculates a segment referring to the defined age group of the initial value vector with * subcompartments using the rki_data and the parameters. * The values for the whole initial value vector stored in populations are calculated recursively. * * @param[in] rki_data Vector with the RKI data. * @param[out] populations The populations for which the inital data should be computed and set. -* @param[in] parameters The parameters that should be used to calculate the initial values. +* @param[in] parameters The parameters that should be used to calculate the initial values. * Probabilities and mean stay times are used. * @param[in] date Date for which the initial values should be computed. date is the start date of the simulation. -* @param[in] total_population Total size of the population of Germany or of every age group. -* @param[in] scale_confirmed_cases Factor(s for each age group) by which to scale the confirmed cases of the rki data +* @param[in] total_population Total size of the population of Germany or of every age group. +* @param[in] scale_confirmed_cases Factor(s for each age group) by which to scale the confirmed cases of the rki data * to consider unreported cases. -* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. +* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. * This defines the number of age groups and the numbers of subcompartments. -* @tparam EntryType is expected to be ConfirmedCasesNoAgeEntry for data that is not age resolved and +* @tparam EntryType is expected to be ConfirmedCasesNoAgeEntry for data that is not age resolved and * ConfirmedCasesDataEntry for age resolved data. See also epi_data.h. -* @tparam Group The age group for which the initial values should be calculated. The function is called recursively +* @tparam Group The age group for which the initial values should be calculated. The function is called recursively * such that the initial values are calculated for every age group if Group is zero at the beginning. * @returns Any io errors that happen during data processing. */ template IOResult set_initial_values_from_confirmed_cases(Populations& populations, const std::vector& rki_data, - const Parameters& parameters, const Date date, + const Parameters& parameters, const Date date, const std::vector& total_population, const std::vector& scale_confirmed_cases) { @@ -376,15 +376,19 @@ IOResult set_initial_values_from_confirmed_cases(Populations& populations, // Define variables for parameters. std::vector staytimes((size_t)InfectionState::Count, -1.); - staytimes[(size_t)InfectionState::Exposed] = parameters.template get()[Group]; - staytimes[(size_t)InfectionState::InfectedNoSymptoms] = parameters.template get()[Group]; - staytimes[(size_t)InfectionState::InfectedSymptoms] = parameters.template get()[Group]; - staytimes[(size_t)InfectionState::InfectedSevere] = parameters.template get()[Group]; - staytimes[(size_t)InfectionState::InfectedCritical] = parameters.template get()[Group]; + staytimes[(size_t)InfectionState::Exposed] = parameters.template get>()[Group]; + staytimes[(size_t)InfectionState::InfectedNoSymptoms] = + parameters.template get>()[Group]; + staytimes[(size_t)InfectionState::InfectedSymptoms] = + parameters.template get>()[Group]; + staytimes[(size_t)InfectionState::InfectedSevere] = + parameters.template get>()[Group]; + staytimes[(size_t)InfectionState::InfectedCritical] = + parameters.template get>()[Group]; ScalarType inv_prob_SymptomsPerNoSymptoms = - 1 / (1 - parameters.template get()[Group]); - ScalarType prob_SeverePerInfectedSymptoms = parameters.template get()[Group]; - ScalarType prob_CriticalPerSevere = parameters.template get()[Group]; + 1 / (1 - parameters.template get>()[Group]); + ScalarType prob_SeverePerInfectedSymptoms = parameters.template get>()[Group]; + ScalarType prob_CriticalPerSevere = parameters.template get>()[Group]; ScalarType min_offset_needed = std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - staytimes[(size_t)InfectionState::InfectedSevere] - @@ -463,19 +467,19 @@ IOResult set_initial_values_from_confirmed_cases(Populations& populations, } /** -* @brief Computes the total number of patients in Intensive Care Units (in all groups and subcompartments) +* @brief Computes the total number of patients in Intensive Care Units (in all groups and subcompartments) * in the provided Population. -* -* This function calculates the total number of individuals within the compartment InfectedCritical -* for the Populations-data provided, irrespective of their subcompartment or group. -* This total number can be used to scale the entries so that the total number in InfectedCritical is equal to -* the number of ICU patients reported in the DIVI data. +* +* This function calculates the total number of individuals within the compartment InfectedCritical +* for the Populations-data provided, irrespective of their subcompartment or group. +* This total number can be used to scale the entries so that the total number in InfectedCritical is equal to +* the number of ICU patients reported in the DIVI data. * Please use the set_initial_values_from_reported_data() function, which calls this function automatically! * * @param[in] populations The populations for which the total number in InfectedCritical should be computed. -* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. +* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. * This defines the number of age groups and the numbers of subcompartments. -* @tparam Group The age group for which the total number should be calculated. The function is called recursively +* @tparam Group The age group for which the total number should be calculated. The function is called recursively * such that the total number in InfectedCritical within all groups is calculated if Group is zero at the beginning. * @returns The total number of patients in Intensive Care Units (in all groups and subcompartments). */ @@ -520,28 +524,28 @@ IOResult get_icu_from_divi_data(const std::vector& divi_d } /** -* @brief Rescales the entries for InfectedCritical in populations such that the total number +* @brief Rescales the entries for InfectedCritical in populations such that the total number * equals the reported number. -* -* This function rescales the entries for InfectedCritical in the given population for every group and subcompartment +* +* This function rescales the entries for InfectedCritical in the given population for every group and subcompartment * such that the total number in all InfectedCritical compartments equals the reported number infectedCritical_reported. * * If the total number of individuals in InfectedCritical in populations is zero and the reported number is not, -* the reported number is distributed uniformly across the groups. -* Within the groups, the number is distributed uniformly to the subcompartments. -* Note that especially the uniform distribution across groups is not necessarily realistic, +* the reported number is distributed uniformly across the groups. +* Within the groups, the number is distributed uniformly to the subcompartments. +* Note that especially the uniform distribution across groups is not necessarily realistic, * because the need for intensive care can differ by group. * * @param[in,out] populations The populations for which the entries of the InfectedCritical compartments are rescaled. -* @param[in] infectedCritical_reported The reported number for patients in ICU. The total number of individuals in the +* @param[in] infectedCritical_reported The reported number for patients in ICU. The total number of individuals in the * InfectedCritical compartment in populations will be equal to this value afterward. * You can calculate this value with the get_icu_from_divi_data() function. -* @param[in] infectedCritical_populations The current total number of individuals in the InfectedCritical compartment +* @param[in] infectedCritical_populations The current total number of individuals in the InfectedCritical compartment * in populations. You can calculate this value with the get_total_InfectedCritical_from_populations() function. -* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. +* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. * This defines the number of age groups and the numbers of subcompartments. -* @tparam Group The age group for which the entries of InfectedCritical should be scaled. -* The function is called recursively for the groups. The total number in the InfectedCritical compartments is only +* @tparam Group The age group for which the entries of InfectedCritical should be scaled. +* The function is called recursively for the groups. The total number in the InfectedCritical compartments is only * equal to infectedCritical_reported after the function call if Group is set to zero in the beginning. * @returns Any io errors that happen during the scaling. */ @@ -624,49 +628,49 @@ IOResult rescale_to_divi_data(Populations& populations, const ScalarType i /** * @brief Computes an initialization vector for an LCT population with case data from RKI (and possibly DIVI data). -* +* * Use just one group in the definition of the populations to not divide between age groups. * Otherwise, the number of groups has to match the number of RKI age groups. * The function calculates an initial value vector referring to an LCT population and updates the initial value vector * in the populations class. * For the computation expected stay times in the subcompartments defined in the parameters variable are used. -* To calculate the initial values, we assume for simplicity that individuals stay in the subcompartment +* To calculate the initial values, we assume for simplicity that individuals stay in the subcompartment * for exactly the expected time. * The RKI data are linearly interpolated within one day to match the expected stay time in a subcompartment. -* The RKI data should contain data for each needed day with or without division of age groups, +* The RKI data should contain data for each needed day with or without division of age groups, * the completeness of the dates is not verified. * Data can be downloaded e.g. with the file pycode/memilio-epidata/memilio/epidata/getCaseData.py, which creates files * named e.g. cases_all_germany.json for no groups or cases_all_age.json with division in age groups or similar names. -* One should set impute_dates=True so that missing dates are imputed. +* One should set impute_dates=True so that missing dates are imputed. * To read the data into a vector, use the functionality from epi_data.h. -* The data and the number of entries in the total_population and scale_confirmed_cases vectors have to match the +* The data and the number of entries in the total_population and scale_confirmed_cases vectors have to match the * number of groups used in Populations. * -* Additionally, one can scale the result from the calculation with the RKI data to match the reported number of -* patients in ICUs. The patient numbers are provided by DIVI and can be downloaded e.g. using +* Additionally, one can scale the result from the calculation with the RKI data to match the reported number of +* patients in ICUs. The patient numbers are provided by DIVI and can be downloaded e.g. using * pycode/memilio-epidata/memilio/epidata/getDIVIData.py (One should also set impute_dates=True so that missing dates * are imputed.). Again, to read the data into a vector, use the functionality from epi_data.h. * * @param[in] rki_data Vector with the RKI data. * @param[out] populations The populations for which the inital data should be computed and set. -* @param[in] parameters The parameters that should be used to calculate the initial values. +* @param[in] parameters The parameters that should be used to calculate the initial values. * Probabilities and mean stay times are used. * @param[in] date Date for which the initial values should be computed. date is the start date of the simulation. -* @param[in] total_population Total size of the population of Germany or of every age group. -* @param[in] scale_confirmed_cases Factor(s for each age group) by which to scale the confirmed cases of the rki data +* @param[in] total_population Total size of the population of Germany or of every age group. +* @param[in] scale_confirmed_cases Factor(s for each age group) by which to scale the confirmed cases of the rki data * to consider unreported cases. -* @param[in] divi_data Vector with DIVI data used to scale the number of individuals in the InfectedCritical -* compartments in populations so that the total number match the reported number. +* @param[in] divi_data Vector with DIVI data used to scale the number of individuals in the InfectedCritical +* compartments in populations so that the total number match the reported number. * For the default value (an empty vector), the calculated populations using the RKI data is not scaled. -* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. +* @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. * This defines the number of age groups and the numbers of subcompartments. -* @tparam EntryType is expected to be ConfirmedCasesNoAgeEntry for data that is not age resolved and +* @tparam EntryType is expected to be ConfirmedCasesNoAgeEntry for data that is not age resolved and * ConfirmedCasesDataEntry for age resolved data. See also epi_data.h. * @returns Any io errors that happen during data processing. */ template IOResult set_initial_values_from_reported_data(const std::vector& rki_data, Populations& populations, - const Parameters& parameters, const Date date, + const Parameters& parameters, const Date date, const std::vector& total_population, const std::vector& scale_confirmed_cases, const std::vector& divi_data = std::vector()) diff --git a/cpp/models/ode_seair/model.h b/cpp/models/ode_seair/model.h index 05d61b70d9..0ad78a130c 100644 --- a/cpp/models/ode_seair/model.h +++ b/cpp/models/ode_seair/model.h @@ -32,7 +32,7 @@ namespace mio namespace oseair { -template +template class Model : public mio::CompartmentalModel, Parameters> { using Base = mio::CompartmentalModel, Parameters>; @@ -42,7 +42,7 @@ class Model : public mio::CompartmentalModel +template struct SocialDistancing { using Type = FP; static Type get_default() @@ -52,7 +52,7 @@ struct SocialDistancing { /** * @brief Quarantining. */ -template +template struct Quarantined { using Type = FP; static Type get_default() @@ -68,7 +68,7 @@ struct Quarantined { /** * @brief Rate of testing. */ -template +template struct TestingRate { using Type = FP; static Type get_default() @@ -84,7 +84,7 @@ struct TestingRate { /** * @brief Recovery rate. */ -template +template struct RecoveryRate { using Type = FP; static Type get_default() @@ -100,7 +100,7 @@ struct RecoveryRate { /** * @brief Death Rate. */ -template +template struct DeathRate { using Type = FP; static Type get_default() @@ -116,7 +116,7 @@ struct DeathRate { /** * @brief Inverse of the latent period of the virus. */ -template +template struct TimeExposed { using Type = FP; static Type get_default() @@ -132,7 +132,7 @@ struct TimeExposed { /** * @brief Infectious period for unconfirmed infected people. */ -template +template struct RecoveryRateFromAsymptomatic { using Type = FP; static Type get_default() @@ -148,7 +148,7 @@ struct RecoveryRateFromAsymptomatic { /** * @brief Rate recovered people become susceptible again. */ -template +template struct TimeRecoveredInv { using Type = FP; static Type get_default() @@ -161,7 +161,7 @@ struct TimeRecoveredInv { } }; -template +template using ParametersBase = ParameterSet, Quarantined, TestingRate, RecoveryRate, DeathRate, TimeExposed, RecoveryRateFromAsymptomatic, TimeRecoveredInv>; @@ -169,7 +169,7 @@ using ParametersBase = /** * @brief Parameters of an SEAIR model. */ -template +template class Parameters : public ParametersBase { public: @@ -195,7 +195,7 @@ class Parameters : public ParametersBase return true; } - const double tol_times = 1e-1; // accepted tolerance for compartment stays + const FP tol_times = 1e-1; // accepted tolerance for compartment stays if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeExposed {:.4f} smaller {:.4f}. Please " "note that unreasonably small compartment stays lead to massively increased run time. " diff --git a/cpp/models/ode_secir/analyze_result.h b/cpp/models/ode_secir/analyze_result.h index 717b485e24..4c55c3def3 100644 --- a/cpp/models/ode_secir/analyze_result.h +++ b/cpp/models/ode_secir/analyze_result.h @@ -35,8 +35,8 @@ namespace osecir * @param p percentile value in open interval (0, 1) * @return p percentile of the parameters over all runs */ -template -std::vector ensemble_params_percentile(const std::vector>& ensemble_params, double p) +template +std::vector ensemble_params_percentile(const std::vector>& ensemble_params, FP p) { assert(p > 0.0 && p < 1.0 && "Invalid percentile value."); @@ -44,12 +44,12 @@ std::vector ensemble_params_percentile(const std::vector single_element_ensemble(num_runs); + std::vector single_element_ensemble(num_runs); // lambda function that calculates the percentile of a single parameter std::vector percentile(num_nodes, Model((int)num_groups)); auto param_percentil = [&ensemble_params, p, num_runs, &percentile](auto n, auto get_param) mutable { - std::vector single_element(num_runs); + std::vector single_element(num_runs); for (size_t run = 0; run < num_runs; run++) { auto const& params = ensemble_params[run][n]; single_element[run] = get_param(params); @@ -63,81 +63,70 @@ std::vector ensemble_params_percentile(const std::vector auto& { - return model.populations[{i, (InfectionState)compart}]; - }); + param_percentil(node, [compart, i](auto&& model) -> auto& { + return model.populations[{i, (InfectionState)compart}]; + }); } // times - param_percentil( - node, [i](auto&& model) -> auto& { return model.parameters.template get>()[i]; }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); //probs - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); } // group independent params - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { - return model.parameters.template get>(); - }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); for (size_t run = 0; run < num_runs; run++) { auto const& params = ensemble_params[run][node]; single_element_ensemble[run] = - params.parameters.template get>() * params.populations.get_total(); + params.parameters.template get>() * params.populations.get_total(); } std::sort(single_element_ensemble.begin(), single_element_ensemble.end()); - percentile[node].parameters.template set>( + percentile[node].parameters.template set>( single_element_ensemble[static_cast(num_runs * p)]); } return percentile; diff --git a/cpp/models/ode_secir/model.h b/cpp/models/ode_secir/model.h old mode 100755 new mode 100644 index 83e9114c26..132fbd545e --- a/cpp/models/ode_secir/model.h +++ b/cpp/models/ode_secir/model.h @@ -31,6 +31,9 @@ #include "memilio/math/eigen_util.h" #include "memilio/math/interpolation.h" +#include +#include + namespace mio { namespace osecir @@ -56,7 +59,7 @@ using Flows = TypeList>; // clang-format on -template +template class Model : public FlowModel, Parameters, Flows> { using Base = FlowModel, Parameters, Flows>; @@ -82,18 +85,18 @@ class Model : public FlowModelparameters; AgeGroup n_agegroups = params.get_num_groups(); - ContactMatrixGroup const& contact_matrix = params.template get>(); + ContactMatrixGroup const& contact_matrix = params.template get>(); - auto icu_occupancy = 0.0; - auto test_and_trace_required = 0.0; - for (auto i = AgeGroup(0); i < n_agegroups; ++i) { + FP icu_occupancy = 0.0; + FP test_and_trace_required = 0.0; + for (AgeGroup i = 0; i < n_agegroups; ++i) { test_and_trace_required += (1 - params.template get>()[i]) / params.template get>()[i] * this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptoms}); icu_occupancy += this->populations.get_from(pop, {i, InfectionState::InfectedCritical}); } - for (auto i = AgeGroup(0); i < n_agegroups; i++) { + for (AgeGroup i = 0; i < n_agegroups; i++) { size_t Si = this->populations.get_flat_index({i, InfectionState::Susceptible}); size_t Ei = this->populations.get_flat_index({i, InfectionState::Exposed}); @@ -104,7 +107,7 @@ class Model : public FlowModelpopulations.get_flat_index({i, InfectionState::InfectedSevere}); size_t ICri = this->populations.get_flat_index({i, InfectionState::InfectedCritical}); - for (auto j = AgeGroup(0); j < n_agegroups; j++) { + for (AgeGroup j = 0; j < n_agegroups; j++) { size_t Sj = this->populations.get_flat_index({j, InfectionState::Susceptible}); size_t Ej = this->populations.get_flat_index({j, InfectionState::Exposed}); size_t INSj = this->populations.get_flat_index({j, InfectionState::InfectedNoSymptoms}); @@ -114,27 +117,27 @@ class Model : public FlowModelpopulations.get_flat_index({j, InfectionState::Recovered}); //symptomatic are less well quarantined when testing and tracing is overwhelmed so they infect more people - auto riskFromInfectedSymptomatic = - smoother_cosine(test_and_trace_required, params.template get>(), - params.template get>() * - params.template get>(), - params.template get>()[j], - params.template get>()[j]); + FP riskFromInfectedSymptomatic = + smoother_cosine(test_and_trace_required, params.template get>(), + params.template get>() * + params.template get>(), + params.template get>()[j], + params.template get>()[j]); // effective contact rate by contact rate between groups i and j and damping j - ScalarType season_val = - (1 + params.template get>() * - sin(3.141592653589793 * ((params.template get() + t) / 182.5 + 0.5))); - ScalarType cont_freq_eff = - season_val * contact_matrix.get_matrix_at(t)(static_cast((size_t)i), - static_cast((size_t)j)); - ScalarType Nj = + FP season_val = (1 + params.template get>() * + sin(std::numbers::pi_v * + ((params.template get>() + t) / 182.5 + 0.5))); + FP cont_freq_eff = + season_val * contact_matrix.get_matrix_at(SimulationTime(t))( + static_cast((size_t)i), static_cast((size_t)j)); + FP Nj = pop[Sj] + pop[Ej] + pop[INSj] + pop[ISyj] + pop[ISevj] + pop[ICrj] + pop[Rj]; // without died people - const ScalarType divNj = (Nj < Limits::zero_tolerance()) ? 0.0 : 1.0 / Nj; - ScalarType dummy_S = y[Si] * cont_freq_eff * divNj * - params.template get>()[i] * - (params.template get>()[j] * pop[INSj] + - riskFromInfectedSymptomatic * pop[ISyj]); + const FP divNj = (Nj < Limits::zero_tolerance()) ? FP(0.0) : FP(1.0 / Nj); + FP dummy_S = y[Si] * cont_freq_eff * divNj * + params.template get>()[i] * + (params.template get>()[j] * pop[INSj] + + riskFromInfectedSymptomatic * pop[ISyj]); // Susceptible -> Exposed flows[this->template get_flat_flow_index({i})] += @@ -142,16 +145,15 @@ class Model : public FlowModel( icu_occupancy, 0.90 * params.template get>(), params.template get>(), - params.template get>()[i], 0); + params.template get>()[i], 0.0); - ScalarType deathsPerSevereAdjusted = - params.template get>()[i] - criticalPerSevereAdjusted; + FP deathsPerSevereAdjusted = params.template get>()[i] - criticalPerSevereAdjusted; // Exposed -> InfectedNoSymptoms flows[this->template get_flat_flow_index( - {i})] = (1 / params.template get>()[i]) * y[Ei]; + {i})] = (1.0 / params.template get>()[i]) * y[Ei]; // InfectedNoSymptoms -> InfectedSymptoms / Recovered flows[this->template get_flat_flow_index>()[i]) * y[INSi]; flows[this->template get_flat_flow_index( {i})] = params.template get>()[i] * - (1 / params.template get>()[i]) * y[INSi]; + (1.0 / params.template get>()[i]) * y[INSi]; // InfectedNoSymptomsConfirmed -> InfectedSymptomsConfirmed / Recovered flows[this->template get_flat_flow_index({i})] = - (1 - params.template get>()[i]) * - (1 / params.template get>()[i]) * y[INSCi]; + (1.0 - params.template get>()[i]) * + (1.0 / params.template get>()[i]) * y[INSCi]; flows[this->template get_flat_flow_index({i})] = params.template get>()[i] * - (1 / params.template get>()[i]) * y[INSCi]; + (1.0 / params.template get>()[i]) * y[INSCi]; // InfectedSymptoms -> InfectedSevere / Recovered flows[this->template get_flat_flow_index( @@ -187,14 +189,14 @@ class Model : public FlowModel>()[i] * y[ISyCi]; flows[this->template get_flat_flow_index({i})] = - (1 - params.template get>()[i]) / + (1.0 - params.template get>()[i]) / params.template get>()[i] * y[ISyCi]; // InfectedSevere -> InfectedCritical / Recovered / Dead flows[this->template get_flat_flow_index( {i})] = criticalPerSevereAdjusted / params.template get>()[i] * y[ISevi]; flows[this->template get_flat_flow_index({i})] = - (1 - params.template get>()[i]) / + (1.0 - params.template get>()[i]) / params.template get>()[i] * y[ISevi]; flows[this->template get_flat_flow_index({i})] = deathsPerSevereAdjusted / params.template get>()[i] * y[ISevi]; @@ -204,7 +206,7 @@ class Model : public FlowModel>()[i] / params.template get>()[i] * y[ICri]; flows[this->template get_flat_flow_index( - {i})] = (1 - params.template get>()[i]) / + {i})] = (1.0 - params.template get>()[i]) / params.template get>()[i] * y[ICri]; } } @@ -241,7 +243,7 @@ class Model : public FlowModel>> +template >> class Simulation; /** @@ -252,8 +254,8 @@ class Simulation; * @tparam FP floating point type, e.g., double. * @tparam Base simulation type that uses a secir compartment model. see Simulation. */ -template >> -double get_infections_relative(const Simulation& model, FP t, const Eigen::Ref>& y); +template >> +FP get_infections_relative(const Simulation& model, FP t, const Eigen::Ref>& y); /** * specialization of compartment model simulation for secir models. @@ -285,16 +287,18 @@ class Simulation : public BaseT */ Eigen::Ref> advance(FP tmax) { + using std::min; auto& t_end_dyn_npis = this->get_model().parameters.get_end_dynamic_npis(); auto& dyn_npis = this->get_model().parameters.template get>(); auto& contact_patterns = this->get_model().parameters.template get>(); FP delay_npi_implementation; // delay which is needed to implement a NPI that is criterion-dependent - auto t = BaseT::get_result().get_last_time(); - const auto dt = dyn_npis.get_thresholds().size() > 0 ? dyn_npis.get_interval().get() : tmax; + FP t = BaseT::get_result().get_last_time(); + const FP dt = dyn_npis.get_thresholds().size() > 0 ? FP(dyn_npis.get_interval().get()) : FP(tmax); while (t < tmax) { - auto dt_eff = std::min({dt, tmax - t, m_t_last_npi_check + dt - t}); + FP dt_eff = min(dt, tmax - t); + dt_eff = min(dt_eff, m_t_last_npi_check + dt - t); BaseT::advance(t + dt_eff); if (t > 0) { @@ -307,22 +311,22 @@ class Simulation : public BaseT t = t + dt_eff; if (dyn_npis.get_thresholds().size() > 0) { - if (floating_point_greater_equal(t, m_t_last_npi_check + dt)) { + if (floating_point_greater_equal(t, m_t_last_npi_check + dt)) { if (t < t_end_dyn_npis) { auto inf_rel = get_infections_relative(*this, t, this->get_result().get_last_value()) * dyn_npis.get_base_value(); auto exceeded_threshold = dyn_npis.get_max_exceeded_threshold(inf_rel); if (exceeded_threshold != dyn_npis.get_thresholds().end() && (exceeded_threshold->first > m_dynamic_npi.first || - t > ScalarType(m_dynamic_npi.second))) { //old npi was weaker or is expired + t > FP(m_dynamic_npi.second))) { //old npi was weaker or is expired - auto t_start = SimulationTime(t + delay_npi_implementation); - auto t_end = t_start + SimulationTime(ScalarType(dyn_npis.get_duration())); + auto t_start = SimulationTime(t + delay_npi_implementation); + auto t_end = t_start + SimulationTime(FP(dyn_npis.get_duration())); m_dynamic_npi = std::make_pair(exceeded_threshold->first, t_end); - implement_dynamic_npis(contact_patterns.get_cont_freq_mat(), exceeded_threshold->second, - t_start, t_end, [](auto& g) { - return make_contact_damping_matrix(g); - }); + implement_dynamic_npis(contact_patterns.get_cont_freq_mat(), exceeded_threshold->second, + t_start, t_end, [](auto& g) { + return make_contact_damping_matrix(g); + }); } } m_t_last_npi_check = t; @@ -337,8 +341,8 @@ class Simulation : public BaseT } private: - double m_t_last_npi_check; - std::pair m_dynamic_npi = {-std::numeric_limits::max(), mio::SimulationTime(0)}; + FP m_t_last_npi_check; + std::pair> m_dynamic_npi = {-std::numeric_limits::max(), mio::SimulationTime(0)}; }; /** @@ -353,11 +357,11 @@ class Simulation : public BaseT * * @return Returns the result of the simulation. */ -template +template inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, std::unique_ptr>&& integrator_core = nullptr) { - return mio::simulate, Simulation<>>(t0, tmax, dt, model, std::move(integrator_core)); + return mio::simulate, Simulation>(t0, tmax, dt, model, std::move(integrator_core)); } /** @@ -372,24 +376,23 @@ inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, * * @return Returns the result of the Flowsimulation. */ -template +template inline auto simulate_flows(FP t0, FP tmax, FP dt, const Model& model, std::unique_ptr>&& integrator_core = nullptr) { - return mio::simulate_flows, Simulation>>>(t0, tmax, dt, model, - std::move(integrator_core)); + return mio::simulate_flows, Simulation>>>( + t0, tmax, dt, model, std::move(integrator_core)); } //see declaration above. template -double get_infections_relative(const Simulation& sim, FP /* t*/, - const Eigen::Ref>& y) +FP get_infections_relative(const Simulation& sim, FP /* t*/, const Eigen::Ref>& y) { - double sum_inf = 0; + FP sum_inf = 0; for (auto i = AgeGroup(0); i < sim.get_model().parameters.get_num_groups(); ++i) { sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptoms}); } - auto inf_rel = sum_inf / sim.get_model().populations.get_total(); + FP inf_rel = sum_inf / sim.get_model().populations.get_total(); return inf_rel; } @@ -405,6 +408,8 @@ double get_infections_relative(const Simulation& sim, FP /* t*/, template IOResult get_reproduction_number(size_t t_idx, const Simulation& sim) { + using std::abs; + using std::sin; if (!(t_idx < static_cast(sim.get_result().get_num_time_points()))) { return mio::failure(mio::StatusCode::OutOfRange, "t_idx is not a valid index for the TimeSeries"); @@ -415,17 +420,17 @@ IOResult get_reproduction_number(size_t t_idx, const Simulation& s //The infected compartments in the SECIR Model are: Exposed, Carrier, Infected, Hospitalized, ICU in respective agegroups const size_t num_infected_compartments = 5; const size_t total_infected_compartments = num_infected_compartments * num_groups; - const double pi = std::acos(-1); + const FP pi = std::numbers::pi_v; //F encodes new Infections and V encodes transition times in the next-generation matrix calculation of R_t - Eigen::MatrixXd F(total_infected_compartments, total_infected_compartments); - Eigen::MatrixXd V(total_infected_compartments, total_infected_compartments); - F = Eigen::MatrixXd::Zero(total_infected_compartments, - total_infected_compartments); //Initialize matrices F and V with zeroes - V = Eigen::MatrixXd::Zero(total_infected_compartments, total_infected_compartments); - - auto test_and_trace_required = 0.0; - auto icu_occupancy = 0.0; + Eigen::MatrixX F(total_infected_compartments, total_infected_compartments); + Eigen::MatrixX V(total_infected_compartments, total_infected_compartments); + F = Eigen::MatrixX::Zero(total_infected_compartments, + total_infected_compartments); //Initialize matrices F and V with zeroes + V = Eigen::MatrixX::Zero(total_infected_compartments, total_infected_compartments); + + FP test_and_trace_required = 0.0; + FP icu_occupancy = 0.0; for (auto i = AgeGroup(0); i < (mio::AgeGroup)num_groups; ++i) { test_and_trace_required += (1 - params.template get>()[i]) / @@ -436,39 +441,40 @@ IOResult get_reproduction_number(size_t t_idx, const Simulation& s t_idx)[sim.get_model().populations.get_flat_index({i, InfectionState::InfectedCritical})]; } - double season_val = - (1 + params.template get>() * - sin(pi * - ((sim.get_model().parameters.template get() + sim.get_result().get_time(t_idx)) / 182.5 + - 0.5))); - ContactMatrixGroup const& contact_matrix = sim.get_model().parameters.template get>(); + FP season_val = + (1 + + params.template get>() * + sin(pi * + ((sim.get_model().parameters.template get>() + sim.get_result().get_time(t_idx)) / 182.5 + + 0.5))); + ContactMatrixGroup const& contact_matrix = sim.get_model().parameters.template get>(); - Eigen::MatrixXd cont_freq_eff(num_groups, num_groups); - Eigen::MatrixXd riskFromInfectedSymptomatic_derivatives(num_groups, num_groups); - Eigen::VectorXd divN(num_groups); - Eigen::VectorXd riskFromInfectedSymptomatic(num_groups); + Eigen::MatrixX cont_freq_eff(num_groups, num_groups); + Eigen::MatrixX riskFromInfectedSymptomatic_derivatives(num_groups, num_groups); + Eigen::VectorX divN(num_groups); + Eigen::VectorX riskFromInfectedSymptomatic(num_groups); for (mio::AgeGroup k = 0; k < (mio::AgeGroup)num_groups; k++) { - double temp = sim.get_result().get_value( - t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::Susceptible})] + - sim.get_result().get_value( - t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::Exposed})] + - sim.get_result().get_value( - t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedNoSymptoms})] + - sim.get_result().get_value( - t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedSymptoms})] + - sim.get_result().get_value( - t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedSevere})] + - sim.get_result().get_value( - t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedCritical})] + - sim.get_result().get_value( - t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::Recovered})]; - if (temp == 0) { - temp = 1; + FP temp = sim.get_result().get_value( + t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::Susceptible})] + + sim.get_result().get_value( + t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::Exposed})] + + sim.get_result().get_value( + t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedNoSymptoms})] + + sim.get_result().get_value( + t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedSymptoms})] + + sim.get_result().get_value( + t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedSevere})] + + sim.get_result().get_value( + t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::InfectedCritical})] + + sim.get_result().get_value( + t_idx)[sim.get_model().populations.get_flat_index({k, InfectionState::Recovered})]; + if (temp < Limits::zero_tolerance()) { + temp = 1.0; } divN[(size_t)k] = 1 / temp; - riskFromInfectedSymptomatic[(size_t)k] = smoother_cosine( + riskFromInfectedSymptomatic[(size_t)k] = smoother_cosine( test_and_trace_required, params.template get>(), (params.template get>()) * params.template get>(), params.template get>()[k], @@ -488,34 +494,34 @@ IOResult get_reproduction_number(size_t t_idx, const Simulation& s (4 * params.template get>()) * (1 - params.template get>()[l]) / params.template get>()[l] * - std::sin(pi / (4 * params.template get>()) * - (test_and_trace_required - params.template get>())); + sin(pi / (4 * params.template get>()) * + (test_and_trace_required - params.template get>())); } } for (Eigen::Index l = 0; l < (Eigen::Index)num_groups; l++) { cont_freq_eff(l, (size_t)k) = - season_val * contact_matrix.get_matrix_at(static_cast(t_idx))( + season_val * contact_matrix.get_matrix_at(SimulationTime(static_cast(t_idx)))( static_cast((size_t)l), static_cast((size_t)k)); } } //Check criterion if matrix V will be invertible by checking if subblock J is invertible - Eigen::MatrixXd J(num_groups, num_groups); - J = Eigen::MatrixXd::Zero(num_groups, num_groups); + Eigen::MatrixX J(num_groups, num_groups); + J = Eigen::MatrixX::Zero(num_groups, num_groups); for (size_t i = 0; i < num_groups; i++) { J(i, i) = 1 / (params.template get>()[(mio::AgeGroup)i]); if (!(icu_occupancy < 0.9 * params.template get>() || - icu_occupancy > (double)(params.template get>()))) { + icu_occupancy > params.template get>())) { for (size_t j = 0; j < num_groups; j++) { J(i, j) -= sim.get_result().get_value(t_idx)[sim.get_model().populations.get_flat_index( {(mio::AgeGroup)i, InfectionState::InfectedSevere})] / params.template get>()[(mio::AgeGroup)i] * 5 * params.template get>()[(mio::AgeGroup)i] * pi / (params.template get>()) * - std::sin(pi / (0.1 * params.template get>()) * - (icu_occupancy - 0.9 * params.template get>())); + sin(pi / (0.1 * params.template get>()) * + (icu_occupancy - 0.9 * params.template get>())); } } } @@ -529,7 +535,7 @@ IOResult get_reproduction_number(size_t t_idx, const Simulation& s for (size_t i = 0; i < num_groups; i++) { for (size_t j = 0; j < num_groups; j++) { - double temp = 0; + FP temp = 0; for (Eigen::Index k = 0; k < (Eigen::Index)num_groups; k++) { temp += cont_freq_eff(i, k) * sim.get_result().get_value(t_idx)[sim.get_model().populations.get_flat_index( @@ -556,7 +562,7 @@ IOResult get_reproduction_number(size_t t_idx, const Simulation& s //Initialize the matrix V for (Eigen::Index i = 0; i < (Eigen::Index)num_groups; i++) { - double criticalPerSevereAdjusted = smoother_cosine( + FP criticalPerSevereAdjusted = smoother_cosine( icu_occupancy, 0.90 * params.template get>(), params.template get>(), params.template get>()[(mio::AgeGroup)i], 0); @@ -583,24 +589,17 @@ IOResult get_reproduction_number(size_t t_idx, const Simulation& s V = V.inverse(); - //Compute F*V - Eigen::MatrixXd NextGenMatrix(num_infected_compartments * num_groups, 5 * num_groups); - NextGenMatrix = F * V; - - //Compute the largest eigenvalue in absolute value - Eigen::ComplexEigenSolver ces; + Eigen::MatrixX NextGenMatrix(num_infected_compartments * num_groups, 5 * num_groups); + NextGenMatrix.noalias() = F * V; + // Compute the largest eigenvalue in absolute value + Eigen::ComplexEigenSolver> ces; ces.compute(NextGenMatrix); - const Eigen::VectorXcd eigen_vals = ces.eigenvalues(); - - Eigen::VectorXd eigen_vals_abs; - eigen_vals_abs.resize(eigen_vals.size()); + FP rho = ces.eigenvalues().cwiseAbs().maxCoeff(); - for (int i = 0; i < eigen_vals.size(); i++) { - eigen_vals_abs[i] = std::abs(eigen_vals[i]); - } - return mio::success(eigen_vals_abs.maxCoeff()); + return mio::success(rho); } + /** *@brief Computes the reproduction number for all time points of the Model output obtained by the Simulation. *@param sim The Model Simulation. @@ -613,7 +612,7 @@ Eigen::VectorX get_reproduction_numbers(const Simulation& sim) { Eigen::VectorX temp(sim.get_result().get_num_time_points()); for (int i = 0; i < sim.get_result().get_num_time_points(); i++) { - temp[i] = get_reproduction_number((size_t)i, sim).value(); + temp[i] = get_reproduction_number((size_t)i, sim).value(); } return temp; } @@ -625,8 +624,8 @@ Eigen::VectorX get_reproduction_numbers(const Simulation& sim) *@tparam Base simulation type that uses a SECIR compartment model. see Simulation. *@returns The computed reproduction number at the provided time point, potentially using linear interpolation. */ -template -IOResult get_reproduction_number(ScalarType t_value, const Simulation& sim) +template +IOResult get_reproduction_number(FP t_value, const Simulation& sim) { if (t_value < sim.get_result().get_time(0) || t_value > sim.get_result().get_last_time()) { return mio::failure(mio::StatusCode::OutOfRange, @@ -641,12 +640,12 @@ IOResult get_reproduction_number(ScalarType t_value, const Simulatio auto time_late = std::distance(times.begin(), std::lower_bound(times.begin(), times.end(), t_value)); - ScalarType y1 = get_reproduction_number(static_cast(time_late - 1), sim).value(); - ScalarType y2 = get_reproduction_number(static_cast(time_late), sim).value(); + FP y1 = get_reproduction_number(static_cast(time_late - 1), sim).value(); + FP y2 = get_reproduction_number(static_cast(time_late), sim).value(); auto result = linear_interpolation(t_value, sim.get_result().get_time(time_late - 1), sim.get_result().get_time(time_late), y1, y2); - return mio::success(static_cast(result)); + return mio::success(static_cast(result)); } /** @@ -660,8 +659,8 @@ IOResult get_reproduction_number(ScalarType t_value, const Simulatio * @tparam FP floating point type, e.g., double. * @tparam Base simulation type that uses a secir compartment model; see Simulation. */ -template , FP>> -auto get_mobility_factors(const Simulation& sim, FP /*t*/, const Eigen::Ref>& y) +template , FP>> +auto get_mobility_factors(const Simulation& sim, FP /*t*/, const Eigen::Ref>& y) { auto& params = sim.get_model().parameters; //parameters as arrays @@ -676,28 +675,28 @@ auto get_mobility_factors(const Simulation& sim, FP /*t*/, const Eigen::Re auto test_and_trace_required = ((1 - p_asymp) / params.template get>().array().template cast() * y_INS.array()) .sum(); - auto test_and_trace_capacity = double(params.template get>()); - auto test_and_trace_capacity_max_risk = double(params.template get>()); - auto riskFromInfectedSymptomatic = - smoother_cosine(test_and_trace_required, test_and_trace_capacity, - test_and_trace_capacity * test_and_trace_capacity_max_risk, p_inf.matrix(), p_inf_max.matrix()); + auto test_and_trace_capacity = FP(params.template get>()); + auto test_and_trace_capacity_max_risk = FP(params.template get>()); + auto riskFromInfectedSymptomatic = smoother_cosine(test_and_trace_required, test_and_trace_capacity, + test_and_trace_capacity * test_and_trace_capacity_max_risk, + p_inf.matrix(), p_inf_max.matrix()); //set factor for infected - auto factors = Eigen::VectorXd::Ones(y.rows()).eval(); + auto factors = Eigen::VectorX::Ones(y.rows()).eval(); slice(factors, {Eigen::Index(InfectionState::InfectedSymptoms), Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) .array() = riskFromInfectedSymptomatic; return factors; } -template , FP>> +template , FP>> auto test_commuters(Simulation& sim, Eigen::Ref> mobile_population, FP time) { - auto& model = sim.get_model(); - auto nondetection = 1.0; + auto& model = sim.get_model(); + FP nondetection = 1.0; if (time >= model.parameters.get_start_commuter_detection() && time < model.parameters.get_end_commuter_detection()) { - nondetection = (double)model.parameters.get_commuter_nondetection(); + nondetection = model.parameters.get_commuter_nondetection(); } for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); ++i) { auto INSi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptoms}); diff --git a/cpp/models/ode_secir/parameter_space.h b/cpp/models/ode_secir/parameter_space.h index 3646781a3f..60ee1f555e 100644 --- a/cpp/models/ode_secir/parameter_space.h +++ b/cpp/models/ode_secir/parameter_space.h @@ -43,28 +43,27 @@ namespace osecir * @param[in] tmax end time * @param[in] dev_rel maximum relative deviation from particular value(s) given in params */ -template -void set_params_distributions_normal(Model& model, double t0, double tmax, double dev_rel) +template +void set_params_distributions_normal(Model& model, FP t0, FP tmax, FP dev_rel) { - auto set_distribution = [dev_rel](UncertainValue& v, double min_val = 0.001) { - auto lower_bound = - std::min(std::max(min_val, (1 - dev_rel * 2.6) * v), 0.1 * std::numeric_limits::max()); - auto upper_bound = - std::min(std::max(min_val, (1 + dev_rel * 2.6) * v), 0.5 * std::numeric_limits::max()); + using std::max; + using std::min; + auto set_distribution = [dev_rel](UncertainValue& v, FP min_val = 0.001) { + auto lower_bound = min(max(min_val, (1 - dev_rel * 2.6) * v), 0.1 * std::numeric_limits::max()); + auto upper_bound = min(max(min_val, (1 + dev_rel * 2.6) * v), 0.5 * std::numeric_limits::max()); - if (mio::floating_point_equal(lower_bound, upper_bound, mio::Limits::zero_tolerance())) { + if (mio::floating_point_equal(lower_bound, upper_bound, mio::Limits::zero_tolerance())) { //MSVC has problems if standard deviation for normal distribution is zero mio::log_debug("Bounded ParameterDistribution has standard deviation close to zero. Therefore constant " "distribution is used."); v.set_distribution(ParameterDistributionConstant( - std::min(std::max(min_val, double(v)), 0.3 * std::numeric_limits::max()))); + min(max(min_val, v.value()), 0.3 * std::numeric_limits::max()))); } else { v.set_distribution(ParameterDistributionNormal( //add add limits for nonsense big values. Also mscv has a problem with a few doubles so this fixes it - lower_bound, upper_bound, - std::min(std::max(min_val, double(v)), 0.3 * std::numeric_limits::max()), - std::min(std::max(min_val, dev_rel * v), std::numeric_limits::max()))); + lower_bound, upper_bound, min(max(min_val, v.value()), 0.3 * std::numeric_limits::max()), + min(max(min_val, dev_rel * v), std::numeric_limits::max()))); } }; @@ -112,10 +111,10 @@ void set_params_distributions_normal(Model& model, double t0, double tmax, d ++i) { matrices.push_back(i); } - auto groups = Eigen::VectorXd::Constant(Eigen::Index(model.parameters.get_num_groups().get()), 1.0); + auto groups = Eigen::VectorX::Constant(Eigen::Index(model.parameters.get_num_groups().get()), 1.0); model.parameters.template get>().get_dampings().emplace_back( - mio::UncertainValue(0.5), mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(t0 + (tmax - t0) / 2), - matrices, groups); + mio::UncertainValue(0.5), mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(t0 + (tmax - t0) / 2), matrices, groups); set_distribution(model.parameters.template get>().get_dampings()[0].get_value(), 0.0); } @@ -123,14 +122,14 @@ void set_params_distributions_normal(Model& model, double t0, double tmax, d * draws a sample from the specified distributions for all parameters related to the demographics, e.g. population. * @param[inout] model Model including contact patterns for alle age groups */ -template +template void draw_sample_demographics(Model& model) { model.parameters.template get>().draw_sample(); model.parameters.template get>().draw_sample(); for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); i++) { - double group_total = model.populations.get_group_total(i); + FP group_total = model.populations.get_group_total(i); model.populations[{i, InfectionState::Exposed}].draw_sample(); model.populations[{i, InfectionState::InfectedNoSymptoms}].draw_sample(); @@ -153,7 +152,7 @@ void draw_sample_demographics(Model& model) * draws a sample from the specified distributions for all parameters related to the infection. * @param[inout] model Model including contact patterns for alle age groups */ -template +template void draw_sample_infection(Model& model) { model.parameters.template get>().draw_sample(); @@ -196,7 +195,7 @@ void draw_sample_infection(Model& model) * as SecirParams parameter values (cf. UncertainValue and SecirParams classes). * @param[inout] model Model including contact patterns for alle age groups. */ -template +template void draw_sample(Model& model) { draw_sample_infection(model); @@ -205,7 +204,7 @@ void draw_sample(Model& model) model.apply_constraints(); } -template +template Graph, MobilityParameters> draw_sample(Graph, MobilityParameters>& graph) { Graph, MobilityParameters> sampled_graph; diff --git a/cpp/models/ode_secir/parameters.h b/cpp/models/ode_secir/parameters.h index a8fa8393b6..76c06b6ffd 100644 --- a/cpp/models/ode_secir/parameters.h +++ b/cpp/models/ode_secir/parameters.h @@ -43,24 +43,24 @@ namespace osecir * If the start day is 180 and simulation takes place from t0=0 to * tmax=100 the days 180 to 280 of the year are simulated */ +template struct StartDay { - using Type = double; + using Type = FP; static Type get_default(AgeGroup) { - return 0.; + return Type(0.0); } static std::string name() { return "StartDay"; } }; - /** * @brief the seasonality in the SECIR model * the seasonality is given as (1+k*sin()) where the sine * curve is below one in summer and above one in winter */ -template +template struct Seasonality { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -76,7 +76,7 @@ struct Seasonality { /** * @brief the icu capacity in the SECIR model */ -template +template struct ICUCapacity { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -92,7 +92,7 @@ struct ICUCapacity { /** * @brief the (mean) latent time in day unit */ -template +template struct TimeExposed { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -109,7 +109,7 @@ struct TimeExposed { * @brief the (mean) time in day unit for asymptomatic cases that are infectious but * have not yet developed symptoms. */ -template +template struct TimeInfectedNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -126,7 +126,7 @@ struct TimeInfectedNoSymptoms { * @brief the infectious time for symptomatic cases that are infected but * who do not need to be hsopitalized in the SECIR model in day unit */ -template +template struct TimeInfectedSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -143,7 +143,7 @@ struct TimeInfectedSymptoms { * @brief the time people are 'simply' hospitalized before returning home in the SECIR model * in day unit */ -template +template struct TimeInfectedSevere { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -160,7 +160,7 @@ struct TimeInfectedSevere { * @brief the time people are treated by ICU before returning home in the SECIR model * in day unit */ -template +template struct TimeInfectedCritical { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -176,7 +176,7 @@ struct TimeInfectedCritical { /** * @brief probability of getting infected from a contact */ -template +template struct TransmissionProbabilityOnContact { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -192,7 +192,7 @@ struct TransmissionProbabilityOnContact { /** * @brief the relative InfectedNoSymptoms infectability */ -template +template struct RelativeTransmissionNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -208,7 +208,7 @@ struct RelativeTransmissionNoSymptoms { /** * @brief the percentage of asymptomatic cases in the SECIR model */ -template +template struct RecoveredPerInfectedNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -224,7 +224,7 @@ struct RecoveredPerInfectedNoSymptoms { /** * @brief the risk of infection from symptomatic cases in the SECIR model */ -template +template struct RiskOfInfectionFromSymptomatic { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -240,7 +240,7 @@ struct RiskOfInfectionFromSymptomatic { /** * @brief risk of infection from symptomatic cases increases as test and trace capacity is exceeded. */ -template +template struct MaxRiskOfInfectionFromSymptomatic { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -256,7 +256,7 @@ struct MaxRiskOfInfectionFromSymptomatic { /** * @brief the percentage of hospitalized patients per infected patients in the SECIR model */ -template +template struct SeverePerInfectedSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -272,7 +272,7 @@ struct SeverePerInfectedSymptoms { /** * @brief the percentage of ICU patients per hospitalized patients in the SECIR model */ -template +template struct CriticalPerSevere { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -288,7 +288,7 @@ struct CriticalPerSevere { /** * @brief the percentage of dead patients per ICU patients in the SECIR model */ -template +template struct DeathsPerCritical { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -304,7 +304,7 @@ struct DeathsPerCritical { /** * @brief the contact patterns within the society are modelled using an UncertainContactMatrix */ -template +template struct ContactPatterns { using Type = UncertainContactMatrix; static Type get_default(AgeGroup size) @@ -320,7 +320,7 @@ struct ContactPatterns { /** * @brief the NPIs that are enforced if certain infection thresholds are exceeded. */ -template +template struct DynamicNPIsInfectedSymptoms { using Type = DynamicNPIs; static Type get_default(AgeGroup /*size*/) @@ -336,12 +336,12 @@ struct DynamicNPIsInfectedSymptoms { /** * @brief The delay with which DynamicNPIs are implemented and enforced after exceedance of threshold. */ -template +template struct DynamicNPIsImplementationDelay { using Type = UncertainValue; static Type get_default(AgeGroup /*size*/) { - return 0.; + return Type(0.0); } static std::string name() { @@ -352,7 +352,7 @@ struct DynamicNPIsImplementationDelay { /** * @brief capacity to test and trace contacts of infected for quarantine per day. */ -template +template struct TestAndTraceCapacity { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -368,7 +368,7 @@ struct TestAndTraceCapacity { /** * @brief Multiplier for the test and trace capacity to determine when it is considered overloaded. */ -template +template struct TestAndTraceCapacityMaxRisk { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -381,20 +381,20 @@ struct TestAndTraceCapacityMaxRisk { } }; -template +template using ParametersBase = - ParameterSet, ICUCapacity, TestAndTraceCapacity, TestAndTraceCapacityMaxRisk, - ContactPatterns, DynamicNPIsImplementationDelay, DynamicNPIsInfectedSymptoms, - TimeExposed, TimeInfectedNoSymptoms, TimeInfectedSymptoms, TimeInfectedSevere, - TimeInfectedCritical, TransmissionProbabilityOnContact, RelativeTransmissionNoSymptoms, - RecoveredPerInfectedNoSymptoms, RiskOfInfectionFromSymptomatic, - MaxRiskOfInfectionFromSymptomatic, SeverePerInfectedSymptoms, CriticalPerSevere, - DeathsPerCritical>; + ParameterSet, Seasonality, ICUCapacity, TestAndTraceCapacity, + TestAndTraceCapacityMaxRisk, ContactPatterns, DynamicNPIsImplementationDelay, + DynamicNPIsInfectedSymptoms, TimeExposed, TimeInfectedNoSymptoms, TimeInfectedSymptoms, + TimeInfectedSevere, TimeInfectedCritical, TransmissionProbabilityOnContact, + RelativeTransmissionNoSymptoms, RecoveredPerInfectedNoSymptoms, + RiskOfInfectionFromSymptomatic, MaxRiskOfInfectionFromSymptomatic, + SeverePerInfectedSymptoms, CriticalPerSevere, DeathsPerCritical>; /** * @brief Parameters of an age-resolved SECIR/SECIHURD model. */ -template +template class Parameters : public ParametersBase { public: @@ -412,11 +412,11 @@ class Parameters : public ParametersBase /** * Percentage of infected commuters that are not detected. */ - double& get_commuter_nondetection() + FP& get_commuter_nondetection() { return m_commuter_nondetection; } - double get_commuter_nondetection() const + FP get_commuter_nondetection() const { return m_commuter_nondetection; } @@ -424,12 +424,12 @@ class Parameters : public ParametersBase /** * Time in simulation before which no infected commuters are detected. */ - double& get_start_commuter_detection() + FP& get_start_commuter_detection() { return m_start_commuter_detection; } - double get_start_commuter_detection() const + FP get_start_commuter_detection() const { return m_start_commuter_detection; } @@ -437,12 +437,12 @@ class Parameters : public ParametersBase /** * Time in simulation after which no infected commuters are detected. */ - double& get_end_commuter_detection() + FP& get_end_commuter_detection() { return m_end_commuter_detection; } - double get_end_commuter_detection() const + FP get_end_commuter_detection() const { return m_end_commuter_detection; } @@ -450,11 +450,11 @@ class Parameters : public ParametersBase /** * Time in simulation after which no dynamic NPIs are applied. */ - double& get_end_dynamic_npis() + FP& get_end_dynamic_npis() { return m_end_dynamic_npis; } - double get_end_dynamic_npis() const + FP get_end_dynamic_npis() const { return m_end_dynamic_npis; } @@ -474,7 +474,7 @@ class Parameters : public ParametersBase */ bool apply_constraints() { - const double tol_times = 1e-1; // accepted tolerance for compartment stays + const FP tol_times = 1e-1; // accepted tolerance for compartment stays int corrected = false; if (this->template get>() < 0.0 || this->template get>() > 0.5) { @@ -649,7 +649,7 @@ class Parameters : public ParametersBase return true; } - const double tol_times = 1e-1; // accepted tolerance for compartment stays + const FP tol_times = 1e-1; // accepted tolerance for compartment stays for (auto i = AgeGroup(0); i < AgeGroup(m_num_groups); ++i) { if (this->template get>()[i] < tol_times) { @@ -760,16 +760,16 @@ class Parameters : public ParametersBase private: AgeGroup m_num_groups; - double m_commuter_nondetection = 0.0; - double m_start_commuter_detection = 0.0; - double m_end_commuter_detection = 0.0; - double m_end_dynamic_npis = std::numeric_limits::max(); + FP m_commuter_nondetection = 0.0; + FP m_start_commuter_detection = 0.0; + FP m_end_commuter_detection = 0.0; + FP m_end_dynamic_npis = std::numeric_limits::max(); }; /** * @brief WIP !! TO DO: returns the actual, approximated reproduction rate */ -//double get_reprod_rate(Parameters const& params, double t, std::vector const& yt); +//FP get_reprod_rate(Parameters const& params, FP t, std::vector const& yt); } // namespace osecir } // namespace mio diff --git a/cpp/models/ode_secir/parameters_io.h b/cpp/models/ode_secir/parameters_io.h index 8471e25a00..0505d45563 100644 --- a/cpp/models/ode_secir/parameters_io.h +++ b/cpp/models/ode_secir/parameters_io.h @@ -29,6 +29,7 @@ #include "memilio/io/epi_data.h" #include "memilio/io/parameters_io.h" #include "memilio/io/result_io.h" +#include "memilio/math/math_utils.h" namespace mio { @@ -69,7 +70,7 @@ IOResult read_confirmed_cases_data( * @param[in] date Date at which the data is read. * @param[in] scaling_factor_inf Factors by which to scale the confirmed cases of rki data. */ -template +template IOResult set_confirmed_cases_data(std::vector>& model, std::vector& case_data, const std::vector& region, Date date, const std::vector& scaling_factor_inf) @@ -139,7 +140,11 @@ IOResult set_confirmed_cases_data(std::vector>& model, std::vect t_InfectedCritical, mu_C_R, mu_I_H, mu_H_U, scaling_factor_inf_full)); for (size_t node = 0; node < model.size(); node++) { - if (std::accumulate(num_InfectedSymptoms[node].begin(), num_InfectedSymptoms[node].end(), 0.0) > 0) { + + if (std::accumulate(num_InfectedSymptoms[node].begin(), num_InfectedSymptoms[node].end(), FP(0.0), + [](const FP& a, const FP& b) { + return evaluate_intermediate(a + b); + }) > 0.0) { size_t num_groups = (size_t)model[node].parameters.get_num_groups(); if (num_groups == num_age_groups) { for (size_t i = 0; i < num_groups; i++) { @@ -161,8 +166,10 @@ IOResult set_confirmed_cases_data(std::vector>& model, std::vect } } else { - const auto sum_vec = [](const std::vector& v) { - return std::accumulate(v.begin(), v.end(), 0.0); + const auto sum_vec = [](const std::vector& v) { + return std::accumulate(v.begin(), v.end(), FP(0.0), [](const FP& a, const FP& b) { + return evaluate_intermediate(a + b); + }); }; const size_t i0 = 0; model[node].populations[{AgeGroup(i0), InfectionState::Exposed}] = sum_vec(num_Exposed[node]); @@ -199,7 +206,7 @@ IOResult set_confirmed_cases_data(std::vector>& model, std::vect * @param[in] date Date at which the data is read. * @param[in] scaling_factor_inf Factors by which to scale the confirmed cases of rki data. */ -template +template IOResult set_confirmed_cases_data(std::vector>& model, const std::string& path, std::vector const& region, Date date, const std::vector& scaling_factor_inf) @@ -218,7 +225,7 @@ IOResult set_confirmed_cases_data(std::vector>& model, const std * @param[in] date Date for which the arrays are initialized. * @param[in] scaling_factor_icu factor by which to scale the icu cases of divi data. */ -template +template IOResult set_divi_data(std::vector>& model, const std::string& path, const std::vector& vregion, Date date, double scaling_factor_icu) { @@ -261,9 +268,8 @@ IOResult set_divi_data(std::vector>& model, const std::string& p * @param[in] num_population Vector of population data. The size should be the same as vregion and model. * @param[in] vregion Vector of keys of the regions of interest. */ -template -IOResult set_population_data(std::vector>& model, - const std::vector>& num_population, +template +IOResult set_population_data(std::vector>& model, const std::vector>& num_population, const std::vector& vregion) { assert(num_population.size() == vregion.size()); @@ -280,7 +286,10 @@ IOResult set_population_data(std::vector>& model, } } else if (model_groups == 1 && data_groups >= 1) { - const double total = std::accumulate(num_population[region].begin(), num_population[region].end(), 0.0); + const FP total = std::accumulate(num_population[region].begin(), num_population[region].end(), FP(0.0), + [](const FP& a, const FP& b) { + return evaluate_intermediate(a + b); + }); model[region].populations.template set_difference_from_group_total( {AgeGroup(0), InfectionState::Susceptible}, total); } @@ -295,7 +304,7 @@ IOResult set_population_data(std::vector>& model, * @param[in] path Path to RKI file containing population data. * @param[in] vregion Vector of keys of the regions of interest. */ -template +template IOResult set_population_data(std::vector>& model, const std::string& path, const std::vector& vregion) { diff --git a/cpp/models/ode_secirts/analyze_result.cpp b/cpp/models/ode_secirts/analyze_result.cpp index 0b50d07784..29c216e80c 100644 --- a/cpp/models/ode_secirts/analyze_result.cpp +++ b/cpp/models/ode_secirts/analyze_result.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker diff --git a/cpp/models/ode_secirts/analyze_result.h b/cpp/models/ode_secirts/analyze_result.h index 180a26e7f7..857658ffa3 100644 --- a/cpp/models/ode_secirts/analyze_result.h +++ b/cpp/models/ode_secirts/analyze_result.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -37,8 +37,8 @@ namespace osecirts * @param p percentile value in open interval (0, 1) * @return p percentile of the parameters over all runs */ -template -std::vector ensemble_params_percentile(const std::vector>& ensemble_params, double p) +template +std::vector ensemble_params_percentile(const std::vector>& ensemble_params, FP p) { assert(p > 0.0 && p < 1.0 && "Invalid percentile value."); @@ -46,15 +46,15 @@ std::vector ensemble_params_percentile(const std::vector>() + .parameters.template get>() .template size(); - std::vector single_element_ensemble(num_runs); + std::vector single_element_ensemble(num_runs); // lambda function that calculates the percentile of a single parameter std::vector percentile(num_nodes, Model((int)num_groups)); auto param_percentil = [&ensemble_params, p, num_runs, &percentile](auto n, auto get_param) mutable { - std::vector single_element(num_runs); + std::vector single_element(num_runs); for (size_t run = 0; run < num_runs; run++) { auto const& params = ensemble_params[run][n]; single_element[run] = get_param(params); @@ -65,148 +65,125 @@ std::vector ensemble_params_percentile(const std::vector>().resize(num_days); - percentile[node].parameters.template get>().resize(num_days); - percentile[node].parameters.template get>().resize(num_days); + percentile[node].parameters.template get>().resize(num_days); + percentile[node].parameters.template get>().resize(num_days); + percentile[node].parameters.template get>().resize(num_days); for (auto i = AgeGroup(0); i < AgeGroup(num_groups); i++) { //Population for (auto compart = Index(0); compart < InfectionState::Count; ++compart) { - param_percentil( - node, [ compart, i ](auto&& model) -> auto& { - return model.populations[{i, compart}]; - }); + param_percentil(node, [compart, i](auto&& model) -> auto& { + return model.populations[{i, compart}]; + }); } // times - param_percentil( - node, [i](auto&& model) -> auto& { return model.parameters.template get>()[i]; }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); //probs - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); //vaccinations - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + + for (auto day = SimulationDay(0); day < num_days; ++day) { + param_percentil(node, [i, day](auto&& model) -> auto& { + return model.parameters.template get>()[{i, day}]; }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; + param_percentil(node, [i, day](auto&& model) -> auto& { + return model.parameters.template get>()[{i, day}]; }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; + param_percentil(node, [i, day](auto&& model) -> auto& { + return model.parameters.template get>()[{i, day}]; }); - - for (auto day = SimulationDay(0); day < num_days; ++day) { - param_percentil( - node, [ i, day ](auto&& model) -> auto& { - return model.parameters.template get>()[{i, day}]; - }); - param_percentil( - node, [ i, day ](auto&& model) -> auto& { - return model.parameters.template get>()[{i, day}]; - }); - param_percentil( - node, [ i, day ](auto&& model) -> auto& { - return model.parameters.template get>()[{i, day}]; - }); } //virus variants - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); } // group independent params - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { - return model.parameters.template get>(); - }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); for (size_t run = 0; run < num_runs; run++) { auto const& params = ensemble_params[run][node]; single_element_ensemble[run] = - params.parameters.template get>() * params.populations.get_total(); + params.parameters.template get>() * params.populations.get_total(); } std::sort(single_element_ensemble.begin(), single_element_ensemble.end()); - percentile[node].parameters.template set>( + percentile[node].parameters.template set>( single_element_ensemble[static_cast(num_runs * p)]); } return percentile; diff --git a/cpp/models/ode_secirts/infection_state.h b/cpp/models/ode_secirts/infection_state.h index 18249fd26c..fc83327f46 100644 --- a/cpp/models/ode_secirts/infection_state.h +++ b/cpp/models/ode_secirts/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn diff --git a/cpp/models/ode_secirts/model.cpp b/cpp/models/ode_secirts/model.cpp index 99b9258a69..21a0094a71 100644 --- a/cpp/models/ode_secirts/model.cpp +++ b/cpp/models/ode_secirts/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker diff --git a/cpp/models/ode_secirts/model.h b/cpp/models/ode_secirts/model.h index 3100a1bb19..2d66b9c165 100644 --- a/cpp/models/ode_secirts/model.h +++ b/cpp/models/ode_secirts/model.h @@ -1,4 +1,4 @@ -/* +/* * * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -29,6 +29,8 @@ #include "memilio/math/smoother.h" #include "memilio/math/eigen_util.h" +#include + namespace mio { namespace osecirts @@ -36,8 +38,8 @@ namespace osecirts // clang-format off using Flows = TypeList< //naive - Flow, - Flow, + Flow, + Flow, Flow, Flow, Flow, @@ -48,7 +50,7 @@ using Flows = TypeList< Flow, Flow, Flow, - Flow, + Flow, Flow, Flow, Flow, @@ -87,7 +89,7 @@ using Flows = TypeList< Flow, Flow, Flow, - + // waning Flow, Flow, @@ -95,7 +97,7 @@ using Flows = TypeList< Flow>; // clang-format on -template +template class Model : public FlowModel, Parameters, Flows> { @@ -118,13 +120,16 @@ class Model void get_flows(Eigen::Ref> pop, Eigen::Ref> y, FP t, Eigen::Ref> flows) const override { + using std::floor; + using std::min; + auto const& params = this->parameters; AgeGroup n_agegroups = params.get_num_groups(); - ContactMatrixGroup const& contact_matrix = params.template get>(); + ContactMatrixGroup const& contact_matrix = params.template get>(); - auto icu_occupancy = 0.0; - auto test_and_trace_required = 0.0; + FP icu_occupancy = 0.0; + FP test_and_trace_required = 0.0; for (auto i = AgeGroup(0); i < n_agegroups; ++i) { // naive flow to symptomatic in unit time test_and_trace_required += @@ -213,17 +218,17 @@ class Model //symptomatic are less well quarantined when testing and tracing is overwhelmed so they infect more people auto riskFromInfectedSymptomatic = - smoother_cosine(test_and_trace_required, params.template get>(), - params.template get>() * - params.template get>(), - params.template get>()[i], - params.template get>()[i]); + smoother_cosine(test_and_trace_required, params.template get>(), + params.template get>() * + params.template get>(), + params.template get>()[i], + params.template get>()[i]); auto riskFromInfectedNoSymptoms = - smoother_cosine(test_and_trace_required, params.template get>(), - params.template get>() * - params.template get>(), - params.template get>()[i], 1.0); + smoother_cosine(test_and_trace_required, params.template get>(), + params.template get>() * + params.template get>(), + params.template get>()[i], 1.0); for (auto j = AgeGroup(0); j < n_agegroups; j++) { size_t SNj = this->populations.get_flat_index({j, InfectionState::SusceptibleNaive}); @@ -263,18 +268,22 @@ class Model this->populations.get_flat_index({j, InfectionState::InfectedSymptomsImprovedImmunityConfirmed}); // effective contact rate by contact rate between groups i and j and damping j - FP season_val = (1 + params.template get>() * - sin(3.141592653589793 * - (std::fmod((params.template get() + t), 365.0) / 182.5 + 0.5))); - FP cont_freq_eff = season_val * contact_matrix.get_matrix_at(t)(static_cast((size_t)i), - static_cast((size_t)j)); + // std::fmod('time', 365.0) is non differentiable. Use std::floor instead to normalize 'time'. + FP normalized_time = (params.template get>() + t) - + 365.0 * floor((params.template get>() + t) / 365.0); + FP season_val = (1 + params.template get>() * + sin(std::numbers::pi_v * (normalized_time / 182.5 + 0.5))); + + FP cont_freq_eff = + season_val * contact_matrix.get_matrix_at(SimulationTime(t))( + static_cast((size_t)i), static_cast((size_t)j)); // without died people FP Nj = pop[SNj] + pop[ENj] + pop[INSNj] + pop[ISyNj] + pop[ISevNj] + pop[ICrNj] + pop[INSNCj] + pop[ISyNCj] + pop[SPIj] + pop[EPIj] + pop[INSPIj] + pop[ISyPIj] + pop[ISevPIj] + pop[ICrPIj] + pop[INSPICj] + pop[ISyPICj] + pop[SIIj] + pop[EIIj] + pop[INSIIj] + pop[ISyIIj] + pop[ISevIIj] + pop[ICrIIj] + pop[INSIICj] + pop[ISyIICj]; - const FP divNj = (Nj < Limits::zero_tolerance()) ? 0.0 : 1.0 / Nj; + const FP divNj = (Nj < Limits::zero_tolerance()) ? FP(0.0) : FP(1.0 / Nj); FP ext_inf_force_dummy = cont_freq_eff * divNj * params.template get>()[(AgeGroup)i] * @@ -298,31 +307,31 @@ class Model // vaccinations flows[this->template get_flat_flow_index({i})] = - std::min(y[SNi] - flows[this->template get_flat_flow_index({i})], - partial_vaccination[static_cast(i)]); + min(y[SNi] - flows[this->template get_flat_flow_index({i})], + partial_vaccination[static_cast(i)]); flows[this->template get_flat_flow_index({i})] = - std::min(y[SPIi] - - flows[this->template get_flat_flow_index({i})], - full_vaccination[static_cast(i)]); + min(y[SPIi] - + flows[this->template get_flat_flow_index({i})], + full_vaccination[static_cast(i)]); flows[this->template get_flat_flow_index({i})] = - std::min(y[SIIi] - - flows[this->template get_flat_flow_index({i})], - booster_vaccination[static_cast(i)]); + min(y[SIIi] - + flows[this->template get_flat_flow_index({i})], + booster_vaccination[static_cast(i)]); // ICU capacity shortage is close // TODO: if this is used with vaccination model, it has to be adapted if CriticalPerSevere // is different for different vaccination status. This is not the case here and in addition, ICUCapacity // is set to infinity and this functionality is deactivated, so this is OK for the moment. - FP criticalPerSevereAdjusted = smoother_cosine(icu_occupancy, 0.90 * params.template get>(), - params.template get>(), - params.template get>()[i], 0); + FP criticalPerSevereAdjusted = smoother_cosine( + icu_occupancy, 0.90 * params.template get>(), params.template get>(), + params.template get>()[i], 0); FP deathsPerSevereAdjusted = params.template get>()[i] - criticalPerSevereAdjusted; @@ -583,7 +592,7 @@ class Model /** * @brief Calculates smoothed vaccinations for a given time point. * - * This function calculates the number of vaccinations for each age group at a given time t, + * This function calculates the number of vaccinations for each age group at a given time t, * based on daily vaccinations data. The smoothing is done using a cosine function. * * @param t The time in the simulation. @@ -591,50 +600,52 @@ class Model * @param eps [Default: 0.15] The smoothing factor used in the cosine smoothing function. * @return A vector containing the number of vaccinations for each age group at time t. */ - Eigen::VectorXd vaccinations_at(const FP t, - const CustomIndexArray& daily_vaccinations, - const FP eps = 0.15) const + Eigen::VectorX vaccinations_at(const FP t, + const CustomIndexArray& daily_vaccinations, + const FP eps = 0.15) const { + using std::floor; + auto const& params = this->parameters; - const FP ub = (size_t)t + 1.0; + const FP ub = floor(t) + 1.0; const FP lb = ub - eps; - const auto max_time = static_cast(daily_vaccinations.size()) - 1; + const auto max_time = static_cast(daily_vaccinations.template size()) - 1; - Eigen::VectorXd smoothed_vaccinations((size_t)params.get_num_groups()); + Eigen::VectorX smoothed_vaccinations((size_t)params.get_num_groups()); smoothed_vaccinations.setZero(); // if daily_vaccinations is not available for the current time point, we return zero vaccinations. - if (max_time <= (size_t)t) { + if (max_time <= floor(t)) { mio::log_warning("Vaccination data not available for time point ", t, ". Returning zero vaccinations."); return smoothed_vaccinations; } if (t >= lb) { for (AgeGroup age = AgeGroup(0); age < params.get_num_groups(); age++) { // if ub + 1 is out of bounds, we use the value at ub - auto ubp1 = static_cast(ub + 1); + FP ubp1 = floor(ub + 1.0); if (max_time < ubp1) { - ubp1 = static_cast(ub); + ubp1 = floor(ub); } - const auto num_vaccinations_ub = daily_vaccinations[{age, SimulationDay(static_cast(ubp1))}] - - daily_vaccinations[{age, SimulationDay(static_cast(ub))}]; - const auto num_vaccinations_lb = daily_vaccinations[{age, SimulationDay(static_cast(lb + 1))}] - - daily_vaccinations[{age, SimulationDay(static_cast(lb))}]; + const auto num_vaccinations_ub = daily_vaccinations[{age, SimulationDay(size_t(floor(ubp1)))}] - + daily_vaccinations[{age, SimulationDay(size_t(floor(ub)))}]; + const auto num_vaccinations_lb = daily_vaccinations[{age, SimulationDay(size_t(floor(lb + 1.0)))}] - + daily_vaccinations[{age, SimulationDay(size_t(floor(lb)))}]; smoothed_vaccinations[(size_t)age] = - smoother_cosine(t, lb, ub, num_vaccinations_lb, num_vaccinations_ub); + smoother_cosine(t, lb, ub, num_vaccinations_lb, num_vaccinations_ub); } } else { for (auto age = AgeGroup(0); age < params.get_num_groups(); age++) { - smoothed_vaccinations[(size_t)age] = daily_vaccinations[{age, SimulationDay((size_t)t + 1)}] - - daily_vaccinations[{age, SimulationDay((size_t)t)}]; + smoothed_vaccinations[(size_t)age] = daily_vaccinations[{age, SimulationDay(size_t(floor(t + 1)))}] - + daily_vaccinations[{age, SimulationDay(size_t(floor(t)))}]; } } return smoothed_vaccinations; } /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -665,7 +676,7 @@ class Model }; // namespace osecirts //forward declaration, see below. -template >> +template >> class Simulation; /** @@ -675,7 +686,7 @@ class Simulation; * @param y current value of compartments. * @tparam Base simulation type that uses the SECIRS-type compartment model. see Simulation. */ -template >> +template >> FP get_infections_relative(const Simulation& model, FP t, const Eigen::Ref>& y); /** @@ -701,25 +712,27 @@ class Simulation : public BaseT /** * @brief Applies the effect of a new variant of a disease to the transmission probability of the model. - * + * * This function adjusts the transmission probability of the disease for each age group based on the share of the new variant. * The share of the new variant is calculated based on the time `t` and the start day of the new variant. * The transmission probability is then updated for each age group in the model. - * + * * Based on Equation (35) and (36) in doi.org/10.1371/journal.pcbi.1010054 - * + * * @param [in] t The current time. * @param [in] base_infectiousness The base infectiousness of the old variant for each age group. */ void apply_variant(const FP t, const CustomIndexArray, AgeGroup> base_infectiousness) { - auto start_day = this->get_model().parameters.template get(); - auto start_day_new_variant = this->get_model().parameters.template get(); + using std::min; + + auto start_day = this->get_model().parameters.template get>(); + auto start_day_new_variant = this->get_model().parameters.template get>(); if (start_day + t >= start_day_new_variant - 1e-10) { const FP days_variant = t - (start_day_new_variant - start_day); - const FP share_new_variant = std::min(1.0, 0.01 * pow(2, (1. / 7) * days_variant)); + const FP share_new_variant = min(1.0, 0.01 * pow(2, (1. / 7) * days_variant)); const auto num_groups = this->get_model().parameters.get_num_groups(); for (auto i = AgeGroup(0); i < num_groups; ++i) { FP new_transmission = (1 - share_new_variant) * base_infectiousness[i] + @@ -737,8 +750,11 @@ class Simulation : public BaseT * @param tmax next stopping point of simulation * @return value at tmax */ - Eigen::Ref advance(FP tmax) + Eigen::Ref> advance(FP tmax) { + using std::floor; + using std::min; + auto& t_end_dyn_npis = this->get_model().parameters.get_end_dynamic_npis(); auto& dyn_npis = this->get_model().parameters.template get>(); auto& contact_patterns = this->get_model().parameters.template get>(); @@ -753,13 +769,13 @@ class Simulation : public BaseT const auto dt = dyn_npis.get_interval().get(); while (t < tmax) { - auto dt_eff = std::min({dt, tmax - t, m_t_last_npi_check + dt - t}); + auto dt_eff = min({dt, tmax - t, m_t_last_npi_check + dt - t}); if (dt_eff >= 1.0) { dt_eff = 1.0; } BaseT::advance(t + dt_eff); - if (t + 0.5 + dt_eff - std::floor(t + 0.5) >= 1) { + if (t + 0.5 + dt_eff - floor(t + 0.5) >= 1) { this->apply_variant(t, base_infectiousness); } @@ -774,7 +790,7 @@ class Simulation : public BaseT t = t + dt_eff; if (dyn_npis.get_thresholds().size() > 0) { - if (floating_point_greater_equal(t, m_t_last_npi_check + dt)) { + if (floating_point_greater_equal(t, m_t_last_npi_check + dt)) { if (t < t_end_dyn_npis) { auto inf_rel = get_infections_relative(*this, t, this->get_result().get_last_value()) * dyn_npis.get_base_value(); @@ -783,8 +799,8 @@ class Simulation : public BaseT (exceeded_threshold->first > m_dynamic_npi.first || t > FP(m_dynamic_npi.second))) { //old npi was weaker or is expired - auto t_start = SimulationTime(t + delay_npi_implementation); - auto t_end = t_start + SimulationTime(dyn_npis.get_duration()); + auto t_start = SimulationTime(t + delay_npi_implementation); + auto t_end = t_start + SimulationTime(dyn_npis.get_duration()); this->get_model().parameters.get_start_commuter_detection() = (FP)t_start; this->get_model().parameters.get_end_commuter_detection() = (FP)t_end; m_dynamic_npi = std::make_pair(exceeded_threshold->first, t_end); @@ -810,12 +826,12 @@ class Simulation : public BaseT private: FP m_t_last_npi_check; - std::pair m_dynamic_npi = {-std::numeric_limits::max(), SimulationTime(0)}; + std::pair> m_dynamic_npi = {-std::numeric_limits::max(), SimulationTime(0)}; }; /** * @brief Specialization of simulate for SECIRS-type models using Simulation. - * + * * @param[in] t0 start time. * @param[in] tmax end time. * @param[in] dt time step. @@ -824,7 +840,7 @@ class Simulation : public BaseT * * @return Returns the result of the simulation. */ -template +template inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, std::unique_ptr>&& integrator_core = nullptr) { @@ -833,7 +849,7 @@ inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, /** * @brief Specialization of simulate for SECIRS-type models using the FlowSimulation. - * + * * @param[in] t0 start time. * @param[in] tmax end time. * @param[in] dt time step. @@ -842,12 +858,12 @@ inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, * * @return Returns the result of the Flowsimulation. */ -template +template inline auto simulate_flows(FP t0, FP tmax, FP dt, const Model& model, std::unique_ptr>&& integrator_core = nullptr) { - return mio::simulate_flows, Simulation>>>(t0, tmax, dt, model, - std::move(integrator_core)); + return mio::simulate_flows, Simulation>>>( + t0, tmax, dt, model, std::move(integrator_core)); } //see declaration above. @@ -865,7 +881,7 @@ FP get_infections_relative(const Simulation& sim, FP /*t*/, const Eige sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed}); } - auto inf_rel = sum_inf / sim.get_model().populations.get_total(); + FP inf_rel = sum_inf / sim.get_model().populations.get_total(); return inf_rel; } @@ -880,14 +896,14 @@ FP get_infections_relative(const Simulation& sim, FP /*t*/, const Eige * @return vector expression, same size as y, with migration factors per compartment. * @tparam Base simulation type that uses a SECIRS-type compartment model. see Simulation. */ -template , FP>> +template , FP>> auto get_migration_factors(const Simulation& sim, FP /*t*/, const Eigen::Ref>& y) { auto& params = sim.get_model().parameters; //parameters as arrays - auto& p_asymp = params.template get>().array().template cast(); - auto& p_inf = params.template get>().array().template cast(); - auto& p_inf_max = params.template get>().array().template cast(); + auto& p_asymp = params.template get>().array().template cast(); + auto& p_inf = params.template get>().array().template cast(); + auto& p_inf_max = params.template get>().array().template cast(); //slice of InfectedNoSymptoms auto y_INS = slice(y, {Eigen::Index(InfectionState::InfectedNoSymptomsNaive), Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) + @@ -898,15 +914,14 @@ auto get_migration_factors(const Simulation& sim, FP /*t*/, const Eigen::R //compute isolation, same as infection risk from main model auto test_and_trace_required = - ((1 - p_asymp) / params.template get>().array().template cast() * - y_INS.array()) + ((1 - p_asymp) / params.template get>().array().template cast() * y_INS.array()) .sum(); auto riskFromInfectedSymptomatic = - smoother_cosine(test_and_trace_required, double(params.template get>()), - params.template get>() * 5, p_inf.matrix(), p_inf_max.matrix()); + smoother_cosine(test_and_trace_required, FP(params.template get>()), + params.template get>() * 5, p_inf.matrix(), p_inf_max.matrix()); //set factor for infected - auto factors = Eigen::VectorXd::Ones(y.rows()).eval(); + auto factors = Eigen::VectorX::Ones(y.rows()).eval(); slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsNaive), Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) .array() = riskFromInfectedSymptomatic; @@ -921,21 +936,21 @@ auto get_migration_factors(const Simulation& sim, FP /*t*/, const Eigen::R /** * @brief Adjusts the state of commuters in a model, accounting for detection and mobility effects. - * + * * @tparam FP Floating-point type, e.g., double. * @tparam Base Simulation type that uses the SECIRTS-type model. see Simulation. * @param[in,out] sim Simulation object containing the model and result data. * @param[in,out] migrated Vector representing the number of commuters in each state. * @param[in] time Current simulation time, used to determine the commuter detection period. */ -template , FP>> +template , FP>> auto test_commuters(Simulation& sim, Eigen::Ref> migrated, FP time) { - auto& model = sim.get_model(); - auto nondetection = 1.0; + auto& model = sim.get_model(); + FP nondetection = 1.0; if (time >= model.parameters.get_start_commuter_detection() && time < model.parameters.get_end_commuter_detection()) { - nondetection = (double)model.parameters.get_commuter_nondetection(); + nondetection = (FP)model.parameters.get_commuter_nondetection(); } for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); ++i) { auto ISyNi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsNaive}); diff --git a/cpp/models/ode_secirts/parameter_space.cpp b/cpp/models/ode_secirts/parameter_space.cpp index 607295d362..c4f59ae898 100644 --- a/cpp/models/ode_secirts/parameter_space.cpp +++ b/cpp/models/ode_secirts/parameter_space.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn diff --git a/cpp/models/ode_secirts/parameter_space.h b/cpp/models/ode_secirts/parameter_space.h index 0d73a09ea8..6ebf7adf64 100644 --- a/cpp/models/ode_secirts/parameter_space.h +++ b/cpp/models/ode_secirts/parameter_space.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -24,6 +24,7 @@ #include "memilio/utils/memory.h" #include "memilio/utils/logging.h" #include "memilio/utils/parameter_distributions.h" +#include "memilio/math/math_utils.h" #include "ode_secirts/model.h" #include @@ -42,7 +43,7 @@ namespace osecirts * @tparam FP Floating point type, e.g., double. * @param[inout] model Model including contact patterns for all age groups */ -template +template void draw_sample_demographics(Model& model) { model.parameters.template get>().draw_sample(); @@ -78,14 +79,14 @@ void draw_sample_demographics(Model& model) // helper function to calculate the total population of a layer for a given age group auto calculate_layer_total = [&model](const std::vector& states, AgeGroup ageGroup) { - return std::accumulate(states.begin(), states.end(), 0.0, - [&model, &ageGroup](double sum, const InfectionState& state) { - return sum + model.populations[{ageGroup, state}]; + return std::accumulate(states.begin(), states.end(), FP(0.0), + [&model, &ageGroup](FP sum, const InfectionState& state) { + return evaluate_intermediate(sum + model.populations[{ageGroup, state}]); }); }; // helper function to adjust the susceptible population of a layer for a given age group - auto adjust_susceptible_population = [&model](AgeGroup i, double diff, InfectionState susceptibleState) { + auto adjust_susceptible_population = [&model](AgeGroup i, FP diff, InfectionState susceptibleState) { model.populations[{i, susceptibleState}] += diff; if (model.populations[{i, susceptibleState}] < 0) { mio::log_warning("Negative population in State " + std::to_string(static_cast(susceptibleState)) + @@ -96,9 +97,9 @@ void draw_sample_demographics(Model& model) for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); i++) { - const double group_naive_total = calculate_layer_total(naive_immunity_states, i); - const double group_partial_total = calculate_layer_total(partial_immunity_states, i); - const double group_improved_total = calculate_layer_total(improved_immunity_states, i); + const FP group_naive_total = calculate_layer_total(naive_immunity_states, i); + const FP group_partial_total = calculate_layer_total(partial_immunity_states, i); + const FP group_improved_total = calculate_layer_total(improved_immunity_states, i); //sample initial compartments (with exceptions) for (auto inf_state = Index(0); inf_state < InfectionState::Count; ++inf_state) { @@ -108,9 +109,9 @@ void draw_sample_demographics(Model& model) model.populations[{i, inf_state}].draw_sample(); } } - const double diff_naive = group_naive_total - calculate_layer_total(naive_immunity_states, i); - const double diff_partial = group_partial_total - calculate_layer_total(partial_immunity_states, i); - const double diff_improved = group_improved_total - calculate_layer_total(improved_immunity_states, i); + const FP diff_naive = group_naive_total - calculate_layer_total(naive_immunity_states, i); + const FP diff_partial = group_partial_total - calculate_layer_total(partial_immunity_states, i); + const FP diff_improved = group_improved_total - calculate_layer_total(improved_immunity_states, i); adjust_susceptible_population(i, diff_naive, InfectionState::SusceptibleNaive); adjust_susceptible_population(i, diff_partial, InfectionState::SusceptiblePartialImmunity); @@ -119,13 +120,13 @@ void draw_sample_demographics(Model& model) } /** - * Draws a sample from the specified distributions for all parameters + * Draws a sample from the specified distributions for all parameters * related to the infection. - * + * * @tparam FP Floating point type, e.g., double. * @param[inout] model Model including contact patterns for all age groups. */ -template +template void draw_sample_infection(Model& model) { model.parameters.template get>().draw_sample(); @@ -191,15 +192,15 @@ void draw_sample_infection(Model& model) /** * Draws a sample from model parameter distributions and stores sample values * as parameters values (cf. UncertainValue and Parameters classes). - * + * * @tparam FP Floating point type, e.g., double. * @param[inout] model Model including contact patterns for all age groups. */ -template +template void draw_sample(Model& model) { - draw_sample_infection(model); - draw_sample_demographics(model); + draw_sample_infection(model); + draw_sample_demographics(model); model.parameters.template get>().draw_sample(); model.apply_constraints(); } @@ -207,19 +208,19 @@ void draw_sample(Model& model) /** * Draws samples for each model node in a graph. * Some parameters are shared between nodes and are only sampled once. - * + * * @tparam FP Floating point type, e.g., double. * @param graph Graph to be sampled. * @return Graph with nodes and edges from the input graph sampled. */ -template +template Graph, MobilityParameters> draw_sample(Graph, MobilityParameters>& graph) { Graph, MobilityParameters> sampled_graph; //sample global parameters auto& shared_params_model = graph.nodes()[0].property; - draw_sample_infection(shared_params_model); + draw_sample_infection(shared_params_model); auto& shared_contacts = shared_params_model.parameters.template get>(); shared_contacts.draw_sample_dampings(); auto& shared_dynamic_npis = shared_params_model.parameters.template get>(); @@ -229,7 +230,7 @@ Graph, MobilityParameters> draw_sample(Graph, MobilityPa auto& node_model = params_node.property; //sample local parameters - draw_sample_demographics(params_node.property); + draw_sample_demographics(params_node.property); //copy global parameters //save demographic parameters so they aren't overwritten diff --git a/cpp/models/ode_secirts/parameters.h b/cpp/models/ode_secirts/parameters.h index 45580bff7d..878aef6431 100644 --- a/cpp/models/ode_secirts/parameters.h +++ b/cpp/models/ode_secirts/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -43,11 +43,12 @@ namespace osecirts * If the start day is 180 and simulation takes place from t0=0 to * tmax=100 the days 180 to 280 of the year are simulated. */ +template struct StartDay { - using Type = double; + using Type = FP; static Type get_default(AgeGroup) { - return 0.; + return Type(0.0); } static std::string name() { @@ -61,11 +62,12 @@ struct StartDay { * Starting on this day, the new variant will impact the transmission probability depending on the * infectiousness of the new variant in the parameter InfectiousnessNewVariant. */ +template struct StartDayNewVariant { - using Type = double; + using Type = FP; static Type get_default(AgeGroup) { - return std::numeric_limits::max(); + return std::numeric_limits::max(); } static std::string name() { @@ -79,7 +81,7 @@ struct StartDayNewVariant { * curve is below one in summer and above one in winter. * @tparam FP The floating-point type (default: double). */ -template +template struct Seasonality { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -96,7 +98,7 @@ struct Seasonality { * @brief Represents the icu capacity in the SECIRTS model. * @tparam FP The floating-point type (default: double). */ -template +template struct ICUCapacity { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -113,7 +115,7 @@ struct ICUCapacity { * @brief The Capacity to test and trace contacts of infected for quarantine per day. * @tparam FP The floating-point type (default: double). */ -template +template struct TestAndTraceCapacity { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -129,7 +131,7 @@ struct TestAndTraceCapacity { /** * @brief Multiplier for the test and trace capacity to determine when it is considered overloaded from cases without symptoms. */ -template +template struct TestAndTraceCapacityMaxRiskNoSymptoms { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -145,7 +147,7 @@ struct TestAndTraceCapacityMaxRiskNoSymptoms { /** * @brief Multiplier for the test and trace capacity to determine when it is considered overloaded by symptomatic cases. */ -template +template struct TestAndTraceCapacityMaxRiskSymptoms { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -162,7 +164,7 @@ struct TestAndTraceCapacityMaxRiskSymptoms { * @brief The contact patterns within the society are modelled using an UncertainContactMatrix. * @tparam FP The floating-point type (default: double). */ -template +template struct ContactPatterns { using Type = UncertainContactMatrix; static Type get_default(AgeGroup size) @@ -179,7 +181,7 @@ struct ContactPatterns { * @brief The NPIs that are enacted if certain infection thresholds are exceeded. * @tparam FP The floating-point type (default: double). */ -template +template struct DynamicNPIsInfectedSymptoms { using Type = DynamicNPIs; static Type get_default(AgeGroup /*size*/) @@ -196,7 +198,7 @@ struct DynamicNPIsInfectedSymptoms { * @brief Represents the mean latent time in days for different age groups. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeExposed { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -214,7 +216,7 @@ struct TimeExposed { * have not yet developed symptoms. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeInfectedNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -232,7 +234,7 @@ struct TimeInfectedNoSymptoms { * who do not need to be hospitalized in the SECIRTS model in day unit. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeInfectedSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -250,7 +252,7 @@ struct TimeInfectedSymptoms { * in day unit. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeInfectedSevere { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -268,7 +270,7 @@ struct TimeInfectedSevere { * in day unit. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeInfectedCritical { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -281,11 +283,11 @@ struct TimeInfectedCritical { } }; -/** +/** * @brief Time in days to describe waning immunity to get susceptible from partial to naive immunity layer. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeWaningPartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -298,11 +300,11 @@ struct TimeWaningPartialImmunity { } }; -/** +/** * @brief Time in days to describe waning immunity to get susceptible from improved to partial immunity layer. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeWaningImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -316,11 +318,11 @@ struct TimeWaningImprovedImmunity { }; /** - * @brief The time people stays immune after infection or vaccination located in naive immunity layer + * @brief The time people stays immune after infection or vaccination located in naive immunity layer * in day unit. * @tparam FP The floating-point type (default: double). */ -template +template struct TimeTemporaryImmunityPI { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -338,7 +340,7 @@ struct TimeTemporaryImmunityPI { * in day unit * @tparam FP The floating-point type (default: double). */ -template +template struct TimeTemporaryImmunityII { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -354,7 +356,7 @@ struct TimeTemporaryImmunityII { * @brief The probability of getting infected from a single contact. * @tparam FP The floating-point type (default: double). */ -template +template struct TransmissionProbabilityOnContact { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -369,9 +371,9 @@ struct TransmissionProbabilityOnContact { /** * @brief The relative infectability from individuals located in the InfectedNoSymptoms infection state. -* @tparam FP The floating-point type (default: double). +* @tparam FP The floating-point type (default: double). */ -template +template struct RelativeTransmissionNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -388,7 +390,7 @@ struct RelativeTransmissionNoSymptoms { * @brief The percentage of asymptomatic cases in the SECIRTS model. * @tparam FP The floating-point type (default: double). */ -template +template struct RecoveredPerInfectedNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -405,7 +407,7 @@ struct RecoveredPerInfectedNoSymptoms { * @brief The risk of infection from symptomatic cases in the SECIRTS model. * @tparam FP The floating-point type (default: double). */ -template +template struct RiskOfInfectionFromSymptomatic { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -422,7 +424,7 @@ struct RiskOfInfectionFromSymptomatic { * @brief Risk of infection from symptomatic cases increases if test and trace capacity is exceeded. * @tparam FP The floating-point type (default: double). */ -template +template struct MaxRiskOfInfectionFromSymptomatic { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -439,7 +441,7 @@ struct MaxRiskOfInfectionFromSymptomatic { * @brief The percentage of hospitalized patients per infected patients in the SECIRTS model. * @tparam FP The floating-point type (default: double). */ -template +template struct SeverePerInfectedSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -456,7 +458,7 @@ struct SeverePerInfectedSymptoms { * @brief The percentage of ICU patients per hospitalized patients in the SECIRTS model. * @tparam FP The floating-point type (default: double). */ -template +template struct CriticalPerSevere { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -473,7 +475,7 @@ struct CriticalPerSevere { * @brief The percentage of dead patients per ICU patients in the SECIRTS model. * @tparam FP The floating-point type (default: double). */ -template +template struct DeathsPerCritical { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -490,7 +492,7 @@ struct DeathsPerCritical { * @brief Time in days until first vaccine dose takes full effect. * @tparam FP The floating-point type (default: double). */ -template +template struct DaysUntilEffectivePartialVaccination { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -507,7 +509,7 @@ struct DaysUntilEffectivePartialVaccination { * @brief Time in days until second vaccine dose takes full effect. * @tparam FP The floating-point type (default: double). */ -template +template struct DaysUntilEffectiveImprovedVaccination { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -524,7 +526,7 @@ struct DaysUntilEffectiveImprovedVaccination { * @brief Time in days until booster vaccine dose takes full effect. * @tparam FP The floating-point type (default: double). */ -template +template struct DaysUntilEffectiveBoosterImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -541,7 +543,7 @@ struct DaysUntilEffectiveBoosterImmunity { * @brief Total number of first vaccinations up to the given day. * @tparam FP The floating-point type (default: double). */ -template +template struct DailyPartialVaccinations { using Type = CustomIndexArray; static Type get_default(AgeGroup size) @@ -558,7 +560,7 @@ struct DailyPartialVaccinations { * @brief Total number of full vaccinations up to the given day. * @tparam FP The floating-point type (default: double). */ -template +template struct DailyFullVaccinations { using Type = CustomIndexArray; static Type get_default(AgeGroup size) @@ -575,7 +577,7 @@ struct DailyFullVaccinations { * @brief Total number of booster vaccinations up to the given day. * @tparam FP The floating-point type (default: double). */ -template +template struct DailyBoosterVaccinations { using Type = CustomIndexArray; static Type get_default(AgeGroup size) @@ -592,7 +594,7 @@ struct DailyBoosterVaccinations { * @brief Factor to reduce infection risk for persons with partial immunity. * @tparam FP The floating-point type (default: double). */ -template +template struct ReducExposedPartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -609,7 +611,7 @@ struct ReducExposedPartialImmunity { * @brief Factor to reduce infection risk for persons with improved immunity. * @tparam FP The floating-point type (default: double). */ -template +template struct ReducExposedImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -626,7 +628,7 @@ struct ReducExposedImprovedImmunity { * @brief Factor to reduce risk of developing symptoms for persons with partial immunity. * @tparam FP The floating-point type (default: double). */ -template +template struct ReducInfectedSymptomsPartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -643,7 +645,7 @@ struct ReducInfectedSymptomsPartialImmunity { * @brief Factor to reduce risk of developing symptoms for persons with improved immunity. * @tparam FP The floating-point type (default: double). */ -template +template struct ReducInfectedSymptomsImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -661,7 +663,7 @@ struct ReducInfectedSymptomsImprovedImmunity { * Also applies to ICU and Death risk. * @tparam FP The floating-point type (default: double). */ -template +template struct ReducInfectedSevereCriticalDeadPartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -678,7 +680,7 @@ struct ReducInfectedSevereCriticalDeadPartialImmunity { * @brief Factor to reduce risk of hospitalization for persons with improved immunity. * @tparam FP The floating-point type (default: double). */ -template +template struct ReducInfectedSevereCriticalDeadImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -695,7 +697,7 @@ struct ReducInfectedSevereCriticalDeadImprovedImmunity { * @brief Factor to reduce infectious time of persons with partial or improved immunity. * @tparam FP The floating-point type (default: double). */ -template +template struct ReducTimeInfectedMild { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -712,7 +714,7 @@ struct ReducTimeInfectedMild { * @brief Represents the relative infectiousness of a new variant. * @tparam FP The floating-point type (default: double). */ -template +template struct InfectiousnessNewVariant { using Type = CustomIndexArray; static Type get_default(AgeGroup size) @@ -728,12 +730,12 @@ struct InfectiousnessNewVariant { /** * @brief The delay with which DynamicNPIs are implemented and enforced after exceedance of threshold. */ -template +template struct DynamicNPIsImplementationDelay { using Type = UncertainValue; static Type get_default(AgeGroup /*size*/) { - return 0.; + return Type(0.0); } static std::string name() { @@ -741,9 +743,9 @@ struct DynamicNPIsImplementationDelay { } }; -template +template using ParametersBase = ParameterSet< - StartDay, Seasonality, ICUCapacity, TestAndTraceCapacity, TestAndTraceCapacityMaxRiskNoSymptoms, + StartDay, Seasonality, ICUCapacity, TestAndTraceCapacity, TestAndTraceCapacityMaxRiskNoSymptoms, TestAndTraceCapacityMaxRiskSymptoms, ContactPatterns, DynamicNPIsInfectedSymptoms, TimeExposed, TimeInfectedNoSymptoms, TimeInfectedSymptoms, TimeInfectedSevere, TimeInfectedCritical, TimeWaningPartialImmunity, TimeWaningImprovedImmunity, TimeTemporaryImmunityPI, @@ -755,13 +757,14 @@ using ParametersBase = ParameterSet< DailyBoosterVaccinations, ReducExposedPartialImmunity, ReducExposedImprovedImmunity, ReducInfectedSymptomsPartialImmunity, ReducInfectedSymptomsImprovedImmunity, ReducInfectedSevereCriticalDeadPartialImmunity, ReducInfectedSevereCriticalDeadImprovedImmunity, - ReducTimeInfectedMild, InfectiousnessNewVariant, DynamicNPIsImplementationDelay, StartDayNewVariant>; + ReducTimeInfectedMild, InfectiousnessNewVariant, DynamicNPIsImplementationDelay, + StartDayNewVariant>; /** * @brief Parameters of the age-resolved SECIRS-type model with high temporary immunity upon immunization and waning immunity over time. */ -template +template class Parameters : public ParametersBase { public: @@ -779,11 +782,11 @@ class Parameters : public ParametersBase /** * Percentage of infected commuters that are not detected. */ - double& get_commuter_nondetection() + FP& get_commuter_nondetection() { return m_commuter_nondetection; } - double get_commuter_nondetection() const + FP get_commuter_nondetection() const { return m_commuter_nondetection; } @@ -791,12 +794,12 @@ class Parameters : public ParametersBase /** * Time in simulation before which no infected commuters are detected. */ - double& get_start_commuter_detection() + FP& get_start_commuter_detection() { return m_start_commuter_detection; } - double get_start_commuter_detection() const + FP get_start_commuter_detection() const { return m_start_commuter_detection; } @@ -804,12 +807,12 @@ class Parameters : public ParametersBase /** * Time in simulation after which no infected commuters are detected. */ - double& get_end_commuter_detection() + FP& get_end_commuter_detection() { return m_end_commuter_detection; } - double get_end_commuter_detection() const + FP get_end_commuter_detection() const { return m_end_commuter_detection; } @@ -817,18 +820,18 @@ class Parameters : public ParametersBase /** * Time in simulation after which no dynamic NPIs are applied. */ - double& get_end_dynamic_npis() + FP& get_end_dynamic_npis() { return m_end_dynamic_npis; } - double get_end_dynamic_npis() const + FP get_end_dynamic_npis() const { return m_end_dynamic_npis; } /** * @brief Checks whether all Parameters satisfy their corresponding constraints and applies them, if they do not. - * Time spans cannot be negative and probabilities can only take values between [0,1]. + * Time spans cannot be negative and probabilities can only take values between [0,1]. * * 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() @@ -837,7 +840,7 @@ class Parameters : public ParametersBase * (like 0 or 1 for probabilities or small positive values for time spans) values are set here and a manual adaptation * may often be necessary to have set meaningful values. * - * @return Returns true if one ore more constraint were corrected, false otherwise. + * @return Returns true if one ore more constraint were corrected, false otherwise. */ bool apply_constraints() { @@ -884,7 +887,7 @@ class Parameters : public ParametersBase corrected = true; } - const double tol_times = 1e-1; // accepted tolerance for compartment stays + const FP tol_times = 1e-1; // accepted tolerance for compartment stays for (auto i = AgeGroup(0); i < AgeGroup(m_num_groups); ++i) { @@ -1107,13 +1110,13 @@ class Parameters : public ParametersBase } /** - * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error + * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error * if constraints are not satisfied. - * @return Returns true if one constraint is not satisfied, otherwise false. + * @return Returns true if one constraint is not satisfied, otherwise false. */ bool check_constraints() const { - const double tol_times = 1e-1; // accepted tolerance for compartment stays + const FP tol_times = 1e-1; // accepted tolerance for compartment stays if (this->template get>() < 0.0 || this->template get>() > 0.5) { log_error("Constraint check: Parameter m_seasonality smaller {} or larger {}", 0, 0.5); return true; @@ -1344,10 +1347,10 @@ class Parameters : public ParametersBase private: AgeGroup m_num_groups; - double m_commuter_nondetection = 0.0; - double m_start_commuter_detection = 0.0; - double m_end_commuter_detection = 0.0; - double m_end_dynamic_npis = std::numeric_limits::max(); + FP m_commuter_nondetection = 0.0; + FP m_start_commuter_detection = 0.0; + FP m_end_commuter_detection = 0.0; + FP m_end_dynamic_npis = std::numeric_limits::max(); }; } // namespace osecirts diff --git a/cpp/models/ode_secirts/parameters_io.cpp b/cpp/models/ode_secirts/parameters_io.cpp index b5df44756d..0764859b02 100644 --- a/cpp/models/ode_secirts/parameters_io.cpp +++ b/cpp/models/ode_secirts/parameters_io.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn diff --git a/cpp/models/ode_secirts/parameters_io.h b/cpp/models/ode_secirts/parameters_io.h index 537a838f6d..c86263e717 100644 --- a/cpp/models/ode_secirts/parameters_io.h +++ b/cpp/models/ode_secirts/parameters_io.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -27,6 +27,7 @@ #include "ode_secirts/model.h" #include "ode_secirts/analyze_result.h" #include "memilio/math/eigen_util.h" +#include "memilio/math/math_utils.h" #include "memilio/mobility/graph.h" #include "memilio/mobility/metapopulation_mobility_instant.h" #include "memilio/io/epi_data.h" @@ -47,8 +48,8 @@ namespace details /** * @brief Computes the distribution of confirmed cases across infection states based on Case (RKI) data. * - * This function processes case data for given regions and distributes the cases across different - * infection states, considering the corresponding transition times and probabilities defined in the model. + * This function processes case data for given regions and distributes the cases across different + * infection states, considering the corresponding transition times and probabilities defined in the model. * * @tparam Model The type of the model used. * @tparam FP Floating point type (default: double). @@ -64,12 +65,12 @@ namespace details * @param[in] vregion Vector of region IDs representing the regions in the model vector. * @param[in] date Date for which the simulation starts. * @param[in] model Vector of models, each representing a region and containing the parameters. - * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases for + * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases for * @param[in] layer Specifies the immunity layer: 0 (Naive), 1 (Partial Immunity), 2 (Improved Immunity). * * @return An IOResult showing success or failure. */ -template +template IOResult compute_confirmed_cases_data( const std::vector& case_data, std::vector>& vnum_Exposed, std::vector>& vnum_InfectedNoSymptoms, std::vector>& vnum_InfectedSymptoms, @@ -249,7 +250,7 @@ IOResult compute_confirmed_cases_data( /** * @brief Reads confirmed case data from a file and computes the distribution of cases across infection states. * - * This function reads transformed RKI data from a specified file and processes the confirmed cases + * This function reads transformed RKI data from a specified file and processes the confirmed cases * to distribute them across different infection states and age groups. * * @tparam Model The type of the model used. @@ -271,7 +272,7 @@ IOResult compute_confirmed_cases_data( * * @return An IOResult indicating success or failure. */ -template +template IOResult read_confirmed_cases_data( std::string const& path, std::vector const& vregion, Date date, std::vector>& vnum_Exposed, std::vector>& vnum_InfectedNoSymptoms, std::vector>& vnum_InfectedSymptoms, @@ -288,7 +289,7 @@ IOResult read_confirmed_cases_data( /** * @brief Sets the confirmed cases data in the model considering different immunity layers. * - * This function distributes confirmed case data across infection states for regions and age groups + * This function distributes confirmed case data across infection states for regions and age groups * in the model. It considers different levels of immunity (naive, partial, and improved). * * @tparam Model The type of the model used. @@ -303,7 +304,7 @@ IOResult read_confirmed_cases_data( * * @return An IOResult indicating success or failure. */ -template +template IOResult set_confirmed_cases_data(std::vector& model, const std::vector& case_data, std::vector const& region, Date date, const std::vector& scaling_factor_inf, @@ -396,7 +397,10 @@ set_confirmed_cases_data(std::vector& model, const std::vector(a + b); + }) == 0) { log_warning( "No infections for unvaccinated reported on date {} for region {}. Population data has not been set.", date, region[county]); @@ -453,7 +457,11 @@ set_confirmed_cases_data(std::vector& model, const std::vector>()[(AgeGroup)i] * denom_E[i] * num_timm1[county][i]; } - if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) { + if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), FP(0.0), + [](const FP& a, const FP& b) { + return evaluate_intermediate(a + b); + }) == 0) { + log_warning("No infections for partially vaccinated reported on date {} for region {}. " "Population data has not been set.", date, region[county]); @@ -511,7 +519,10 @@ set_confirmed_cases_data(std::vector& model, const std::vector>()[(AgeGroup)i] * denom_E[i] * num_timm2[county][i]; } - if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) { + if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), FP(0.0), + [](const FP& a, const FP& b) { + return evaluate_intermediate(a + b); + }) == 0) { log_warning("No infections for vaccinated reported on date {} for region {}. " "Population data has not been set.", date, region[county]); @@ -523,8 +534,8 @@ set_confirmed_cases_data(std::vector& model, const std::vector& model, const std::vector +template IOResult set_confirmed_cases_data(std::vector& model, const std::string& path, std::vector const& region, Date date, const std::vector& scaling_factor_inf, @@ -554,8 +565,8 @@ IOResult set_confirmed_cases_data(std::vector& model, const std::st /** * @brief Sets ICU data from DIVI data into the a vector of models, distributed across age groups. * - * This function reads DIVI data from a file, computes the number of individuals in critical condition (ICU) - * for each region, and sets these values in the model. The ICU cases are distributed across age groups + * This function reads DIVI data from a file, computes the number of individuals in critical condition (ICU) + * for each region, and sets these values in the model. The ICU cases are distributed across age groups * using the transition probabilities from severe to critical. * * @tparam Model The type of the model used. @@ -569,7 +580,7 @@ IOResult set_confirmed_cases_data(std::vector& model, const std::st * * @return An IOResult indicating success or failure. */ -template +template IOResult set_divi_data(std::vector& model, const std::string& path, const std::vector& vregion, Date date, FP scaling_factor_icu) { @@ -610,22 +621,25 @@ IOResult set_divi_data(std::vector& model, const std::string& path, * * @tparam Model The type of the model used. * @tparam FP Floating point type (default: double). - * + * * @param[in,out] model A vector of models for which population data will be set. * @param[in] num_population A 2D vector where each row represents the age group population distribution for a specific region. * @param[in] vregion A vector of region identifiers corresponding to the population data. - * @param[in] immunity_population A 2D vector where each row represents the immunity distribution for a specific region + * @param[in] immunity_population A 2D vector where each row represents the immunity distribution for a specific region * across different levels of immunity (e.g., naive, partial, improved immunity). - * + * * @return An IOResult indicating success or failure. */ -template +template IOResult set_population_data(std::vector& model, const std::vector>& num_population, const std::vector& vregion, const std::vector> immunity_population) { for (size_t region = 0; region < vregion.size(); region++) { - if (std::accumulate(num_population[region].begin(), num_population[region].end(), 0.0) > 0) { + if (std::accumulate(num_population[region].begin(), num_population[region].end(), FP(0.0), + [](const FP& a, const FP& b) { + return evaluate_intermediate(a + b); + }) > 0.0) { auto num_groups = model[region].parameters.get_num_groups(); for (auto i = AgeGroup(0); i < num_groups; i++) { @@ -684,13 +698,13 @@ IOResult set_population_data(std::vector& model, const std::vector< * @brief Reads population data from a file and sets it for the each given model. * * @tparam Model The type of the model used. - * + * * @param[in,out] model A vector of models for which population data will be set. * @param[in] path The file path to the population data. * @param[in] vregion A vector of region identifiers corresponding to the population data. - * @param[in] immunity_population A 2D vector where each row represents the immunity distribution for a specific region + * @param[in] immunity_population A 2D vector where each row represents the immunity distribution for a specific region * across different levels of immunity (e.g., naive, partial, improved). - * + * * @return An IOResult indicating success or failure. */ template @@ -716,7 +730,7 @@ IOResult set_population_data(std::vector& model, const std::string& * * @return An IOResult indicating success or failure. */ -template +template IOResult set_vaccination_data(std::vector>& model, const std::vector& vacc_data, Date date, const std::vector& vregion, int num_days) { @@ -841,7 +855,7 @@ IOResult set_vaccination_data(std::vector>& model, const std::ve * * @return An IOResult indicating success or failure. */ -template +template IOResult set_vaccination_data(std::vector>& model, const std::string& path, Date date, const std::vector& vregion, int num_days) { diff --git a/cpp/models/ode_secirvvs/analyze_result.cpp b/cpp/models/ode_secirvvs/analyze_result.cpp index 74ac482a05..42b18823c3 100644 --- a/cpp/models/ode_secirvvs/analyze_result.cpp +++ b/cpp/models/ode_secirvvs/analyze_result.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele diff --git a/cpp/models/ode_secirvvs/analyze_result.h b/cpp/models/ode_secirvvs/analyze_result.h index fdc18b1a7a..9d603338b1 100644 --- a/cpp/models/ode_secirvvs/analyze_result.h +++ b/cpp/models/ode_secirvvs/analyze_result.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -67,132 +67,109 @@ std::vector ensemble_params_percentile(const std::vector(0); compart < InfectionState::Count; ++compart) { - param_percentil( - node, [ compart, i ](auto&& model) -> auto& { - return model.populations[{i, compart}]; - }); + param_percentil(node, [compart, i](auto&& model) -> auto& { + return model.populations[{i, compart}]; + }); } // times - param_percentil( - node, [i](auto&& model) -> auto& { return model.parameters.template get>()[i]; }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); //probs - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); //vaccinations - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); for (auto day = SimulationDay(0); day < num_days; ++day) { - param_percentil( - node, [ i, day ](auto&& model) -> auto& { - return model.parameters.template get>()[{i, day}]; - }); - param_percentil( - node, [ i, day ](auto&& model) -> auto& { - return model.parameters.template get>()[{i, day}]; - }); + param_percentil(node, [i, day](auto&& model) -> auto& { + return model.parameters.template get>()[{i, day}]; + }); + param_percentil(node, [i, day](auto&& model) -> auto& { + return model.parameters.template get>()[{i, day}]; + }); } //virus variants - param_percentil( - node, [i](auto&& model) -> auto& { - return model.parameters.template get>()[i]; - }); + param_percentil(node, [i](auto&& model) -> auto& { + return model.parameters.template get>()[i]; + }); } // group independent params - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { return model.parameters.template get>(); }); - param_percentil( - node, [](auto&& model) -> auto& { - return model.parameters.template get>(); - }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); + param_percentil(node, [](auto&& model) -> auto& { + return model.parameters.template get>(); + }); for (size_t run = 0; run < num_runs; run++) { auto const& params = ensemble_params[run][node]; diff --git a/cpp/models/ode_secirvvs/infection_state.h b/cpp/models/ode_secirvvs/infection_state.h index 76015a0f2d..58606c2838 100644 --- a/cpp/models/ode_secirvvs/infection_state.h +++ b/cpp/models/ode_secirvvs/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn diff --git a/cpp/models/ode_secirvvs/model.cpp b/cpp/models/ode_secirvvs/model.cpp index dea83118e9..85a1f2009e 100644 --- a/cpp/models/ode_secirvvs/model.cpp +++ b/cpp/models/ode_secirvvs/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn diff --git a/cpp/models/ode_secirvvs/model.h b/cpp/models/ode_secirvvs/model.h index b84f496120..5a30fe330f 100644 --- a/cpp/models/ode_secirvvs/model.h +++ b/cpp/models/ode_secirvvs/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -30,6 +30,8 @@ #include "memilio/math/smoother.h" #include "memilio/math/eigen_util.h" +#include + namespace mio { namespace osecirvvs @@ -37,7 +39,7 @@ namespace osecirvvs // clang-format off using Flows = TypeList< //naive - Flow, + Flow, Flow, Flow, Flow, @@ -48,7 +50,7 @@ using Flows = TypeList< Flow, Flow, Flow, - Flow, + Flow, Flow, Flow, Flow, @@ -86,7 +88,7 @@ using Flows = TypeList< Flow>; // clang-format on -template +template class Model : public FlowModel, Parameters, Flows> { @@ -112,10 +114,10 @@ class Model auto const& params = this->parameters; AgeGroup n_agegroups = params.get_num_groups(); - ContactMatrixGroup const& contact_matrix = params.template get>(); + ContactMatrixGroup const& contact_matrix = params.template get>(); - auto icu_occupancy = 0.0; - auto test_and_trace_required = 0.0; + FP icu_occupancy = 0.0; + FP test_and_trace_required = 0.0; for (auto i = AgeGroup(0); i < n_agegroups; ++i) { test_and_trace_required += (1 - params.template get>()[i]) / @@ -168,31 +170,31 @@ class Model size_t SIIi = this->populations.get_flat_index({i, InfectionState::SusceptibleImprovedImmunity}); - double reducExposedPartialImmunity = params.template get>()[i]; - double reducExposedImprovedImmunity = params.template get>()[i]; - double reducInfectedSymptomsPartialImmunity = + FP reducExposedPartialImmunity = params.template get>()[i]; + FP reducExposedImprovedImmunity = params.template get>()[i]; + FP reducInfectedSymptomsPartialImmunity = params.template get>()[i]; - double reducInfectedSymptomsImprovedImmunity = + FP reducInfectedSymptomsImprovedImmunity = params.template get>()[i]; - double reducInfectedSevereCriticalDeadPartialImmunity = + FP reducInfectedSevereCriticalDeadPartialImmunity = params.template get>()[i]; - double reducInfectedSevereCriticalDeadImprovedImmunity = + FP reducInfectedSevereCriticalDeadImprovedImmunity = params.template get>()[i]; - double reducTimeInfectedMild = params.template get>()[i]; + FP reducTimeInfectedMild = params.template get>()[i]; //symptomatic are less well quarantined when testing and tracing is overwhelmed so they infect more people auto riskFromInfectedSymptomatic = - smoother_cosine(test_and_trace_required, params.template get>(), - params.template get>() * - params.template get>(), - params.template get>()[i], - params.template get>()[i]); + smoother_cosine(test_and_trace_required, params.template get>(), + params.template get>() * + params.template get>(), + params.template get>()[i], + params.template get>()[i]); auto riskFromInfectedNoSymptoms = - smoother_cosine(test_and_trace_required, params.template get>(), - params.template get>() * - params.template get>(), - params.template get>()[i], 1.0); + smoother_cosine(test_and_trace_required, params.template get>(), + params.template get>() * + params.template get>(), + params.template get>()[i], 1.0); for (auto j = AgeGroup(0); j < n_agegroups; j++) { size_t SNj = this->populations.get_flat_index({j, InfectionState::SusceptibleNaive}); @@ -232,17 +234,18 @@ class Model this->populations.get_flat_index({j, InfectionState::InfectedSymptomsImprovedImmunityConfirmed}); // effective contact rate by contact rate between groups i and j and damping j - FP season_val = - (1 + params.template get>() * - sin(3.141592653589793 * ((params.template get() + t) / 182.5 + 0.5))); - FP cont_freq_eff = season_val * contact_matrix.get_matrix_at(t)(static_cast((size_t)i), - static_cast((size_t)j)); + FP season_val = (1 + params.template get>() * + sin(std::numbers::pi_v * + ((params.template get>() + t) / 182.5 + 0.5))); + FP cont_freq_eff = + season_val * contact_matrix.get_matrix_at(SimulationTime(t))( + static_cast((size_t)i), static_cast((size_t)j)); // without died people FP Nj = pop[SNj] + pop[ENj] + pop[INSNj] + pop[ISyNj] + pop[ISevNj] + pop[ICrNj] + pop[INSNCj] + pop[ISyNCj] + pop[SPIj] + pop[EPIj] + pop[INSPIj] + pop[ISyPIj] + pop[ISevPIj] + pop[ICrPIj] + pop[INSPICj] + pop[ISyPICj] + pop[SIIj] + pop[EIIj] + pop[INSIIj] + pop[ISyIIj] + pop[ISevIIj] + pop[ICrIIj] + pop[INSIICj] + pop[ISyIICj]; - const FP divNj = (Nj < Limits::zero_tolerance()) ? 0.0 : 1.0 / Nj; + const FP divNj = (Nj < Limits::zero_tolerance()) ? FP(0.0) : FP(1.0 / Nj); FP ext_inf_force_dummy = cont_freq_eff * divNj * params.template get>()[(AgeGroup)i] * @@ -267,7 +270,7 @@ class Model // TODO: if this is used with vaccination model, it has to be adapted if CriticalPerSevere // is different for different vaccination status. This is not the case here and in addition, ICUCapacity // is set to infinity and this functionality is deactivated, so this is OK for the moment. - double criticalPerSevereAdjusted = smoother_cosine( + FP criticalPerSevereAdjusted = smoother_cosine( icu_occupancy, 0.90 * params.template get>(), params.template get>(), params.template get>()[i], 0); @@ -512,7 +515,7 @@ class Model } /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -543,7 +546,7 @@ class Model }; //forward declaration, see below. -template >> +template >> class Simulation; /** @@ -553,7 +556,7 @@ class Simulation; * @param y current value of compartments. * @tparam Base simulation type that uses a secir compartment model. see Simulation. */ -template >> +template >> FP get_infections_relative(const Simulation& model, FP t, const Eigen::Ref>& y); /** @@ -579,38 +582,41 @@ class Simulation : public BaseT /** * @brief Applies the effect of a new variant of a disease to the transmission probability of the model. - * + * * This function adjusts the transmission probability of the disease for each age group based on the share of the new variant. * The share of the new variant is calculated based on the time `t` and the start day of the new variant. * The transmission probability is then updated for each age group in the model. - * + * * Based on Equation (35) and (36) in doi.org/10.1371/journal.pcbi.1010054 - * + * * @param [in] t The current time. * @param [in] base_infectiousness The base infectiousness of the old variant for each age group. */ - void apply_variant(const double t, const CustomIndexArray, AgeGroup> base_infectiousness) + void apply_variant(const FP t, const CustomIndexArray, AgeGroup> base_infectiousness) { - auto start_day = this->get_model().parameters.template get(); - auto start_day_new_variant = this->get_model().parameters.template get(); + using std::min; + using std::pow; + + auto start_day = this->get_model().parameters.template get>(); + auto start_day_new_variant = this->get_model().parameters.template get>(); if (start_day + t >= start_day_new_variant - 1e-10) { - const double days_variant = t - (start_day_new_variant - start_day); - const double share_new_variant = std::min(1.0, 0.01 * pow(2, (1. / 7) * days_variant)); - const auto num_groups = this->get_model().parameters.get_num_groups(); + const FP days_variant = t - (start_day_new_variant - start_day); + const FP share_new_variant = min(1.0, 0.01 * pow(2, (1. / 7) * days_variant)); + const auto num_groups = this->get_model().parameters.get_num_groups(); for (auto i = AgeGroup(0); i < num_groups; ++i) { - double new_transmission = - (1 - share_new_variant) * base_infectiousness[i] + - share_new_variant * base_infectiousness[i] * - this->get_model().parameters.template get>()[i]; + FP new_transmission = (1 - share_new_variant) * base_infectiousness[i] + + share_new_variant * base_infectiousness[i] * + this->get_model().parameters.template get>()[i]; this->get_model().parameters.template get>()[i] = new_transmission; } } } - void apply_vaccination(double t) + void apply_vaccination(FP t) { - auto t_idx = SimulationDay((size_t)t); + using std::floor; + auto t_idx = SimulationDay(size_t(floor(t))); auto& params = this->get_model().parameters; size_t num_groups = (size_t)params.get_num_groups(); auto last_value = this->get_result().get_last_value(); @@ -622,8 +628,8 @@ class Simulation : public BaseT for (size_t i = 0; i < num_groups; ++i) { - double first_vacc; - double full_vacc; + FP first_vacc; + FP full_vacc; if (t_idx == SimulationDay(0)) { first_vacc = params.template get>()[{(AgeGroup)i, t_idx}]; full_vacc = params.template get>()[{(AgeGroup)i, t_idx}]; @@ -637,7 +643,7 @@ class Simulation : public BaseT } if (last_value(count * i + S) - first_vacc < 0) { - auto corrected = 0.99 * last_value(count * i + S); + FP corrected = 0.99 * last_value(count * i + S); log_warning("too many first vaccinated at time {}: setting first_vacc from {} to {}", t, first_vacc, corrected); first_vacc = corrected; @@ -647,7 +653,7 @@ class Simulation : public BaseT last_value(count * i + SV) += first_vacc; if (last_value(count * i + SV) - full_vacc < 0) { - auto corrected = 0.99 * last_value(count * i + SV); + FP corrected = 0.99 * last_value(count * i + SV); log_warning("too many fully vaccinated at time {}: setting full_vacc from {} to {}", t, full_vacc, corrected); full_vacc = corrected; @@ -667,6 +673,9 @@ class Simulation : public BaseT */ Eigen::Ref> advance(FP tmax) { + using std::floor; + using std::min; + auto& t_end_dyn_npis = this->get_model().parameters.get_end_dynamic_npis(); auto& dyn_npis = this->get_model().parameters.template get>(); auto& contact_patterns = this->get_model().parameters.template get>(); @@ -676,12 +685,14 @@ class Simulation : public BaseT // the base value to use it in the apply_variant function and also to reset the parameter after the simulation. auto base_infectiousness = this->get_model().parameters.template get>(); - ScalarType delay_npi_implementation; - auto t = BaseT::get_result().get_last_time(); - const auto dt = dyn_npis.get_thresholds().size() > 0 ? dyn_npis.get_interval().get() : tmax; + FP delay_npi_implementation; + FP t = BaseT::get_result().get_last_time(); + const FP dt = dyn_npis.get_thresholds().size() > 0 ? dyn_npis.get_interval().get() : tmax; while (t < tmax) { - auto dt_eff = std::min({dt, tmax - t, m_t_last_npi_check + dt - t}); + FP dt_eff = min(dt, tmax - t); + dt_eff = min(dt_eff, m_t_last_npi_check + dt - t); + if (dt_eff >= 1.0) { dt_eff = 1.0; } @@ -691,7 +702,7 @@ class Simulation : public BaseT this->apply_variant(t, base_infectiousness); } BaseT::advance(t + dt_eff); - if (t + 0.5 + dt_eff - std::floor(t + 0.5) >= 1) { + if (t + 0.5 + dt_eff - floor(t + 0.5) >= 1) { this->apply_vaccination(t + 0.5 + dt_eff); this->apply_variant(t, base_infectiousness); } @@ -706,19 +717,19 @@ class Simulation : public BaseT t = t + dt_eff; if (dyn_npis.get_thresholds().size() > 0) { - if (floating_point_greater_equal(t, m_t_last_npi_check + dt)) { + if (floating_point_greater_equal(t, m_t_last_npi_check + dt)) { if (t < t_end_dyn_npis) { auto inf_rel = get_infections_relative(*this, t, this->get_result().get_last_value()) * dyn_npis.get_base_value(); auto exceeded_threshold = dyn_npis.get_max_exceeded_threshold(inf_rel); if (exceeded_threshold != dyn_npis.get_thresholds().end() && (exceeded_threshold->first > m_dynamic_npi.first || - t > ScalarType(m_dynamic_npi.second))) { //old npi was weaker or is expired + t > FP(m_dynamic_npi.second))) { //old npi was weaker or is expired - auto t_start = SimulationTime(t + delay_npi_implementation); - auto t_end = t_start + SimulationTime(dyn_npis.get_duration()); - this->get_model().parameters.get_start_commuter_detection() = (ScalarType)t_start; - this->get_model().parameters.get_end_commuter_detection() = (ScalarType)t_end; + auto t_start = SimulationTime(t + delay_npi_implementation); + auto t_end = t_start + SimulationTime(dyn_npis.get_duration()); + this->get_model().parameters.get_start_commuter_detection() = t_start.get(); + this->get_model().parameters.get_end_commuter_detection() = t_end.get(); m_dynamic_npi = std::make_pair(exceeded_threshold->first, t_end); implement_dynamic_npis(contact_patterns.get_cont_freq_mat(), exceeded_threshold->second, t_start, t_end, [](auto& g) { @@ -741,13 +752,13 @@ class Simulation : public BaseT } private: - double m_t_last_npi_check; - std::pair m_dynamic_npi = {-std::numeric_limits::max(), SimulationTime(0)}; + FP m_t_last_npi_check; + std::pair> m_dynamic_npi = {-std::numeric_limits::max(), SimulationTime(0)}; }; /** * @brief Specialization of simulate for SECIRVVS models using Simulation. - * + * * @tparam FP floating point type, e.g., double. * @param[in] t0 start time. * @param[in] tmax end time. @@ -757,7 +768,7 @@ class Simulation : public BaseT * * @return Returns the result of the simulation. */ -template +template inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, std::unique_ptr>&& integrator_core = nullptr) { @@ -766,7 +777,7 @@ inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, /** * @brief Specialization of simulate for SECIRVVS models using the FlowSimulation. - * + * * @tparam FP floating point type, e.g., double. * @param[in] t0 start time. * @param[in] tmax end time. @@ -776,12 +787,12 @@ inline auto simulate(FP t0, FP tmax, FP dt, const Model& model, * * @return Returns the result of the Flowsimulation. */ -template +template inline auto simulate_flows(FP t0, FP tmax, FP dt, const Model& model, std::unique_ptr>&& integrator_core = nullptr) { - return mio::simulate_flows, Simulation>>>(t0, tmax, dt, model, - std::move(integrator_core)); + return mio::simulate_flows, Simulation>>>( + t0, tmax, dt, model, std::move(integrator_core)); } //see declaration above. @@ -814,7 +825,7 @@ FP get_infections_relative(const Simulation& sim, FP /*t*/, const Eige * @return vector expression, same size as y, with mobility factors per compartment. * @tparam Base simulation type that uses a secir compartment model. see Simulation. */ -template , FP>> +template , FP>> auto get_mobility_factors(const Simulation& sim, FP /*t*/, const Eigen::Ref>& y) { @@ -833,17 +844,16 @@ auto get_mobility_factors(const Simulation& sim, FP /*t*/, const Eigen::Re //compute isolation, same as infection risk from main model auto test_and_trace_required = - ((1 - p_asymp) / params.template get>().array().template cast() * - y_INS.array()) + ((1 - p_asymp) / params.template get>().array().template cast() * y_INS.array()) .sum(); auto riskFromInfectedSymptomatic = - smoother_cosine(test_and_trace_required, double(params.template get>()), - params.template get>() * - params.template get>(), - p_inf.matrix(), p_inf_max.matrix()); + smoother_cosine(test_and_trace_required, FP(params.template get>()), + params.template get>() * + params.template get>(), + p_inf.matrix(), p_inf_max.matrix()); //set factor for infected - auto factors = Eigen::VectorXd::Ones(y.rows()).eval(); + auto factors = Eigen::VectorX::Ones(y.rows()).eval(); slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsNaive), Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) .array() = riskFromInfectedSymptomatic; @@ -856,14 +866,14 @@ auto get_mobility_factors(const Simulation& sim, FP /*t*/, const Eigen::Re return factors; } -template , FP>> +template , FP>> auto test_commuters(Simulation& sim, Eigen::Ref> mobile_population, FP time) { - auto& model = sim.get_model(); - auto nondetection = 1.0; + auto& model = sim.get_model(); + FP nondetection = 1.0; if (time >= model.parameters.get_start_commuter_detection() && time < model.parameters.get_end_commuter_detection()) { - nondetection = (double)model.parameters.get_commuter_nondetection(); + nondetection = (FP)model.parameters.get_commuter_nondetection(); } for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); ++i) { auto ISyNi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsNaive}); diff --git a/cpp/models/ode_secirvvs/parameter_space.cpp b/cpp/models/ode_secirvvs/parameter_space.cpp index ab36ac930b..9ce4a81ce8 100644 --- a/cpp/models/ode_secirvvs/parameter_space.cpp +++ b/cpp/models/ode_secirvvs/parameter_space.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn diff --git a/cpp/models/ode_secirvvs/parameter_space.h b/cpp/models/ode_secirvvs/parameter_space.h index 6e65bce23e..9bef0c68fa 100644 --- a/cpp/models/ode_secirvvs/parameter_space.h +++ b/cpp/models/ode_secirvvs/parameter_space.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -36,14 +36,16 @@ namespace osecirvvs * @tparam FP floating point type, e.g., double * @param[inout] model Model including contact patterns for alle age groups */ -template +template void draw_sample_demographics(Model& model) { + using std::abs; + model.parameters.template get>().draw_sample(); model.parameters.template get>().draw_sample(); for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); i++) { - double group_total = model.populations.get_group_total(i); + FP group_total = model.populations.get_group_total(i); //sample initial compartments (with exceptions) for (auto inf_state = Index(0); inf_state < InfectionState::Count; ++inf_state) { @@ -59,13 +61,13 @@ void draw_sample_demographics(Model& model) //if the new total without susceptibles is already bigger than the previous total //subtract the overflow from SusceptibleImprovedImmunity, susceptibles will then be approximately zero. model.populations[{i, InfectionState::SusceptibleNaive}] = 0; - double diff = model.populations.get_group_total(i) - group_total; + FP diff = model.populations.get_group_total(i) - group_total; if (diff > 0) { model.populations[{i, InfectionState::SusceptibleImprovedImmunity}] -= diff; if (model.populations[{i, InfectionState::SusceptibleImprovedImmunity}] < 0.0) { log_error("Negative Compartment after sampling."); } - assert(std::abs(group_total - model.populations.get_group_total(i)) < 1e-10 && "Sanity check."); + assert(abs(group_total - model.populations.get_group_total(i)) < 1e-10 && "Sanity check."); } model.populations.template set_difference_from_group_total({i, InfectionState::SusceptibleNaive}, group_total); @@ -77,7 +79,7 @@ void draw_sample_demographics(Model& model) * @tparam FP floating point type, e.g., double * @param[inout] model Model including contact patterns for alle age groups */ -template +template void draw_sample_infection(Model& model) { model.parameters.template get>().draw_sample(); @@ -143,7 +145,7 @@ void draw_sample_infection(Model& model) * @tparam FP floating point type, e.g., double * @param[inout] model Model including contact patterns for alle age groups */ -template +template void draw_sample(Model& model) { draw_sample_infection(model); @@ -160,7 +162,7 @@ void draw_sample(Model& model) * @param variant_high If true, use high value for infectiousness of variant. * @return Graph with nodes and edges from the input graph sampled. */ -template +template Graph, MobilityParameters> draw_sample(Graph, MobilityParameters>& graph, bool variant_high) { Graph, MobilityParameters> sampled_graph; @@ -175,7 +177,7 @@ Graph, MobilityParameters> draw_sample(Graph, MobilityPa auto& shared_dynamic_npis_delay = shared_params_model.parameters.template get>(); shared_dynamic_npis_delay.draw_sample(); - double delta_fac; + FP delta_fac; if (variant_high) { delta_fac = 1.6; } diff --git a/cpp/models/ode_secirvvs/parameters.h b/cpp/models/ode_secirvvs/parameters.h index 956f64cdcd..2034817803 100644 --- a/cpp/models/ode_secirvvs/parameters.h +++ b/cpp/models/ode_secirvvs/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -39,11 +39,12 @@ namespace osecirvvs * If the start day is 180 and simulation takes place from t0=0 to * tmax=100 the days 180 to 280 of the year are simulated */ +template struct StartDay { - using Type = double; + using Type = FP; static Type get_default(AgeGroup) { - return 0.; + return Type(0.0); } static std::string name() { @@ -57,11 +58,12 @@ struct StartDay { * Starting on this day, the new variant will impact the transmission probability depending on the * infectiousness of the new variant in the parameter InfectiousnessNewVariant. */ +template struct StartDayNewVariant { - using Type = double; + using Type = FP; static Type get_default(AgeGroup) { - return std::numeric_limits::max(); + return std::numeric_limits::max(); } static std::string name() { @@ -74,7 +76,7 @@ struct StartDayNewVariant { * the seasonality is given as (1+k*sin()) where the sine * curve is below one in summer and above one in winter */ -template +template struct Seasonality { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -90,7 +92,7 @@ struct Seasonality { /** * @brief the icu capacity in the SECIR model */ -template +template struct ICUCapacity { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -106,7 +108,7 @@ struct ICUCapacity { /** * @brief capacity to test and trace contacts of infected for quarantine per day. */ -template +template struct TestAndTraceCapacity { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -122,7 +124,7 @@ struct TestAndTraceCapacity { /** * @brief Multiplier for the test and trace capacity to determine when it is considered overloaded from cases without symptoms. */ -template +template struct TestAndTraceCapacityMaxRiskNoSymptoms { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -138,7 +140,7 @@ struct TestAndTraceCapacityMaxRiskNoSymptoms { /** * @brief Multiplier for the test and trace capacity to determine when it is considered overloaded by symptomatic cases. */ -template +template struct TestAndTraceCapacityMaxRiskSymptoms { using Type = UncertainValue; static Type get_default(AgeGroup) @@ -154,7 +156,7 @@ struct TestAndTraceCapacityMaxRiskSymptoms { /** * @brief the contact patterns within the society are modelled using an UncertainContactMatrix */ -template +template struct ContactPatterns { using Type = UncertainContactMatrix; static Type get_default(AgeGroup size) @@ -170,7 +172,7 @@ struct ContactPatterns { /** * @brief the NPIs that are enforced if certain infection thresholds are exceeded. */ -template +template struct DynamicNPIsInfectedSymptoms { using Type = DynamicNPIs; static Type get_default(AgeGroup /*size*/) @@ -186,12 +188,12 @@ struct DynamicNPIsInfectedSymptoms { /** * @brief The delay with which DynamicNPIs are implemented and enforced after exceedance of threshold. */ -template +template struct DynamicNPIsImplementationDelay { using Type = UncertainValue; static Type get_default(AgeGroup /*size*/) { - return 0.; + return Type(0.0); } static std::string name() { @@ -202,7 +204,7 @@ struct DynamicNPIsImplementationDelay { /** * @brief the (mean) latent time in day unit */ -template +template struct TimeExposed { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -219,7 +221,7 @@ struct TimeExposed { * @brief the (mean) time in day unit for asymptomatic cases that are infected but * have not yet developed symptoms. */ -template +template struct TimeInfectedNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -236,7 +238,7 @@ struct TimeInfectedNoSymptoms { * @brief the infectious time for symptomatic cases that are infected but * who do not need to be hospitalized in the SECIR model in day unit */ -template +template struct TimeInfectedSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -253,7 +255,7 @@ struct TimeInfectedSymptoms { * @brief the time people are 'simply' hospitalized before returning home in the SECIR model * in day unit */ -template +template struct TimeInfectedSevere { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -270,7 +272,7 @@ struct TimeInfectedSevere { * @brief the time people are treated by ICU before returning home in the SECIR model * in day unit */ -template +template struct TimeInfectedCritical { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -286,7 +288,7 @@ struct TimeInfectedCritical { /** * @brief probability of getting infected from a contact */ -template +template struct TransmissionProbabilityOnContact { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -302,7 +304,7 @@ struct TransmissionProbabilityOnContact { /** * @brief the relative InfectedNoSymptoms infectability */ -template +template struct RelativeTransmissionNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -318,7 +320,7 @@ struct RelativeTransmissionNoSymptoms { /** * @brief the percentage of asymptomatic cases in the SECIR model */ -template +template struct RecoveredPerInfectedNoSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -334,7 +336,7 @@ struct RecoveredPerInfectedNoSymptoms { /** * @brief the risk of infection from symptomatic cases in the SECIR model */ -template +template struct RiskOfInfectionFromSymptomatic { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -350,7 +352,7 @@ struct RiskOfInfectionFromSymptomatic { /** * @brief risk of infection from symptomatic cases increases as test and trace capacity is exceeded. */ -template +template struct MaxRiskOfInfectionFromSymptomatic { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -366,7 +368,7 @@ struct MaxRiskOfInfectionFromSymptomatic { /** * @brief the percentage of hospitalized patients per infected patients in the SECIR model */ -template +template struct SeverePerInfectedSymptoms { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -382,7 +384,7 @@ struct SeverePerInfectedSymptoms { /** * @brief the percentage of ICU patients per hospitalized patients in the SECIR model */ -template +template struct CriticalPerSevere { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -398,7 +400,7 @@ struct CriticalPerSevere { /** * @brief the percentage of dead patients per ICU patients in the SECIR model */ -template +template struct DeathsPerCritical { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -414,7 +416,7 @@ struct DeathsPerCritical { /** * @brief Time in days between first and second vaccine dose. */ -template +template struct VaccinationGap { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -430,7 +432,7 @@ struct VaccinationGap { /** * @brief Time in days until first vaccine dose takes full effect. */ -template +template struct DaysUntilEffectivePartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -446,7 +448,7 @@ struct DaysUntilEffectivePartialImmunity { /** * @brief Time in days until second vaccine dose takes full effect. */ -template +template struct DaysUntilEffectiveImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -462,7 +464,7 @@ struct DaysUntilEffectiveImprovedImmunity { /** * @brief Total number of first vaccinations up to the given day. */ -template +template struct DailyPartialVaccinations { using Type = CustomIndexArray; static Type get_default(AgeGroup size) @@ -478,7 +480,7 @@ struct DailyPartialVaccinations { /** * @brief Total number of full vaccinations up to the given day. */ -template +template struct DailyFullVaccinations { using Type = CustomIndexArray; static Type get_default(AgeGroup size) @@ -494,7 +496,7 @@ struct DailyFullVaccinations { /** * @brief Factor to reduce infection risk for persons with partial immunity. */ -template +template struct ReducExposedPartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -510,7 +512,7 @@ struct ReducExposedPartialImmunity { /** * @brief Factor to reduce infection risk for persons with improved immunity. */ -template +template struct ReducExposedImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -526,7 +528,7 @@ struct ReducExposedImprovedImmunity { /** * @brief Factor to reduce risk of developing symptoms for persons with partial immunity. */ -template +template struct ReducInfectedSymptomsPartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -542,7 +544,7 @@ struct ReducInfectedSymptomsPartialImmunity { /** * @brief Factor to reduce risk of developing symptoms for persons with improved immunity. */ -template +template struct ReducInfectedSymptomsImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -559,7 +561,7 @@ struct ReducInfectedSymptomsImprovedImmunity { * @brief Factor to reduce risk of hospitalization for persons with partial immunity. * Also applies to ICU and Death risk. */ -template +template struct ReducInfectedSevereCriticalDeadPartialImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -575,7 +577,7 @@ struct ReducInfectedSevereCriticalDeadPartialImmunity { /** * @brief Factor to reduce risk of hospitalization for persons with improved immunity. */ -template +template struct ReducInfectedSevereCriticalDeadImprovedImmunity { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -591,7 +593,7 @@ struct ReducInfectedSevereCriticalDeadImprovedImmunity { /** * @brief Factor to reduce infectious time of persons with partial or improved immunity. */ -template +template struct ReducTimeInfectedMild { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -607,7 +609,7 @@ struct ReducTimeInfectedMild { /** * @brief Represents the relative infectiousness of a new variant. */ -template +template struct InfectiousnessNewVariant { using Type = CustomIndexArray; static Type get_default(AgeGroup size) @@ -620,9 +622,9 @@ struct InfectiousnessNewVariant { } }; -template +template using ParametersBase = ParameterSet< - StartDay, Seasonality, ICUCapacity, TestAndTraceCapacity, TestAndTraceCapacityMaxRiskNoSymptoms, + StartDay, Seasonality, ICUCapacity, TestAndTraceCapacity, TestAndTraceCapacityMaxRiskNoSymptoms, TestAndTraceCapacityMaxRiskSymptoms, ContactPatterns, DynamicNPIsImplementationDelay, DynamicNPIsInfectedSymptoms, TimeExposed, TimeInfectedNoSymptoms, TimeInfectedSymptoms, TimeInfectedSevere, TimeInfectedCritical, TransmissionProbabilityOnContact, @@ -633,12 +635,12 @@ using ParametersBase = ParameterSet< ReducExposedImprovedImmunity, ReducInfectedSymptomsPartialImmunity, ReducInfectedSymptomsImprovedImmunity, ReducInfectedSevereCriticalDeadPartialImmunity, ReducInfectedSevereCriticalDeadImprovedImmunity, ReducTimeInfectedMild, InfectiousnessNewVariant, - StartDayNewVariant>; + StartDayNewVariant>; /** * @brief Parameters of an age-resolved SECIR/SECIHURD model with paths for partial and improved immunity through vaccination. */ -template +template class Parameters : public ParametersBase { public: @@ -656,11 +658,11 @@ class Parameters : public ParametersBase /** * Percentage of infected commuters that are not detected. */ - double& get_commuter_nondetection() + FP& get_commuter_nondetection() { return m_commuter_nondetection; } - double get_commuter_nondetection() const + FP get_commuter_nondetection() const { return m_commuter_nondetection; } @@ -668,12 +670,12 @@ class Parameters : public ParametersBase /** * Time in simulation before which no infected commuters are detected. */ - double& get_start_commuter_detection() + FP& get_start_commuter_detection() { return m_start_commuter_detection; } - double get_start_commuter_detection() const + FP get_start_commuter_detection() const { return m_start_commuter_detection; } @@ -681,12 +683,12 @@ class Parameters : public ParametersBase /** * Time in simulation after which no infected commuters are detected. */ - double& get_end_commuter_detection() + FP& get_end_commuter_detection() { return m_end_commuter_detection; } - double get_end_commuter_detection() const + FP get_end_commuter_detection() const { return m_end_commuter_detection; } @@ -694,18 +696,18 @@ class Parameters : public ParametersBase /** * Time in simulation after which no dynamic NPIs are applied. */ - double& get_end_dynamic_npis() + FP& get_end_dynamic_npis() { return m_end_dynamic_npis; } - double get_end_dynamic_npis() const + FP get_end_dynamic_npis() const { return m_end_dynamic_npis; } /** * @brief Checks whether all Parameters satisfy their corresponding constraints and applies them, if they do not. - * Time spans cannot be negative and probabilities can only take values between [0,1]. + * Time spans cannot be negative and probabilities can only take values between [0,1]. * * 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() @@ -714,7 +716,7 @@ class Parameters : public ParametersBase * (like 0 or 1 for probabilities or small positive values for time spans) values are set here and a manual adaptation * may often be necessary to have set meaningful values. * - * @return Returns true if one ore more constraint were corrected, false otherwise. + * @return Returns true if one ore more constraint were corrected, false otherwise. */ bool apply_constraints() { @@ -761,7 +763,7 @@ class Parameters : public ParametersBase corrected = true; } - const double tol_times = 1e-1; // accepted tolerance for compartment stays + const FP tol_times = 1e-1; // accepted tolerance for compartment stays for (auto i = AgeGroup(0); i < AgeGroup(m_num_groups); ++i) { if (this->template get>()[i] < tol_times) { @@ -945,13 +947,13 @@ class Parameters : public ParametersBase } /** - * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error + * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error * if constraints are not satisfied. - * @return Returns true if one constraint is not satisfied, otherwise false. + * @return Returns true if one constraint is not satisfied, otherwise false. */ bool check_constraints() const { - const double tol_times = 1e-1; // accepted tolerance for compartment stays + const FP tol_times = 1e-1; // accepted tolerance for compartment stays if (this->template get>() < 0.0 || this->template get>() > 0.5) { log_error("Constraint check: Parameter Seasonality smaller {} or larger {}", 0, 0.5); return true; @@ -1149,10 +1151,10 @@ class Parameters : public ParametersBase private: AgeGroup m_num_groups; - double m_commuter_nondetection = 0.0; - double m_start_commuter_detection = 0.0; - double m_end_commuter_detection = 0.0; - double m_end_dynamic_npis = std::numeric_limits::max(); + FP m_commuter_nondetection = 0.0; + FP m_start_commuter_detection = 0.0; + FP m_end_commuter_detection = 0.0; + FP m_end_dynamic_npis = std::numeric_limits::max(); }; } // namespace osecirvvs diff --git a/cpp/models/ode_secirvvs/parameters_io.cpp b/cpp/models/ode_secirvvs/parameters_io.cpp index 1a43ceddb7..b1f0dcddb2 100644 --- a/cpp/models/ode_secirvvs/parameters_io.cpp +++ b/cpp/models/ode_secirvvs/parameters_io.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn diff --git a/cpp/models/ode_secirvvs/parameters_io.h b/cpp/models/ode_secirvvs/parameters_io.h index 6c5e2c9a37..3cd0a412c0 100644 --- a/cpp/models/ode_secirvvs/parameters_io.h +++ b/cpp/models/ode_secirvvs/parameters_io.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn @@ -31,6 +31,7 @@ #include "memilio/io/result_io.h" #include "memilio/utils/date.h" #include "memilio/utils/logging.h" +#include "memilio/math/math_utils.h" namespace mio { @@ -166,7 +167,13 @@ IOResult set_confirmed_cases_data(std::vector& model, t_InfectedCritical, mu_C_R, mu_I_H, mu_H_U, scaling_factor_inf)); for (size_t county = 0; county < model.size(); county++) { - // if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) > 0) { + // if (std::accumulate( + // num_InfectedSymptoms[county].begin(), + // num_InfectedSymptoms[county].end(), + // double(0.0), + // [](const double& a, const double& b) { return evaluate_intermediate(a + b); } + // ) > 0) + // { size_t num_groups = (size_t)model[county].parameters.get_num_groups(); for (size_t i = 0; i < num_groups; i++) { model[county].populations[{AgeGroup(i), InfectionState::ExposedNaive}] = num_Exposed[county][i]; @@ -196,7 +203,10 @@ IOResult set_confirmed_cases_data(std::vector& model, } // } - if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) { + if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), double(0.0), + [](const double& a, const double& b) { + return evaluate_intermediate(a + b); + }) == 0.0) { log_warning( "No infections for unvaccinated reported on date {} for region {}. Population data has not been set.", date, region[county]); @@ -265,7 +275,13 @@ IOResult set_confirmed_cases_data(std::vector& model, t_InfectedCritical, mu_C_R, mu_I_H, mu_H_U, scaling_factor_inf)); for (size_t county = 0; county < model.size(); county++) { - // if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) > 0) { + // if (std::accumulate( + // num_InfectedSymptoms[county].begin(), + // num_InfectedSymptoms[county].end(), + // double(0.0), + // [](const double& a, const double& b) { return evaluate_intermediate(a + b); } + // ) > 0) + // { size_t num_groups = (size_t)model[county].parameters.get_num_groups(); for (size_t i = 0; i < num_groups; i++) { model[county].populations[{AgeGroup(i), InfectionState::ExposedPartialImmunity}] = num_Exposed[county][i]; @@ -284,7 +300,10 @@ IOResult set_confirmed_cases_data(std::vector& model, } } // } - if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) { + if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), double(0.0), + [](const double& a, const double& b) { + return evaluate_intermediate(a + b); + }) == 0.0) { log_warning("No infections for partially vaccinated reported on date {} for region {}. " "Population data has not been set.", date, region[county]); @@ -370,7 +389,11 @@ IOResult set_confirmed_cases_data(std::vector& model, num_icu[county][i]; } } - if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) { + + if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), double(0.0), + [](const double& a, const double& b) { + return evaluate_intermediate(a + b); + }) == 0.0) { log_warning("No infections for vaccinated reported on date {} for region {}. " "Population data has not been set.", date, region[county]); @@ -463,7 +486,11 @@ IOResult set_population_data(std::vector& model, const std::vector< BOOST_OUTCOME_TRY(read_confirmed_cases_data_fix_recovered(case_data, vregion, date, vnum_rec, 14.)); for (size_t region = 0; region < vregion.size(); region++) { - if (std::accumulate(num_population[region].begin(), num_population[region].end(), 0.0) > 0) { + + if (std::accumulate(num_population[region].begin(), num_population[region].end(), double(0.0), + [](const double& a, const double& b) { + return evaluate_intermediate(a + b); + }) > 0) { auto num_groups = model[region].parameters.get_num_groups(); for (auto i = AgeGroup(0); i < num_groups; i++) { @@ -656,7 +683,7 @@ IOResult set_population_data(std::vector& model, const std::string& /** * @brief Sets vaccination data for models stored in a vector. - * + * * @tparam FP Floating point type used in the Model objects. * @param[in, out] model Vector of Model objects in which the vaccination data is set. * @param[in] vacc_data Vector of VaccinationDataEntry objects containing the vaccination data. @@ -805,7 +832,7 @@ IOResult set_vaccination_data(std::vector>& model, const std::ve /** * @brief Reads vaccination data from a file and sets it for each model. - * + * * @tparam FP Floating point type used in the Model objects. * @param[in, out] model Vector of Model objects in which the vaccination data is set. * @param[in] path Path to vaccination data file. @@ -847,7 +874,7 @@ IOResult set_vaccination_data(std::vector>& model, const std::st #ifdef MEMILIO_HAS_HDF5 /** -* @brief Uses the initialisation method, which uses the reported data to set the initial conditions for the model for a given day. +* @brief Uses the initialisation method, which uses the reported data to set the initial conditions for the model for a given day. * The initialisation is applied for a predefined number of days and finally saved in a timeseries for each region. In the end, * we save the files "Results_rki.h5" and "Results_rki_sum.h5" in the results_dir. * Results_rki.h5 contains a time series for each region and Results_rki_sum.h5 contains the sum of all regions. @@ -936,7 +963,7 @@ IOResult export_input_data_county_timeseries(std::vector, const std /** * Reads compartments for German counties at a specified date from data files. - * Estimates all compartments from available data using the model parameters, so the + * Estimates all compartments from available data using the model parameters, so the * model parameters must be set before calling this function. * Uses data files that contain centered 7-day moving average. * @param[in, out] model Vector of SECIRVVS models, one per county. @@ -985,7 +1012,7 @@ IOResult read_input_data_county(std::vector& model, Date date, cons /** * Reads compartments for German counties at a specified date from data files. - * Estimates all compartments from available data using the model parameters, so the + * Estimates all compartments from available data using the model parameters, so the * model parameters must be set before calling this function. * Uses data files that contain centered 7-day moving average. * @param[in, out] model Vector of SECIRVVS models, one per county. diff --git a/cpp/models/ode_seir/infection_state.h b/cpp/models/ode_seir/infection_state.h index 0e4e692472..8244ddebca 100644 --- a/cpp/models/ode_seir/infection_state.h +++ b/cpp/models/ode_seir/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn diff --git a/cpp/models/ode_seir/model.cpp b/cpp/models/ode_seir/model.cpp index b89b6642f8..814b51b95f 100644 --- a/cpp/models/ode_seir/model.cpp +++ b/cpp/models/ode_seir/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn @@ -18,4 +18,3 @@ * limitations under the License. */ #include "ode_seir/model.h" - diff --git a/cpp/models/ode_seir/model.h b/cpp/models/ode_seir/model.h index d771bae94c..e4fd216a10 100644 --- a/cpp/models/ode_seir/model.h +++ b/cpp/models/ode_seir/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn @@ -48,7 +48,7 @@ using Flows = TypeList, Flow>; // clang-format on -template +template class Model : public FlowModel, Parameters, Flows> { @@ -85,11 +85,11 @@ class Model const size_t Ij = this->populations.get_flat_index({j, InfectionState::Infected}); const size_t Rj = this->populations.get_flat_index({j, InfectionState::Recovered}); - const ScalarType Nj = pop[Sj] + pop[Ej] + pop[Ij] + pop[Rj]; - const ScalarType divNj = (Nj < Limits::zero_tolerance()) ? 0.0 : 1.0 / Nj; - const ScalarType coeffStoE = - params.template get>().get_cont_freq_mat().get_matrix_at(t)(i.get(), j.get()) * - params.template get>()[i] * divNj; + const FP Nj = pop[Sj] + pop[Ej] + pop[Ij] + pop[Rj]; + const FP divNj = (Nj < Limits::zero_tolerance()) ? FP(0.0) : FP(1.0 / Nj); + const FP coeffStoE = params.template get>().get_cont_freq_mat().get_matrix_at( + SimulationTime(t))(i.get(), j.get()) * + params.template get>()[i] * divNj; flows[Base::template get_flat_flow_index(i)] += coeffStoE * y[Si] * pop[Ij]; @@ -107,7 +107,7 @@ class Model *@param y The TimeSeries obtained from the Model Simulation. *@returns The computed reproduction number at the provided index time. */ - IOResult get_reproduction_number(size_t t_idx, const mio::TimeSeries& y) + IOResult get_reproduction_number(size_t t_idx, const mio::TimeSeries& y) { if (!(t_idx < static_cast(y.get_num_time_points()))) { return mio::failure(mio::StatusCode::OutOfRange, "t_idx is not a valid index for the TimeSeries"); @@ -119,10 +119,10 @@ class Model constexpr size_t num_infected_compartments = 2; const size_t total_infected_compartments = num_infected_compartments * num_groups; - ContactMatrixGroup const& contact_matrix = params.template get>(); + ContactMatrixGroup const& contact_matrix = params.template get>(); - Eigen::MatrixXd F = Eigen::MatrixXd::Zero(total_infected_compartments, total_infected_compartments); - Eigen::MatrixXd V = Eigen::MatrixXd::Zero(total_infected_compartments, total_infected_compartments); + Eigen::MatrixX F = Eigen::MatrixX::Zero(total_infected_compartments, total_infected_compartments); + Eigen::MatrixX V = Eigen::MatrixX::Zero(total_infected_compartments, total_infected_compartments); for (auto i = AgeGroup(0); i < AgeGroup(num_groups); i++) { size_t Si = this->populations.get_flat_index({i, InfectionState::Susceptible}); @@ -131,36 +131,29 @@ class Model const ScalarType Nj = this->populations.get_group_total(j); const ScalarType divNj = (Nj < 1e-12) ? 0.0 : 1.0 / Nj; - ScalarType coeffStoE = contact_matrix.get_matrix_at(y.get_time(t_idx))(i.get(), j.get()) * - params.template get>()[i] * divNj; + FP coeffStoE = contact_matrix.get_matrix_at(SimulationTime(y.get_time(t_idx)))(i.get(), j.get()) * + params.template get>()[i] * divNj; F((size_t)i, (size_t)j + num_groups) = coeffStoE * y.get_value(t_idx)[Si]; } - ScalarType T_Ei = params.template get>()[i]; - ScalarType T_Ii = params.template get>()[i]; - V((size_t)i, (size_t)i) = 1.0 / T_Ei; - V((size_t)i + num_groups, (size_t)i) = -1.0 / T_Ei; + FP T_Ei = params.template get>()[i]; + FP T_Ii = params.template get>()[i]; + V((size_t)i, (size_t)i) = 1.0 / T_Ei; + V((size_t)i + num_groups, (size_t)i) = -1.0 / T_Ei; V((size_t)i + num_groups, (size_t)i + num_groups) = 1.0 / T_Ii; } V = V.inverse(); - Eigen::MatrixXd NextGenMatrix = Eigen::MatrixXd::Zero(total_infected_compartments, total_infected_compartments); - NextGenMatrix = F * V; - - //Compute the largest eigenvalue in absolute value - Eigen::ComplexEigenSolver ces; + Eigen::MatrixXd NextGenMatrix(total_infected_compartments, total_infected_compartments); + NextGenMatrix.noalias() = F * V; + // Compute the largest eigenvalue in absolute value + Eigen::ComplexEigenSolver> ces; ces.compute(NextGenMatrix); - const Eigen::VectorXcd eigen_vals = ces.eigenvalues(); - - Eigen::VectorXd eigen_vals_abs; - eigen_vals_abs.resize(eigen_vals.size()); + FP rho = ces.eigenvalues().cwiseAbs().maxCoeff(); - for (int i = 0; i < eigen_vals.size(); i++) { - eigen_vals_abs[i] = std::abs(eigen_vals[i]); - } - return mio::success(eigen_vals_abs.maxCoeff()); + return mio::success(rho); } /** @@ -168,10 +161,10 @@ class Model *@param y The TimeSeries obtained from the Model Simulation. *@returns vector containing all reproduction numbers */ - Eigen::VectorXd get_reproduction_numbers(const mio::TimeSeries& y) + Eigen::VectorX get_reproduction_numbers(const mio::TimeSeries& y) { auto num_time_points = y.get_num_time_points(); - Eigen::VectorXd temp(num_time_points); + Eigen::VectorX temp(num_time_points); for (size_t i = 0; i < static_cast(num_time_points); i++) { temp[i] = get_reproduction_number(i, y).value(); } @@ -184,7 +177,7 @@ class Model *@param y The TimeSeries obtained from the Model Simulation. *@returns The computed reproduction number at the provided time point, potentially using linear interpolation. */ - IOResult get_reproduction_number(ScalarType t_value, const mio::TimeSeries& y) + IOResult get_reproduction_number(FP t_value, const mio::TimeSeries& y) { if (t_value < y.get_time(0) || t_value > y.get_last_time()) { return mio::failure(mio::StatusCode::OutOfRange, @@ -195,19 +188,19 @@ class Model return mio::success(get_reproduction_number((size_t)0, y).value()); } - auto times = std::vector(y.get_times().begin(), y.get_times().end()); + auto times = std::vector(y.get_times().begin(), y.get_times().end()); auto time_late = std::distance(times.begin(), std::lower_bound(times.begin(), times.end(), t_value)); - ScalarType y1 = get_reproduction_number(static_cast(time_late - 1), y).value(); - ScalarType y2 = get_reproduction_number(static_cast(time_late), y).value(); + FP y1 = get_reproduction_number(static_cast(time_late - 1), y).value(); + FP y2 = get_reproduction_number(static_cast(time_late), y).value(); auto result = linear_interpolation(t_value, y.get_time(time_late - 1), y.get_time(time_late), y1, y2); - return mio::success(static_cast(result)); + return mio::success(static_cast(result)); } /** - * serialize this. + * serialize this. * @see mio::serialize */ template diff --git a/cpp/models/ode_seir/parameters.h b/cpp/models/ode_seir/parameters.h index 8aa78fc9dc..8a590b7d04 100644 --- a/cpp/models/ode_seir/parameters.h +++ b/cpp/models/ode_seir/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn @@ -39,7 +39,7 @@ namespace oseir /** * @brief probability of getting infected from a contact */ -template +template struct TransmissionProbabilityOnContact { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -55,7 +55,7 @@ struct TransmissionProbabilityOnContact { /** * @brief the latent time in day unit */ -template +template struct TimeExposed { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -71,7 +71,7 @@ struct TimeExposed { /** * @brief the infectious time in day unit */ -template +template struct TimeInfected { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -87,7 +87,7 @@ struct TimeInfected { /** * @brief the contact patterns within the society are modelled using a ContactMatrix */ -template +template struct ContactPatterns { using Type = UncertainContactMatrix; static Type get_default(AgeGroup size) @@ -100,14 +100,14 @@ struct ContactPatterns { } }; -template +template using ParametersBase = ParameterSet, TimeExposed, TimeInfected, ContactPatterns>; /** * @brief Parameters of an age-resolved SECIR/SECIHURD model. */ -template +template class Parameters : public ParametersBase { public: @@ -124,7 +124,7 @@ class Parameters : public ParametersBase /** * @brief Checks whether all Parameters satisfy their corresponding constraints and applies them, if they do not. - * Time spans cannot be negative and probabilities can only take values between [0,1]. + * Time spans cannot be negative and probabilities can only take values between [0,1]. * * 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() @@ -133,11 +133,11 @@ class Parameters : public ParametersBase * (like 0 or 1 for probabilities or small positive values for time spans) values are set here and a manual adaptation * may often be necessary to have set meaningful values. * - * @return Returns true if one ore more constraint were corrected, false otherwise. + * @return Returns true if one ore more constraint were corrected, false otherwise. */ bool apply_constraints() { - const double tol_times = 1e-1; + const FP tol_times = 1e-1; int corrected = false; @@ -173,13 +173,13 @@ class Parameters : public ParametersBase } /** - * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error + * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error * if constraints are not satisfied. - * @return Returns true if one constraint is not satisfied, otherwise false. + * @return Returns true if one constraint is not satisfied, otherwise false. */ bool check_constraints() const { - const double tol_times = 1e-1; + const FP tol_times = 1e-1; for (auto i = AgeGroup(0); i < m_num_groups; i++) { if (this->template get>()[i] < tol_times) { diff --git a/cpp/models/ode_sir/infection_state.h b/cpp/models/ode_sir/infection_state.h index 061a3655d6..c13515afc5 100644 --- a/cpp/models/ode_sir/infection_state.h +++ b/cpp/models/ode_sir/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn diff --git a/cpp/models/ode_sir/model.cpp b/cpp/models/ode_sir/model.cpp index a6046a47be..2d1f8799d6 100644 --- a/cpp/models/ode_sir/model.cpp +++ b/cpp/models/ode_sir/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn diff --git a/cpp/models/ode_sir/model.h b/cpp/models/ode_sir/model.h index 7766a91523..ab4bc473f7 100644 --- a/cpp/models/ode_sir/model.h +++ b/cpp/models/ode_sir/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn @@ -37,7 +37,7 @@ namespace osir * define the model * ********************/ -template +template class Model : public mio::CompartmentalModel, Parameters> { @@ -61,9 +61,9 @@ class Model void get_derivatives(Eigen::Ref> pop, Eigen::Ref> y, FP t, Eigen::Ref> dydt) const override { - auto params = this->parameters; - AgeGroup n_agegroups = params.get_num_groups(); - ContactMatrixGroup const& contact_matrix = params.template get>(); + auto params = this->parameters; + AgeGroup n_agegroups = params.get_num_groups(); + ContactMatrixGroup const& contact_matrix = params.template get>(); for (auto i = AgeGroup(0); i < n_agegroups; i++) { @@ -77,12 +77,12 @@ class Model size_t Ij = this->populations.get_flat_index({j, InfectionState::Infected}); size_t Rj = this->populations.get_flat_index({j, InfectionState::Recovered}); - const ScalarType Nj = pop[Sj] + pop[Ij] + pop[Rj]; - const ScalarType divNj = (Nj < Limits::zero_tolerance()) ? 0.0 : 1.0 / Nj; + const FP Nj = pop[Sj] + pop[Ij] + pop[Rj]; + const FP divNj = (Nj < Limits::zero_tolerance()) ? FP(0.0) : FP(1.0 / Nj); - ScalarType coeffStoI = contact_matrix.get_matrix_at(t)(static_cast((size_t)i), - static_cast((size_t)j)) * - params.template get>()[i] * divNj; + FP coeffStoI = contact_matrix.get_matrix_at(SimulationTime(t))( + static_cast((size_t)i), static_cast((size_t)j)) * + params.template get>()[i] * divNj; dydt[Si] += -coeffStoI * y[Si] * pop[Ij]; dydt[Ii] += coeffStoI * y[Si] * pop[Ij]; @@ -93,7 +93,7 @@ class Model } /** - * serialize this. + * serialize this. * @see mio::serialize */ template diff --git a/cpp/models/ode_sir/parameters.h b/cpp/models/ode_sir/parameters.h index 7358265bba..2d2700305d 100644 --- a/cpp/models/ode_sir/parameters.h +++ b/cpp/models/ode_sir/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert, Martin J. Kuehn @@ -39,7 +39,7 @@ namespace osir /** * @brief probability of getting infected from a contact */ -template +template struct TransmissionProbabilityOnContact { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -55,7 +55,7 @@ struct TransmissionProbabilityOnContact { /** * @brief the infectious time in day unit */ -template +template struct TimeInfected { using Type = CustomIndexArray, AgeGroup>; static Type get_default(AgeGroup size) @@ -71,7 +71,7 @@ struct TimeInfected { /** * @brief the contact patterns within the society are modelled using a ContactMatrix */ -template +template struct ContactPatterns { using Type = UncertainContactMatrix; static Type get_default(AgeGroup size) @@ -84,13 +84,13 @@ struct ContactPatterns { } }; -template +template using ParametersBase = ParameterSet, TimeInfected, ContactPatterns>; /** * @brief Parameters of SIR model. */ -template +template class Parameters : public ParametersBase { public: @@ -107,7 +107,7 @@ class Parameters : public ParametersBase /** * @brief Checks whether all Parameters satisfy their corresponding constraints and applies them, if they do not. - * Time spans cannot be negative and probabilities can only take values between [0,1]. + * Time spans cannot be negative and probabilities can only take values between [0,1]. * * 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() @@ -116,11 +116,11 @@ class Parameters : public ParametersBase * (like 0 or 1 for probabilities or small positive values for time spans) values are set here and a manual adaptation * may often be necessary to have set meaningful values. * - * @return Returns true if one ore more constraint were corrected, false otherwise. + * @return Returns true if one ore more constraint were corrected, false otherwise. */ bool apply_constraints() { - double tol_times = 1e-1; + FP tol_times = 1e-1; int corrected = false; @@ -147,13 +147,13 @@ class Parameters : public ParametersBase } /** - * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error + * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error * if constraints are not satisfied. - * @return Returns true if one constraint is not satisfied, otherwise false. + * @return Returns true if one constraint is not satisfied, otherwise false. */ bool check_constraints() const { - double tol_times = 1e-1; + FP tol_times = 1e-1; for (auto i = AgeGroup(0); i < AgeGroup(m_num_groups); i++) { diff --git a/cpp/models/sde_seirvv/infection_state.h b/cpp/models/sde_seirvv/infection_state.h index 04d4a81603..36a6e451c6 100644 --- a/cpp/models/sde_seirvv/infection_state.h +++ b/cpp/models/sde_seirvv/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn diff --git a/cpp/models/sde_seirvv/model.cpp b/cpp/models/sde_seirvv/model.cpp index 7955b68069..440178eeb5 100644 --- a/cpp/models/sde_seirvv/model.cpp +++ b/cpp/models/sde_seirvv/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn diff --git a/cpp/models/sde_seirvv/model.h b/cpp/models/sde_seirvv/model.h index 04f17b8804..76a707a3c5 100644 --- a/cpp/models/sde_seirvv/model.h +++ b/cpp/models/sde_seirvv/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -46,65 +46,65 @@ using Flows = TypeList, Flow>; -class Model : public mio::StochasticModel, - Parameters, Flows> +template +class Model + : public mio::StochasticModel, Parameters, Flows> { - using Base = mio::StochasticModel, - Parameters, Flows>; + using Base = mio::StochasticModel, Parameters, Flows>; public: Model() - : Base(Populations({InfectionState::Count}, 0.), ParameterSet()) + : Base(typename Base::Populations({InfectionState::Count}, 0.0), typename Base::ParameterSet()) { } - void get_flows(Eigen::Ref> pop, Eigen::Ref> y, - ScalarType t, Eigen::Ref> flows) const + void get_flows(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> flows) const { const auto& params = this->parameters; - params.get().get_matrix_at(t)(0, 0); - ScalarType coeffStoIV1 = params.get().get_matrix_at(t)(0, 0) * - params.get() / populations.get_total(); - ScalarType coeffStoIV2 = params.get().get_matrix_at(t)(0, 0) * - params.get() / populations.get_total(); + params.template get>().get_matrix_at(SimulationTime(t))(0, 0); + FP coeffStoIV1 = params.template get>().get_matrix_at(SimulationTime(t))(0, 0) * + params.template get>() / this->populations.get_total(); + FP coeffStoIV2 = params.template get>().get_matrix_at(SimulationTime(t))(0, 0) * + params.template get>() / this->populations.get_total(); - flows[get_flat_flow_index()] = + flows[this->template get_flat_flow_index()] = coeffStoIV1 * y[(size_t)InfectionState::Susceptible] * pop[(size_t)InfectionState::InfectedV1]; - flows[get_flat_flow_index()] = + flows[this->template get_flat_flow_index()] = coeffStoIV2 * y[(size_t)InfectionState::Susceptible] * (pop[(size_t)InfectionState::InfectedV1V2] + pop[(size_t)InfectionState::InfectedV2]); - flows[get_flat_flow_index()] = - (1.0 / params.get()) * y[(size_t)InfectionState::ExposedV1]; + flows[this->template get_flat_flow_index()] = + (1.0 / params.template get>()) * y[(size_t)InfectionState::ExposedV1]; - flows[get_flat_flow_index()] = - (1.0 / params.get()) * y[(size_t)InfectionState::ExposedV2]; + flows[this->template get_flat_flow_index()] = + (1.0 / params.template get>()) * y[(size_t)InfectionState::ExposedV2]; - flows[get_flat_flow_index()] = - (1.0 / params.get()) * y[(size_t)InfectionState::InfectedV1]; + flows[this->template get_flat_flow_index()] = + (1.0 / params.template get>()) * y[(size_t)InfectionState::InfectedV1]; - flows[get_flat_flow_index()] = - (1.0 / params.get()) * y[(size_t)InfectionState::InfectedV2]; + flows[this->template get_flat_flow_index()] = + (1.0 / params.template get>()) * y[(size_t)InfectionState::InfectedV2]; - flows[get_flat_flow_index()] = + flows[this->template get_flat_flow_index()] = coeffStoIV2 * y[(size_t)InfectionState::RecoveredV1] * (pop[(size_t)InfectionState::InfectedV1V2] + pop[(size_t)InfectionState::InfectedV2]); - flows[get_flat_flow_index()] = - (1.0 / params.get()) * y[(size_t)InfectionState::ExposedV1V2]; + flows[this->template get_flat_flow_index()] = + (1.0 / params.template get>()) * y[(size_t)InfectionState::ExposedV1V2]; - flows[get_flat_flow_index()] = - (1.0 / params.get()) * y[(size_t)InfectionState::InfectedV1V2]; + flows[this->template get_flat_flow_index()] = + (1.0 / params.template get>()) * y[(size_t)InfectionState::InfectedV1V2]; } - void get_noise(Eigen::Ref> pop, Eigen::Ref> y, - ScalarType t, Eigen::Ref> noise) const + void get_noise(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> noise) const { - Eigen::VectorX flows(Flows::size()); + Eigen::VectorX flows(Flows::size()); get_flows(pop, y, t, flows); flows = flows.array().sqrt() * Base::white_noise(Flows::size()).array(); - get_derivatives(flows, noise); + this->get_derivatives(flows, noise); } }; diff --git a/cpp/models/sde_seirvv/parameters.h b/cpp/models/sde_seirvv/parameters.h index a31f10dd24..534289d0c8 100644 --- a/cpp/models/sde_seirvv/parameters.h +++ b/cpp/models/sde_seirvv/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -35,11 +35,12 @@ namespace sseirvv ******************************************/ /** - * @brief Probability of getting infected from a contact + * @brief Probability of getting infected from a contact * with variant 1. */ +template struct TransmissionProbabilityOnContactV1 { - using Type = UncertainValue<>; + using Type = UncertainValue; static Type get_default() { return Type(1.0); @@ -51,11 +52,12 @@ struct TransmissionProbabilityOnContactV1 { }; /** - * @brief Probability of getting infected from a contact + * @brief Probability of getting infected from a contact * with variant 2. */ +template struct TransmissionProbabilityOnContactV2 { - using Type = UncertainValue<>; + using Type = UncertainValue; static Type get_default() { return Type(1.0); @@ -69,8 +71,9 @@ struct TransmissionProbabilityOnContactV2 { /** * @brief The latent time of variant 1 in days. */ +template struct TimeExposedV1 { - using Type = UncertainValue; + using Type = UncertainValue; static Type get_default() { return Type(6.0); @@ -84,8 +87,9 @@ struct TimeExposedV1 { /** * @brief The latent time of variant 2 in days. */ +template struct TimeExposedV2 { - using Type = UncertainValue; + using Type = UncertainValue; static Type get_default() { return Type(6.0); @@ -99,8 +103,9 @@ struct TimeExposedV2 { /** * @brief The infectious time of variant 1 in days. */ +template struct TimeInfectedV1 { - using Type = UncertainValue; + using Type = UncertainValue; static Type get_default() { return Type(6.0); @@ -114,8 +119,9 @@ struct TimeInfectedV1 { /** * @brief The infectious time of variant 2 in days. */ +template struct TimeInfectedV2 { - using Type = UncertainValue; + using Type = UncertainValue; static Type get_default() { return Type(6.0); @@ -129,8 +135,9 @@ struct TimeInfectedV2 { /** * @brief The contact patterns within the society are modelled using a ContactMatrix. */ +template struct ContactPatterns { - using Type = ContactMatrix; + using Type = ContactMatrix; static Type get_default() { return Type{1}; @@ -141,23 +148,26 @@ struct ContactPatterns { } }; -using ParametersBase = ParameterSet; +template +using ParametersBase = + ParameterSet, TransmissionProbabilityOnContactV2, TimeExposedV1, + TimeExposedV2, TimeInfectedV1, TimeInfectedV2, ContactPatterns>; /** * @brief Parameters of stochastic SEIRVV model. */ -class Parameters : public ParametersBase +template +class Parameters : public ParametersBase { public: Parameters() - : ParametersBase() + : ParametersBase() { } /** * @brief Checks whether all Parameters satisfy their corresponding constraints and applies them, if they do not. - * Time spans cannot be negative and probabilities can only take values between [0,1]. + * Time spans cannot be negative and probabilities can only take values between [0,1]. * * 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() @@ -166,119 +176,119 @@ class Parameters : public ParametersBase * (like 0 or 1 for probabilities or small positive values for time spans) values are set here and a manual adaptation * may often be necessary to have set meaningful values. * - * @return Returns true if one ore more constraint were corrected, false otherwise. + * @return Returns true if one ore more constraint were corrected, false otherwise. */ bool apply_constraints() { - double tol_times = 1e-1; + FP tol_times = 1e-1; int corrected = false; - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_warning("Constraint check: Parameter TimeExposedV1 changed from {:.4f} to {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), tol_times); - this->get() = tol_times; - corrected = true; + this->template get>(), tol_times); + this->template get>() = tol_times; + corrected = true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_warning("Constraint check: Parameter TimeExposedV2 changed from {:.4f} to {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), tol_times); - this->get() = tol_times; - corrected = true; + this->template get>(), tol_times); + this->template get>() = tol_times; + corrected = true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_warning("Constraint check: Parameter TimeInfectedV1 changed from {:.4f} to {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), tol_times); - this->get() = tol_times; - corrected = true; + this->template get>(), tol_times); + this->template get>() = tol_times; + corrected = true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_warning("Constraint check: Parameter TimeInfectedV2 changed from {:.4f} to {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), tol_times); - this->get() = tol_times; - corrected = true; + this->template get>(), tol_times); + this->template get>() = tol_times; + corrected = true; } - if (this->get() < 0.0 || - this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_warning("Constraint check: Parameter TransmissionProbabilityOnContactV1 changed from {:0.4f} to {:d} ", - this->get(), 0.0); - this->get() = 0.0; - corrected = true; + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } - if (this->get() < 0.0 || - this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_warning("Constraint check: Parameter TransmissionProbabilityOnContactV2 changed from {:0.4f} to {:d} ", - this->get(), 0.0); - this->get() = 0.0; - corrected = true; + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } return corrected; } /** - * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error + * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error * if constraints are not satisfied. - * @return Returns true if one constraint is not satisfied, otherwise false. + * @return Returns true if one constraint is not satisfied, otherwise false. */ bool check_constraints() const { - ScalarType tol_times = 1e-1; + FP tol_times = 1e-1; - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeExposedV1 {:.4f} smaller or equal {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), 0.0); + this->template get>(), 0.0); return true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeExposedV2 {:.4f} smaller or equal {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), 0.0); + this->template get>(), 0.0); return true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeInfectedV1 {:.4f} smaller or equal {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), 0.0); + this->template get>(), 0.0); return true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeInfectedV2 {:.4f} smaller or equal {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), 0.0); + this->template get>(), 0.0); return true; } - if (this->get() < 0.0 || - this->get() > 1.0) { - log_error( - "Constraint check: Parameter TransmissionProbabilityOnContactV1 {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_error("Constraint check: Parameter TransmissionProbabilityOnContactV1 {:.4f} smaller {:.4f} or greater " + "{:.4f}", + this->template get>(), 0.0, 1.0); return true; } - if (this->get() < 0.0 || - this->get() > 1.0) { - log_error( - "Constraint check: Parameter TransmissionProbabilityOnContactV2 {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_error("Constraint check: Parameter TransmissionProbabilityOnContactV2 {:.4f} smaller {:.4f} or greater " + "{:.4f}", + this->template get>(), 0.0, 1.0); return true; } return false; } private: - Parameters(ParametersBase&& base) - : ParametersBase(std::move(base)) + Parameters(ParametersBase&& base) + : ParametersBase(std::move(base)) { } @@ -290,7 +300,7 @@ class Parameters : public ParametersBase template static IOResult deserialize(IOContext& io) { - BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); return success(Parameters(std::move(base))); } }; diff --git a/cpp/models/sde_sir/infection_state.h b/cpp/models/sde_sir/infection_state.h index 04804b79dd..772ce33dc6 100644 --- a/cpp/models/sde_sir/infection_state.h +++ b/cpp/models/sde_sir/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn diff --git a/cpp/models/sde_sir/model.cpp b/cpp/models/sde_sir/model.cpp index d3214eaec0..772358f15c 100644 --- a/cpp/models/sde_sir/model.cpp +++ b/cpp/models/sde_sir/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn diff --git a/cpp/models/sde_sir/model.h b/cpp/models/sde_sir/model.h index 8f02333c1b..557488870d 100644 --- a/cpp/models/sde_sir/model.h +++ b/cpp/models/sde_sir/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -39,38 +39,38 @@ namespace ssir using Flows = TypeList, Flow>; -class Model : public mio::StochasticModel, - Parameters, Flows> +template +class Model + : public mio::StochasticModel, Parameters, Flows> { public: - using Base = mio::StochasticModel, - Parameters, Flows>; + using Base = mio::StochasticModel, Parameters, Flows>; Model() : Base(typename Base::Populations({InfectionState::Count}, 0.), typename Base::ParameterSet()) { } - void get_flows(Eigen::Ref> pop, Eigen::Ref> y, - ScalarType t, Eigen::Ref> flows) const + void get_flows(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> flows) const { - auto& params = Base::parameters; - ScalarType coeffStoI = params.template get().get_matrix_at(t)(0, 0) * - params.template get() / Base::populations.get_total(); + auto& params = Base::parameters; + FP coeffStoI = params.template get>().get_matrix_at(SimulationTime(t))(0, 0) * + params.template get>() / this->populations.get_total(); flows[Base::template get_flat_flow_index()] = coeffStoI * y[(size_t)InfectionState::Susceptible] * pop[(size_t)InfectionState::Infected]; flows[Base::template get_flat_flow_index()] = - (1.0 / params.template get()) * y[(size_t)InfectionState::Infected]; + (1.0 / params.template get>()) * y[(size_t)InfectionState::Infected]; } - void get_noise(Eigen::Ref> pop, Eigen::Ref> y, - ScalarType t, Eigen::Ref> noise) const + void get_noise(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> noise) const { - Eigen::VectorX flows(Flows::size()); + Eigen::VectorX flows(Flows::size()); get_flows(pop, y, t, flows); flows = flows.array().sqrt() * Base::white_noise(Flows::size()).array(); - get_derivatives(flows, noise); + this->get_derivatives(flows, noise); } }; diff --git a/cpp/models/sde_sir/parameters.h b/cpp/models/sde_sir/parameters.h index fbf5e30583..ea4b115ee0 100644 --- a/cpp/models/sde_sir/parameters.h +++ b/cpp/models/sde_sir/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -37,8 +37,9 @@ namespace ssir /** * @brief probability of getting infected from a contact */ +template struct TransmissionProbabilityOnContact { - using Type = UncertainValue<>; + using Type = UncertainValue; static Type get_default() { return Type(1.0); @@ -52,8 +53,9 @@ struct TransmissionProbabilityOnContact { /** * @brief the infectious time in day unit */ +template struct TimeInfected { - using Type = UncertainValue; + using Type = UncertainValue; static Type get_default() { return Type(6.0); @@ -67,8 +69,9 @@ struct TimeInfected { /** * @brief the contact patterns within the society are modelled using a ContactMatrix */ +template struct ContactPatterns { - using Type = ContactMatrix; + using Type = ContactMatrix; static Type get_default() { return Type{1}; @@ -79,22 +82,24 @@ struct ContactPatterns { } }; -using ParametersBase = ParameterSet; +template +using ParametersBase = ParameterSet, TimeInfected, ContactPatterns>; /** * @brief Parameters of SIR model. */ -class Parameters : public ParametersBase +template +class Parameters : public ParametersBase { public: Parameters() - : ParametersBase() + : ParametersBase() { } /** * @brief Checks whether all Parameters satisfy their corresponding constraints and applies them, if they do not. - * Time spans cannot be negative and probabilities can only take values between [0,1]. + * Time spans cannot be negative and probabilities can only take values between [0,1]. * * 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() @@ -103,60 +108,60 @@ class Parameters : public ParametersBase * (like 0 or 1 for probabilities or small positive values for time spans) values are set here and a manual adaptation * may often be necessary to have set meaningful values. * - * @return Returns true if one ore more constraint were corrected, false otherwise. + * @return Returns true if one ore more constraint were corrected, false otherwise. */ bool apply_constraints() { - ScalarType tol_times = 1e-1; + FP tol_times = 1e-1; int corrected = false; - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_warning("Constraint check: Parameter TimeInfected changed from {:.4f} to {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), tol_times); - this->get() = tol_times; - corrected = true; + this->template get>(), tol_times); + this->template get>() = tol_times; + corrected = true; } - if (this->get() < 0.0 || - this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_warning("Constraint check: Parameter TransmissionProbabilityOnContact changed from {:0.4f} to {:d} ", - this->get(), 0.0); - this->get() = 0.0; - corrected = true; + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } return corrected; } /** - * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error + * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error * if constraints are not satisfied. - * @return Returns true if one constraint is not satisfied, otherwise false. + * @return Returns true if one constraint is not satisfied, otherwise false. */ bool check_constraints() const { - ScalarType tol_times = 1e-1; + FP tol_times = 1e-1; - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeInfected {:.4f} smaller or equal {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), 0.0); + this->template get>(), 0.0); return true; } - if (this->get() < 0.0 || - this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_error( "Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); + this->template get>(), 0.0, 1.0); return true; } return false; } private: - Parameters(ParametersBase&& base) - : ParametersBase(std::move(base)) + Parameters(ParametersBase&& base) + : ParametersBase(std::move(base)) { } @@ -168,7 +173,7 @@ class Parameters : public ParametersBase template static IOResult deserialize(IOContext& io) { - BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); return success(Parameters(std::move(base))); } }; diff --git a/cpp/models/sde_sirs/infection_state.h b/cpp/models/sde_sirs/infection_state.h index af3a250a97..5094d644d7 100644 --- a/cpp/models/sde_sirs/infection_state.h +++ b/cpp/models/sde_sirs/infection_state.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn diff --git a/cpp/models/sde_sirs/model.cpp b/cpp/models/sde_sirs/model.cpp index 210e2b1b65..ad565bd2b1 100644 --- a/cpp/models/sde_sirs/model.cpp +++ b/cpp/models/sde_sirs/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn diff --git a/cpp/models/sde_sirs/model.h b/cpp/models/sde_sirs/model.h index 3df4b4ac1f..ea40664bd3 100644 --- a/cpp/models/sde_sirs/model.h +++ b/cpp/models/sde_sirs/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -40,40 +40,40 @@ using Flows = TypeList, Flow>; -class Model : public mio::StochasticModel, - Parameters, Flows> +template +class Model + : public mio::StochasticModel, Parameters, Flows> { public: - using Base = mio::StochasticModel, - Parameters, Flows>; + using Base = mio::StochasticModel, Parameters, Flows>; Model() - : Base(typename Base::Populations({InfectionState::Count}, 0.), typename Base::ParameterSet()) + : Base(typename Base::Populations({InfectionState::Count}, 0.0), typename Base::ParameterSet()) { } - void get_flows(Eigen::Ref> pop, Eigen::Ref> y, - ScalarType t, Eigen::Ref> flows) const + void get_flows(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> flows) const { - auto& params = Base::parameters; - ScalarType coeffStoI = params.template get().get_matrix_at(t)(0, 0) * - params.template get() / Base::populations.get_total(); + auto& params = Base::parameters; + FP coeffStoI = params.template get>().get_matrix_at(SimulationTime(t))(0, 0) * + params.template get>() / Base::populations.get_total(); flows[this->template get_flat_flow_index()] = coeffStoI * y[(size_t)InfectionState::Susceptible] * pop[(size_t)InfectionState::Infected]; flows[this->template get_flat_flow_index()] = - (1.0 / params.template get()) * y[(size_t)InfectionState::Infected]; + (1.0 / params.template get>()) * y[(size_t)InfectionState::Infected]; flows[this->template get_flat_flow_index()] = - (1.0 / params.template get()) * y[(size_t)InfectionState::Recovered]; + (1.0 / params.template get>()) * y[(size_t)InfectionState::Recovered]; } - void get_noise(Eigen::Ref> pop, Eigen::Ref> y, - ScalarType t, Eigen::Ref> noise) const + void get_noise(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> noise) const { - Eigen::VectorX flows(Flows::size()); + Eigen::VectorX flows(Flows::size()); get_flows(pop, y, t, flows); flows = flows.array().sqrt() * Base::white_noise(Flows::size()).array(); - get_derivatives(flows, noise); + this->get_derivatives(flows, noise); } }; diff --git a/cpp/models/sde_sirs/parameters.h b/cpp/models/sde_sirs/parameters.h index 8695af1ced..4d4ca6bda7 100644 --- a/cpp/models/sde_sirs/parameters.h +++ b/cpp/models/sde_sirs/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -37,8 +37,9 @@ namespace ssirs /** * @brief probability of getting infected from a contact */ +template struct TransmissionProbabilityOnContact { - using Type = UncertainValue<>; + using Type = UncertainValue; static Type get_default() { return Type(1.0); @@ -52,8 +53,9 @@ struct TransmissionProbabilityOnContact { /** * @brief the infectious time in day unit */ +template struct TimeInfected { - using Type = UncertainValue<>; + using Type = UncertainValue; static Type get_default() { return Type(6.0); @@ -67,8 +69,9 @@ struct TimeInfected { /** * @brief the infectious time in day unit */ +template struct TimeImmune { - using Type = UncertainValue<>; + using Type = UncertainValue; static Type get_default() { return Type(6.0); @@ -82,8 +85,9 @@ struct TimeImmune { /** * @brief the contact patterns within the society are modelled using a ContactMatrix */ +template struct ContactPatterns { - using Type = ContactMatrix; + using Type = ContactMatrix; static Type get_default() { return Type{1}; @@ -94,22 +98,25 @@ struct ContactPatterns { } }; -using ParametersBase = ParameterSet; +template +using ParametersBase = + ParameterSet, TimeInfected, ContactPatterns, TimeImmune>; /** * @brief Parameters of SIR model. */ -class Parameters : public ParametersBase +template +class Parameters : public ParametersBase { public: Parameters() - : ParametersBase() + : ParametersBase() { } /** * @brief Checks whether all Parameters satisfy their corresponding constraints and applies them, if they do not. - * Time spans cannot be negative and probabilities can only take values between [0,1]. + * Time spans cannot be negative and probabilities can only take values between [0,1]. * * 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() @@ -118,75 +125,75 @@ class Parameters : public ParametersBase * (like 0 or 1 for probabilities or small positive values for time spans) values are set here and a manual adaptation * may often be necessary to have set meaningful values. * - * @return Returns true if one ore more constraint were corrected, false otherwise. + * @return Returns true if one ore more constraint were corrected, false otherwise. */ bool apply_constraints() { - ScalarType tol_times = 1e-1; + FP tol_times = 1e-1; int corrected = false; - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_warning("Constraint check: Parameter TimeInfected changed from {:.4f} to {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), tol_times); - this->get() = tol_times; - corrected = true; + this->template get>(), tol_times); + this->template get>() = tol_times; + corrected = true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_warning("Constraint check: Parameter TimeInfected changed from {:.4f} to {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), tol_times); - this->get() = tol_times; - corrected = true; + this->template get>(), tol_times); + this->template get>() = tol_times; + corrected = true; } - if (this->get() < 0.0 || - this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_warning("Constraint check: Parameter TransmissionProbabilityOnContact changed from {:0.4f} to {:d} ", - this->get(), 0.0); - this->get() = 0.0; - corrected = true; + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } return corrected; } /** - * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error + * @brief Checks whether all Parameters satisfy their corresponding constraints and logs an error * if constraints are not satisfied. - * @return Returns true if one constraint is not satisfied, otherwise false. + * @return Returns true if one constraint is not satisfied, otherwise false. */ bool check_constraints() const { - ScalarType tol_times = 1e-1; + FP tol_times = 1e-1; - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeInfected {:.4f} smaller or equal {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), 0.0); + this->template get>(), 0.0); return true; } - if (this->get() < tol_times) { + if (this->template get>() < tol_times) { log_error("Constraint check: Parameter TimeInfected {:.4f} smaller or equal {:.4f}. Please note that " "unreasonably small compartment stays lead to massively increased run time. Consider to cancel " "and reset parameters.", - this->get(), 0.0); + this->template get>(), 0.0); return true; } - if (this->get() < 0.0 || - this->get() > 1.0) { + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { log_error( "Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); + this->template get>(), 0.0, 1.0); return true; } return false; } private: - Parameters(ParametersBase&& base) - : ParametersBase(std::move(base)) + Parameters(ParametersBase&& base) + : ParametersBase(std::move(base)) { } @@ -198,7 +205,7 @@ class Parameters : public ParametersBase template static IOResult deserialize(IOContext& io) { - BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); return success(Parameters(std::move(base))); } }; diff --git a/cpp/models/smm/model.cpp b/cpp/models/smm/model.cpp index 0d771f0e52..af0d58eb73 100644 --- a/cpp/models/smm/model.cpp +++ b/cpp/models/smm/model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 German Aerospace Center (DLR-SC) * * Authors: René Schmieding diff --git a/cpp/models/smm/model.h b/cpp/models/smm/model.h index 4066667455..a30a6f365e 100644 --- a/cpp/models/smm/model.h +++ b/cpp/models/smm/model.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 German Aerospace Center (DLR-SC) * * Authors: René Schmieding, Julia Bicker @@ -37,17 +37,16 @@ namespace smm * @tparam regions Number of regions. * @tparam Status An infection state enum. */ -template -class Model - : public mio::CompartmentalModel, - ParametersBase> +template +class Model : public mio::CompartmentalModel, + ParametersBase> { - using Base = mio::CompartmentalModel, - ParametersBase>; + using Base = mio::CompartmentalModel, + ParametersBase>; public: Model() - : Base(typename Base::Populations({static_cast(regions), Status::Count}, 0.), + : Base(typename Base::Populations({static_cast(regions), Status::Count}, 0.0), typename Base::ParameterSet()) { } @@ -58,7 +57,7 @@ class Model * @param[in] x The current state of the model. * @return Current value of the adoption rate. */ - ScalarType evaluate(const AdoptionRate& rate, const Eigen::VectorXd& x) const + FP evaluate(const AdoptionRate& rate, const Eigen::VectorX& x) const { const auto& pop = this->populations; const auto source = pop.get_flat_index({rate.region, rate.from}); @@ -67,12 +66,12 @@ class Model return rate.factor * x[source]; } else { // second order adoption - ScalarType N = 0; + FP N = 0; for (size_t s = 0; s < static_cast(Status::Count); ++s) { N += x[pop.get_flat_index({rate.region, Status(s)})]; } // accumulate influences - ScalarType influences = 0.0; + FP influences = 0.0; for (size_t i = 0; i < rate.influences.size(); i++) { influences += rate.influences[i].factor * x[pop.get_flat_index({rate.region, rate.influences[i].status})]; @@ -87,7 +86,7 @@ class Model * @param[in] x The current state of the model. * @return Current value of the transition rate. */ - ScalarType evaluate(const TransitionRate& rate, const Eigen::VectorXd& x) const + FP evaluate(const TransitionRate& rate, const Eigen::VectorX& x) const { const auto source = this->populations.get_flat_index({rate.from, rate.status}); return rate.factor * x[source]; diff --git a/cpp/models/smm/parameters.h b/cpp/models/smm/parameters.h index 70b8212291..4cc54ebb5b 100644 --- a/cpp/models/smm/parameters.h +++ b/cpp/models/smm/parameters.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 German Aerospace Center (DLR-SC) * * Authors: René Schmieding, Julia Bicker @@ -35,9 +35,9 @@ namespace smm * @brief A vector of AdoptionRate%s, see mio::AdoptionRate * @tparam Status An infection state enum. */ -template +template struct AdoptionRates { - using Type = std::vector>; + using Type = std::vector>; const static std::string name() { return "AdoptionRates"; @@ -48,25 +48,25 @@ struct AdoptionRates { * @brief Struct defining a possible regional transition in a Model based on Poisson Processes. * @tparam Status An infection state enum. */ -template +template struct TransitionRate { Status status; // i mio::regions::Region from; // k mio::regions::Region to; // l - ScalarType factor; // lambda_i^{kl} + FP factor; // lambda_i^{kl} }; -template +template struct TransitionRates { - using Type = std::vector>; + using Type = std::vector>; const static std::string name() { return "TransitionRates"; } }; -template -using ParametersBase = mio::ParameterSet, TransitionRates>; +template +using ParametersBase = mio::ParameterSet, TransitionRates>; } // namespace smm diff --git a/cpp/models/smm/simulation.cpp b/cpp/models/smm/simulation.cpp index e84c630051..a374c08831 100644 --- a/cpp/models/smm/simulation.cpp +++ b/cpp/models/smm/simulation.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: René Schmieding, Julia Bicker diff --git a/cpp/models/smm/simulation.h b/cpp/models/smm/simulation.h index b63e77fc99..4231f76a05 100644 --- a/cpp/models/smm/simulation.h +++ b/cpp/models/smm/simulation.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 German Aerospace Center (DLR-SC) * * Authors: René Schmieding, Julia Bicker @@ -37,12 +37,12 @@ namespace smm * @tparam regions The number of regions. * @tparam Status An infection state enum. */ -template +template class Simulation { public: public: - using Model = smm::Model; + using Model = smm::Model; /** * @brief Set up the simulation for a Stochastic Metapopulation Model. @@ -50,7 +50,7 @@ class Simulation * @param[in] t0 Start time. * @param[in] dt Initial Step size. */ - Simulation(Model const& model, ScalarType t0 = 0., ScalarType dt = 1.) + Simulation(Model const& model, FP t0 = 0., FP dt = 1.) : m_dt(dt) , m_model(std::make_unique(model)) , m_result(t0, m_model->get_initial_values()) @@ -69,7 +69,7 @@ class Simulation })); // initialize (internal) next event times by random values for (size_t i = 0; i < m_tp_next_event.size(); i++) { - m_tp_next_event[i] += mio::ExponentialDistribution::get_instance()(m_model->get_rng(), 1.0); + m_tp_next_event[i] += mio::ExponentialDistribution::get_instance()(m_model->get_rng(), 1.0); } } @@ -78,13 +78,13 @@ class Simulation * This function performs a Gillespie algorithm. * @param tmax Next stopping point of simulation. */ - Eigen::Ref advance(ScalarType tmax) + Eigen::Ref> advance(FP tmax) { update_current_rates_and_waiting_times(); - size_t next_event = determine_next_event(); // index of the next event - ScalarType current_time = m_result.get_last_time(); + size_t next_event = determine_next_event(); // index of the next event + FP current_time = m_result.get_last_time(); // set in the past to add a new time point immediately - ScalarType last_result_time = current_time - m_dt; + FP last_result_time = current_time - m_dt; // iterate over time while (current_time + m_waiting_times[next_event] < tmax) { // update time @@ -101,25 +101,24 @@ class Simulation // perform adoption event const auto& rate = adoption_rates()[next_event]; m_result.get_last_value()[m_model->populations.get_flat_index({rate.region, rate.from})] -= 1; - m_model->populations[{rate.region, rate.from}] -= 1; + m_model->populations[{rate.region, rate.from}] -= 1.0; m_result.get_last_value()[m_model->populations.get_flat_index({rate.region, rate.to})] += 1; - m_model->populations[{rate.region, rate.to}] += 1; + m_model->populations[{rate.region, rate.to}] += 1.0; } else { // perform transition event const auto& rate = transition_rates()[next_event - adoption_rates().size()]; m_result.get_last_value()[m_model->populations.get_flat_index({rate.from, rate.status})] -= 1; - m_model->populations[{rate.from, rate.status}] -= 1; + m_model->populations[{rate.from, rate.status}] -= 1.0; m_result.get_last_value()[m_model->populations.get_flat_index({rate.to, rate.status})] += 1; - m_model->populations[{rate.to, rate.status}] += 1; + m_model->populations[{rate.to, rate.status}] += 1.0; } // update internal times for (size_t i = 0; i < m_internal_time.size(); i++) { m_internal_time[i] += m_current_rates[i] * m_waiting_times[next_event]; } // draw new "next event" time for the occured event - m_tp_next_event[next_event] += - mio::ExponentialDistribution::get_instance()(m_model->get_rng(), 1.0); + m_tp_next_event[next_event] += mio::ExponentialDistribution::get_instance()(m_model->get_rng(), 1.0); // precalculate next event update_current_rates_and_waiting_times(); next_event = determine_next_event(); @@ -136,11 +135,11 @@ class Simulation * @brief Returns the final simulation result. * @return A TimeSeries to represent the final simulation result. */ - TimeSeries& get_result() + TimeSeries& get_result() { return m_result; } - const TimeSeries& get_result() const + const TimeSeries& get_result() const { return m_result; } @@ -161,17 +160,17 @@ class Simulation /** * @brief Returns the model's transition rates. */ - inline constexpr const typename smm::TransitionRates::Type& transition_rates() + inline constexpr const typename smm::TransitionRates::Type& transition_rates() { - return m_model->parameters.template get>(); + return m_model->parameters.template get>(); } /** * @brief Returns the model's adoption rates. */ - inline constexpr const typename smm::AdoptionRates::Type& adoption_rates() + inline constexpr const typename smm::AdoptionRates::Type& adoption_rates() { - return m_model->parameters.template get>(); + return m_model->parameters.template get>(); } /** @@ -184,14 +183,14 @@ class Simulation m_current_rates[i] = m_model->evaluate(rate, m_result.get_last_value()); m_waiting_times[i] = (m_current_rates[i] > 0) ? (m_tp_next_event[i] - m_internal_time[i]) / m_current_rates[i] - : std::numeric_limits::max(); + : std::numeric_limits::max(); i++; } for (const auto& rate : transition_rates()) { m_current_rates[i] = m_model->evaluate(rate, m_result.get_last_value()); m_waiting_times[i] = (m_current_rates[i] > 0) ? (m_tp_next_event[i] - m_internal_time[i]) / m_current_rates[i] - : std::numeric_limits::max(); + : std::numeric_limits::max(); i++; } } @@ -204,15 +203,14 @@ class Simulation return std::distance(m_waiting_times.begin(), std::min_element(m_waiting_times.begin(), m_waiting_times.end())); } - ScalarType m_dt; ///< Initial step size + FP m_dt; ///< Initial step size std::unique_ptr m_model; ///< Pointer to the model used in the simulation. - mio::TimeSeries m_result; ///< Result time series. + mio::TimeSeries m_result; ///< Result time series. - std::vector m_internal_time; ///< Internal times of all poisson processes (aka T_k). - std::vector m_tp_next_event; ///< Internal time points of next event i after m_internal[i] (aka P_k). - std::vector m_waiting_times; ///< External times between m_internal_time and m_tp_next_event. - std::vector - m_current_rates; ///< Current values of both types of rates i.e. adoption and transition rates. + std::vector m_internal_time; ///< Internal times of all poisson processes (aka T_k). + std::vector m_tp_next_event; ///< Internal time points of next event i after m_internal[i] (aka P_k). + std::vector m_waiting_times; ///< External times between m_internal_time and m_tp_next_event. + std::vector m_current_rates; ///< Current values of both types of rates i.e. adoption and transition rates. }; } //namespace smm diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 126e17ff2c..2994eb2b38 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -87,6 +87,7 @@ set(TESTSOURCES test_graph_abm.cpp test_abstract_parameter_dist.cpp test_temporal_hybrid_model.cpp + test_uncertain_ad_compatibility.cpp ) if(MEMILIO_HAS_JSONCPP) diff --git a/cpp/tests/actions.h b/cpp/tests/actions.h index 43d2a5dca9..387f3a71e9 100644 --- a/cpp/tests/actions.h +++ b/cpp/tests/actions.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/distributions_helpers.cpp b/cpp/tests/distributions_helpers.cpp index 85fadd0f3e..cc1821e299 100644 --- a/cpp/tests/distributions_helpers.cpp +++ b/cpp/tests/distributions_helpers.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/distributions_helpers.h b/cpp/tests/distributions_helpers.h index 3030533978..eba2e97c79 100644 --- a/cpp/tests/distributions_helpers.h +++ b/cpp/tests/distributions_helpers.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/load_test_data.h b/cpp/tests/load_test_data.h index 2cb327e690..ce82a40ca5 100644 --- a/cpp/tests/load_test_data.h +++ b/cpp/tests/load_test_data.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/matchers.cpp b/cpp/tests/matchers.cpp index e51c2a7a58..94a23cc73f 100644 --- a/cpp/tests/matchers.cpp +++ b/cpp/tests/matchers.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: René Schmieding diff --git a/cpp/tests/matchers.h b/cpp/tests/matchers.h index ca8ccbfad1..1e053ca4aa 100644 --- a/cpp/tests/matchers.h +++ b/cpp/tests/matchers.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/random_number_test.h b/cpp/tests/random_number_test.h index f9c82590fa..351956e3bc 100644 --- a/cpp/tests/random_number_test.h +++ b/cpp/tests/random_number_test.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: René Schmieding @@ -30,7 +30,7 @@ class RandomNumberTest : public ::testing::Test * @brief Draws a uniformly distributed random number. * @tparam FP A floating point type, defaults to double. * @param[in] min, max Lower and upper bound to the uniform distribution. - * @return A random value between min and max. + * @return A random value between min and max. */ template FP random_number(FP min = FP{-1e+3}, FP max = FP{1e+3}) @@ -42,7 +42,7 @@ class RandomNumberTest : public ::testing::Test * @brief Draws a uniformly distributed integer. * @tparam IntType A integer type, defaults to int. * @param[in] min, max Lower and upper bound to the uniform distribution. - * @return A random value between min and max. + * @return A random value between min and max. */ template IntType random_integer(IntType min = IntType{-1e+3}, IntType max = IntType{1e+3}) diff --git a/cpp/tests/temp_file_register.h b/cpp/tests/temp_file_register.h index 3a0b480f05..a27f4c6d72 100644 --- a/cpp/tests/temp_file_register.h +++ b/cpp/tests/temp_file_register.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -31,7 +31,7 @@ class TempFileRegister { public: /** - * Destructor. + * Destructor. * Removes all registered files. */ ~TempFileRegister() @@ -49,9 +49,9 @@ class TempFileRegister /** * create a unique path and register it for cleanup. * The path will be in the system temp directory if available. - * Otherwise it will be in the current working directory. + * Otherwise it will be in the current working directory. * The name of the file or directory follows the specified model. - * The model may contain placeholders one or more `%` that are replaced with random characters to make the name unique. + * The model may contain placeholders one or more `%` that are replaced with random characters to make the name unique. * @param model name of the file or directory with placeholders for random characters. * @return a unique file path that follows the specified model. */ diff --git a/cpp/tests/test_abm_household.cpp b/cpp/tests/test_abm_household.cpp index 8e71346a33..7072ae02c3 100644 --- a/cpp/tests/test_abm_household.cpp +++ b/cpp/tests/test_abm_household.cpp @@ -21,7 +21,6 @@ #include "abm_helpers.h" #include - /** * @brief Test adding a household to a model. * Verifies correct number of persons, age groups, and location assignments. @@ -75,7 +74,7 @@ TEST(TestHouseholds, test_add_household_group_to_model) // Create the first household and add members auto household1 = mio::abm::Household(); household1.add_members(member1, 10); // Add ten members of age group 35-59 - household1.add_members(member2, 2); // Add two members of age group 5-14 + household1.add_members(member2, 2); // Add two members of age group 5-14 // Create the second household and add members auto household2 = mio::abm::Household(); diff --git a/cpp/tests/test_ad.cpp b/cpp/tests/test_ad.cpp index 9403ac2993..184e3b6a0e 100644 --- a/cpp/tests/test_ad.cpp +++ b/cpp/tests/test_ad.cpp @@ -24,7 +24,7 @@ #include "memilio/utils/logging.h" -#include "ad/ad.hpp" +#include "memilio/ad/ad.h" #include "boost/numeric/odeint.hpp" #include diff --git a/cpp/tests/test_analyze_result.cpp b/cpp/tests/test_analyze_result.cpp index a5d033ebcc..5360c7c3de 100644 --- a/cpp/tests/test_analyze_result.cpp +++ b/cpp/tests/test_analyze_result.cpp @@ -177,7 +177,7 @@ TEST(TestInterpolateGraph, basic) { using Model = mio::osecir::Model; using Simulation = mio::Simulation; - auto g = mio::Graph, mio::MobilityEdge>(); + auto g = mio::Graph, mio::MobilityEdge>(); g.add_node(0, Model(1), 0.5); g.add_node(1, Model(1), 0.5); for (auto& n : g.nodes()) { diff --git a/cpp/tests/test_binary_serializer.cpp b/cpp/tests/test_binary_serializer.cpp index 32f5739c8f..893d746a2f 100644 --- a/cpp/tests/test_binary_serializer.cpp +++ b/cpp/tests/test_binary_serializer.cpp @@ -181,7 +181,7 @@ TEST(BinarySerializer, model) //in a very complex object. correct serializing of single values is tested by other tests. mio::osecir::Model model{5}; mio::set_log_level(mio::LogLevel::err); - mio::osecir::set_params_distributions_normal(model, 0, 10, 0.01); + mio::osecir::set_params_distributions_normal(model, 0, 10, 0.01); mio::set_log_level(mio::LogLevel::warn); auto stream = mio::serialize_binary(model); auto result = mio::deserialize_binary(stream, mio::Tag>{}); diff --git a/cpp/tests/test_contact_matrix.cpp b/cpp/tests/test_contact_matrix.cpp index 540f5fb3df..4a8f3ad53f 100644 --- a/cpp/tests/test_contact_matrix.cpp +++ b/cpp/tests/test_contact_matrix.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -23,55 +23,59 @@ TEST(TestContactMatrix, initZero) { - mio::ContactMatrix cm(Eigen::Index(3)); - EXPECT_EQ(print_wrap(cm.get_matrix_at(-1e5)), print_wrap(Eigen::MatrixXd::Zero(3, 3))); - EXPECT_EQ(print_wrap(cm.get_matrix_at(0)), print_wrap(Eigen::MatrixXd::Zero(3, 3))); - EXPECT_EQ(print_wrap(cm.get_matrix_at(1e-32)), print_wrap(Eigen::MatrixXd::Zero(3, 3))); - EXPECT_EQ(print_wrap(cm.get_matrix_at(1e5)), print_wrap(Eigen::MatrixXd::Zero(3, 3))); + mio::ContactMatrix cm(Eigen::Index(3)); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(-1e5))), print_wrap(Eigen::MatrixXd::Zero(3, 3))); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(0))), print_wrap(Eigen::MatrixXd::Zero(3, 3))); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(1e-32))), + print_wrap(Eigen::MatrixXd::Zero(3, 3))); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(1e5))), print_wrap(Eigen::MatrixXd::Zero(3, 3))); } TEST(TestContactMatrix, initBaseAndMin) { auto B = (Eigen::MatrixXd(2, 2) << 1, 2, 3, 4).finished(); auto M = Eigen::MatrixXd::Constant(2, 2, 0.1); - mio::ContactMatrix cm(B, M); - EXPECT_EQ(print_wrap(cm.get_matrix_at(-1e5)), print_wrap(B)); - EXPECT_EQ(print_wrap(cm.get_matrix_at(0)), print_wrap(B)); - EXPECT_EQ(print_wrap(cm.get_matrix_at(1e-32)), print_wrap(B)); - EXPECT_EQ(print_wrap(cm.get_matrix_at(1e5)), print_wrap(B)); + mio::ContactMatrix cm(B, M); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(-1e5))), print_wrap(B)); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(0))), print_wrap(B)); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(1e-32))), print_wrap(B)); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(1e5))), print_wrap(B)); } TEST(TestContactMatrix, dampings) { auto B = (Eigen::MatrixXd(2, 2) << 1, 2, 3, 4).finished(); auto M = Eigen::MatrixXd::Constant(2, 2, 0.1); - mio::ContactMatrix cm(B, M); + mio::ContactMatrix cm(B, M); auto D1 = 0.25; auto D2 = (Eigen::MatrixXd(2, 2) << 0.0, 0.75, 0.5, 0.25).finished(); - cm.add_damping(D1, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(0.5)); - cm.add_damping(D2, mio::DampingLevel(7), mio::DampingType(2), mio::SimulationTime(2.0)); + cm.add_damping(D1, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(0.5)); + cm.add_damping(D2, mio::DampingLevel(7), mio::DampingType(2), mio::SimulationTime(2.0)); EXPECT_EQ(cm.get_dampings().size(), 2); EXPECT_THAT(cm.get_dampings(), - testing::ElementsAre( - mio::SquareDamping(D1, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(0.5), - Eigen::Index(2)), - mio::SquareDamping(D2, mio::DampingLevel(7), mio::DampingType(2), mio::SimulationTime(2.0)))); + testing::ElementsAre(mio::SquareDamping(D1, mio::DampingLevel(7), mio::DampingType(3), + mio::SimulationTime(0.5), Eigen::Index(2)), + mio::SquareDamping(D2, mio::DampingLevel(7), mio::DampingType(2), + mio::SimulationTime(2.0)))); - EXPECT_EQ(print_wrap(cm.get_matrix_at(-1e5)), print_wrap(B)); - EXPECT_EQ(print_wrap(cm.get_matrix_at(-0.5)), print_wrap(B)); - EXPECT_THAT(print_wrap(cm.get_matrix_at(0.5 + 1e-32)), MatrixNear(B - D1 * (B - M))); - EXPECT_THAT(print_wrap(cm.get_matrix_at(1e5)), MatrixNear(B - ((D1 + D2.array()) * (B - M).array()).matrix())); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(-1e5))), print_wrap(B)); + EXPECT_EQ(print_wrap(cm.get_matrix_at(mio::SimulationTime(-0.5))), print_wrap(B)); + EXPECT_THAT(print_wrap(cm.get_matrix_at(mio::SimulationTime(0.5 + 1e-32))), MatrixNear(B - D1 * (B - M))); + EXPECT_THAT(print_wrap(cm.get_matrix_at(mio::SimulationTime(1e5))), + MatrixNear(B - ((D1 + D2.array()) * (B - M).array()).matrix())); } TEST(TestContactMatrixGroup, sum) { - mio::ContactMatrixGroup cmg(3, 2); - cmg[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 1.0)); - cmg[1] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 2.0)); - cmg[2] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 3.0)); - cmg.add_damping(0.5, mio::DampingLevel(3), mio::DampingType(1), mio::SimulationTime(1.0)); + mio::ContactMatrixGroup cmg(3, 2); + cmg[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 1.0)); + cmg[1] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 2.0)); + cmg[2] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 3.0)); + cmg.add_damping(0.5, mio::DampingLevel(3), mio::DampingType(1), mio::SimulationTime(1.0)); - EXPECT_THAT(print_wrap(cmg.get_matrix_at(0.0)), MatrixNear(Eigen::MatrixXd::Constant(3, 3, 6.0))); - EXPECT_THAT(print_wrap(cmg.get_matrix_at(1.0)), MatrixNear(Eigen::MatrixXd::Constant(3, 3, 3.0))); + EXPECT_THAT(print_wrap(cmg.get_matrix_at(mio::SimulationTime(0.0))), + MatrixNear(Eigen::MatrixXd::Constant(3, 3, 6.0))); + EXPECT_THAT(print_wrap(cmg.get_matrix_at(mio::SimulationTime(1.0))), + MatrixNear(Eigen::MatrixXd::Constant(3, 3, 3.0))); } diff --git a/cpp/tests/test_custom_index_array.cpp b/cpp/tests/test_custom_index_array.cpp index 3d373feb44..f1b1f72a0e 100644 --- a/cpp/tests/test_custom_index_array.cpp +++ b/cpp/tests/test_custom_index_array.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Khoa Nguyen @@ -382,16 +382,14 @@ TEST(CustomIndexArray, resize_one_dimension) } // Additional test for checking the set_multiple functionality -TEST(CustomIndexArray, setMultiple_validIndices) { +TEST(CustomIndexArray, setMultiple_validIndices) +{ using ArrayType = mio::CustomIndexArray; // Initialize with zeros ArrayType array({mio::Index(3), Dim2::Count}, 0); // Define indices to set - std::vector indices = { - {mio::Index(0), Dim2::Male}, - {mio::Index(2), Dim2::Female} - }; + std::vector indices = {{mio::Index(0), Dim2::Male}, {mio::Index(2), Dim2::Female}}; // Set these indices to 42 array.set_multiple(indices, 42); @@ -407,7 +405,8 @@ TEST(CustomIndexArray, setMultiple_validIndices) { EXPECT_EQ((array[{mio::Index(2), Dim2::Male}]), 0); } -TEST(CustomIndexArray, setMultiple_emptyIndices) { +TEST(CustomIndexArray, setMultiple_emptyIndices) +{ using ArrayType = mio::CustomIndexArray; // Initialize with fives ArrayType array({mio::Index(2), Dim2::Count}, 5); diff --git a/cpp/tests/test_d_abm_model.cpp b/cpp/tests/test_d_abm_model.cpp index 1d59643345..c31b4aa5e4 100644 --- a/cpp/tests/test_d_abm_model.cpp +++ b/cpp/tests/test_d_abm_model.cpp @@ -236,7 +236,7 @@ TEST(TestDABMSimulation, advance) QuadWell::Agent a1{Eigen::Vector2d{-1, 1}, InfectionState::S}; QuadWell::Agent a2{Eigen::Vector2d{-1, 1}, InfectionState::R}; QuadWell::Agent a3{Eigen::Vector2d{-1, 1}, InfectionState::I}; - std::vector> adoption_rates; + std::vector> adoption_rates; //Add adoption rates for every region for (size_t region = 0; region < 4; ++region) { adoption_rates.push_back({InfectionState::S, diff --git a/cpp/tests/test_damping.cpp b/cpp/tests/test_damping.cpp index 996fc45e2d..860e7bf0ac 100644 --- a/cpp/tests/test_damping.cpp +++ b/cpp/tests/test_damping.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -23,123 +23,153 @@ TEST(TestDampings, initZero) { - mio::Dampings> dampings(3, 2); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-1e5)), print_wrap(Eigen::MatrixXd::Zero(3, 2))); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(0)), print_wrap(Eigen::MatrixXd::Zero(3, 2))); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(1e-32)), print_wrap(Eigen::MatrixXd::Zero(3, 2))); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(1e5)), print_wrap(Eigen::MatrixXd::Zero(3, 2))); + mio::Dampings>> dampings(3, 2); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-1e5))), + print_wrap(Eigen::MatrixXd::Zero(3, 2))); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(0))), + print_wrap(Eigen::MatrixXd::Zero(3, 2))); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1e-32))), + print_wrap(Eigen::MatrixXd::Zero(3, 2))); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1e5))), + print_wrap(Eigen::MatrixXd::Zero(3, 2))); } TEST(TestDampings, dampingsOnDifferentLevels) { - mio::Dampings> dampings(2, 2); + mio::Dampings>> dampings(2, 2); auto D1 = 0.25; auto D2 = (Eigen::MatrixXd(2, 2) << 0.25, 0.5, 0.75, 1).finished(); - dampings.add(D1, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(0.5)); - dampings.add(D2, mio::DampingLevel(13), mio::DampingType(3), mio::SimulationTime(2.0)); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-1e5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-0.5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(0.5 + 1e-32)), MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(1e5)), MatrixNear((D1 + D2.array() - D1 * D2.array()).matrix())); + dampings.add(D1, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(0.5)); + dampings.add(D2, mio::DampingLevel(13), mio::DampingType(3), mio::SimulationTime(2.0)); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-1e5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-0.5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(0.5 + 1e-32))), + MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1e5))), + MatrixNear((D1 + D2.array() - D1 * D2.array()).matrix())); } TEST(TestDampings, dampingsOnSameLevel) { - mio::Dampings> dampings(2); + mio::Dampings>> dampings(2); auto D1 = 0.25; auto D2 = (Eigen::MatrixXd(2, 2) << 0.0, 0.25, 0.5, 0.75).finished(); - dampings.add(D1, mio::DampingLevel(-2), mio::DampingType(0), mio::SimulationTime(0.5)); - dampings.add(D2, mio::DampingLevel(-2), mio::DampingType(1), mio::SimulationTime(2.0)); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-1e5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-0.5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(0.5 + 1e-32)), MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(1e5)), MatrixNear((D1 + D2.array()).matrix())); + dampings.add(D1, mio::DampingLevel(-2), mio::DampingType(0), mio::SimulationTime(0.5)); + dampings.add(D2, mio::DampingLevel(-2), mio::DampingType(1), mio::SimulationTime(2.0)); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-1e5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-0.5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(0.5 + 1e-32))), + MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1e5))), + MatrixNear((D1 + D2.array()).matrix())); } TEST(TestDampings, dampingsAtTheSameTime) { - mio::Dampings> dampings(2); + mio::Dampings>> dampings(2); auto D1 = 0.25; auto D2 = (Eigen::MatrixXd(2, 2) << 0.0, 0.25, 0.5, 0.75).finished(); - dampings.add(D1, mio::DampingLevel(-2), mio::DampingType(0), mio::SimulationTime(0.5)); - dampings.add(D2, mio::DampingLevel(-2), mio::DampingType(1), mio::SimulationTime(0.5)); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(-0.5)), MatrixNear(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(0.5 + 1e-5)), MatrixNear((D1 + D2.array()).matrix())); + dampings.add(D1, mio::DampingLevel(-2), mio::DampingType(0), mio::SimulationTime(0.5)); + dampings.add(D2, mio::DampingLevel(-2), mio::DampingType(1), mio::SimulationTime(0.5)); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-0.5))), + MatrixNear(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(0.5 + 1e-5))), + MatrixNear((D1 + D2.array()).matrix())); } TEST(TestDampings, dampingOfSameType) { - mio::Dampings> dampings(2); + mio::Dampings>> dampings(2); auto D1 = 0.25; auto D2 = (Eigen::MatrixXd(2, 2) << 0.0, 0.25, 0.5, 0.75).finished(); - dampings.add(D1, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(0.5)); - dampings.add(D2, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(2.0)); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-1e5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-0.5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(0.5 + 1e-32)), MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(1e5)), MatrixNear(D2)); + dampings.add(D1, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(0.5)); + dampings.add(D2, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(2.0)); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-1e5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-0.5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(0.5 + 1e-32))), + MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1e5))), MatrixNear(D2)); } TEST(TestDampings, dampingsNegative) { - mio::Dampings> dampings(2, 2); + mio::Dampings>> dampings(2, 2); auto D1 = -4.25; auto D2 = (Eigen::MatrixXd(2, 2) << 0.25, -0.5, 0.75, -1).finished(); - dampings.add(D1, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(0.5)); - dampings.add(D2, mio::DampingLevel(13), mio::DampingType(3), mio::SimulationTime(2.0)); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-1e5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_EQ(print_wrap(dampings.get_matrix_at(-0.5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(0.5 + 1e-32)), MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(1e5)), MatrixNear((D1 + D2.array() - D1 * D2.array()).matrix())); + dampings.add(D1, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(0.5)); + dampings.add(D2, mio::DampingLevel(13), mio::DampingType(3), mio::SimulationTime(2.0)); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-1e5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_EQ(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-0.5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(0.5 + 1e-32))), + MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1e5))), + MatrixNear((D1 + D2.array() - D1 * D2.array()).matrix())); } TEST(TestDampings, dampingsCombined) { - mio::Dampings> dampings(2); + mio::Dampings>> dampings(2); auto D1 = 0.25; auto D2 = (Eigen::MatrixXd(2, 2) << 0.1, 0.1, 0.1, 0.1).finished(); auto D3 = (Eigen::MatrixXd(2, 2) << 0.0, 0.25, 0.5, 0.75).finished(); auto D4 = 0.5; //add dampings out of order to check sorting - dampings.add(D2, mio::DampingLevel(7), mio::DampingType(2), mio::SimulationTime(0.0)); - dampings.add(D1, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(-2.0)); - dampings.add(D4, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(3.0)); - dampings.add(D3, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(1.5)); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(-1e5)), print_wrap(Eigen::MatrixXd::Zero(2, 2))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(-1.0)), MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(0.2)), MatrixNear((D1 + D2.array() - D1 * D2.array()).matrix())); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(2.0)), + dampings.add(D2, mio::DampingLevel(7), mio::DampingType(2), mio::SimulationTime(0.0)); + dampings.add(D1, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(-2.0)); + dampings.add(D4, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(3.0)); + dampings.add(D3, mio::DampingLevel(7), mio::DampingType(3), mio::SimulationTime(1.5)); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-1e5))), + print_wrap(Eigen::MatrixXd::Zero(2, 2))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-1.0))), + MatrixNear(Eigen::MatrixXd::Constant(2, 2, D1))); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(0.2))), + MatrixNear((D1 + D2.array() - D1 * D2.array()).matrix())); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(2.0))), MatrixNear((D1 + D2.array() + D3.array() - D1 * (D2 + D3).array()).matrix())); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(1e45)), + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1e45))), MatrixNear((D4 + D2.array() + D3.array() - D4 * (D2 + D3).array()).matrix())); } TEST(TestDampings, smoothTransitions) { - mio::Dampings> dampings(2); + mio::Dampings>> dampings(2); auto D1 = 0.25; auto D2 = (Eigen::VectorXd(2) << 0.1, 0.1).finished(); - dampings.add(D1, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(-2.0)); - dampings.add(D2, mio::DampingLevel(1), mio::DampingType(10), mio::SimulationTime(1.5)); + dampings.add(D1, mio::DampingLevel(123), mio::DampingType(5), mio::SimulationTime(-2.0)); + dampings.add(D2, mio::DampingLevel(1), mio::DampingType(10), mio::SimulationTime(1.5)); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(-2.5)), - MatrixNear((dampings.get_matrix_at(-3.) + dampings.get_matrix_at(-2.)) / 2)); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(1.0)), - MatrixNear((dampings.get_matrix_at(0.5) + dampings.get_matrix_at(1.5)) / 2)); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(-2.5))), + MatrixNear((dampings.get_matrix_at(mio::SimulationTime(-3.)) + + dampings.get_matrix_at(mio::SimulationTime(-2.))) / + 2)); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(1.0))), + MatrixNear((dampings.get_matrix_at(mio::SimulationTime(0.5)) + + dampings.get_matrix_at(mio::SimulationTime(1.5))) / + 2)); } TEST(TestDampings, automatic_cache_update) { - mio::Dampings> dampings(2); + mio::Dampings>> dampings(2); auto D1 = 0.25; dampings.set_automatic_cache_update(false); - dampings.add(D1, mio::DampingLevel(1), mio::DampingType(2), mio::SimulationTime(1.0)); + dampings.add(D1, mio::DampingLevel(1), mio::DampingType(2), mio::SimulationTime(1.0)); #ifndef NDEBUG - EXPECT_DEATH(dampings.get_matrix_at(2.0), "Cache is not current\\. Did you disable the automatic cache update\\?"); + EXPECT_DEATH(dampings.get_matrix_at(mio::SimulationTime(2.0)), + "Cache is not current\\. Did you disable the automatic cache update\\?"); #endif dampings.set_automatic_cache_update(true); - EXPECT_THAT(print_wrap(dampings.get_matrix_at(2.0)), MatrixNear((Eigen::VectorXd(2) << 0.25, 0.25).finished())); + EXPECT_THAT(print_wrap(dampings.get_matrix_at(mio::SimulationTime(2.0))), + MatrixNear((Eigen::VectorXd(2) << 0.25, 0.25).finished())); } diff --git a/cpp/tests/test_damping_sampling.cpp b/cpp/tests/test_damping_sampling.cpp index 24a304dc89..f347a9a16b 100644 --- a/cpp/tests/test_damping_sampling.cpp +++ b/cpp/tests/test_damping_sampling.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -30,32 +30,33 @@ TEST(TestDampingSampling, apply) std::vector>{mio::DampingSampling{mio::UncertainValue(0.5), mio::DampingLevel(0), mio::DampingType(0), - mio::SimulationTime(0.0), + mio::SimulationTime(0.0), {0}, Eigen::VectorXd::Constant(2, 1.0)}, mio::DampingSampling{mio::UncertainValue(0.25), mio::DampingLevel(1), mio::DampingType(0), - mio::SimulationTime(1.0), + mio::SimulationTime(1.0), { 0, 1, }, Eigen::VectorXd::Constant(2, 1.0)}}; - auto cmg = mio::ContactMatrixGroup(2, 2); + auto cmg = mio::ContactMatrixGroup(2, 2); mio::apply_dampings(cmg, ds, [](auto&& v) { return mio::make_contact_damping_matrix(v); }); - ASSERT_THAT(cmg[0].get_dampings(), - testing::ElementsAre(mio::SquareDamping(Eigen::MatrixXd::Constant(2, 2, 0.5), mio::DampingLevel(0), - mio::DampingType(0), mio::SimulationTime(0.0)), - mio::SquareDamping(Eigen::MatrixXd::Constant(2, 2, 0.25), mio::DampingLevel(1), - mio::DampingType(0), mio::SimulationTime(1.0)))); - ASSERT_THAT(cmg[1].get_dampings(), - testing::ElementsAre(mio::SquareDamping(Eigen::MatrixXd::Constant(2, 2, 0.25), mio::DampingLevel(1), - mio::DampingType(0), mio::SimulationTime(1.0)))); + ASSERT_THAT( + cmg[0].get_dampings(), + testing::ElementsAre(mio::SquareDamping(Eigen::MatrixXd::Constant(2, 2, 0.5), mio::DampingLevel(0), + mio::DampingType(0), mio::SimulationTime(0.0)), + mio::SquareDamping(Eigen::MatrixXd::Constant(2, 2, 0.25), mio::DampingLevel(1), + mio::DampingType(0), mio::SimulationTime(1.0)))); + ASSERT_THAT(cmg[1].get_dampings(), testing::ElementsAre(mio::SquareDamping( + Eigen::MatrixXd::Constant(2, 2, 0.25), mio::DampingLevel(1), + mio::DampingType(0), mio::SimulationTime(1.0)))); } TEST(TestDampingSampling, contactMask) @@ -67,7 +68,8 @@ TEST(TestDampingSampling, contactMask) TEST(TestDampingSampling, mobilityMask) { - auto m = mio::make_mobility_damping_vector(mio::ColumnVectorShape(6), (Eigen::VectorXd(2) << 0.5, 0.25).finished()) + auto m = mio::make_mobility_damping_vector(mio::ColumnVectorShape(6), + (Eigen::VectorXd(2) << 0.5, 0.25).finished()) .eval(); ASSERT_THAT(print_wrap(m), MatrixNear((Eigen::VectorXd(6) << 0.5, 0.5, 0.5, 0.25, 0.25, 0.25).finished())); } diff --git a/cpp/tests/test_data_dir.h.in b/cpp/tests/test_data_dir.h.in index d8da63cf0d..757a8bda67 100644 --- a/cpp/tests/test_data_dir.h.in +++ b/cpp/tests/test_data_dir.h.in @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/test_date.cpp b/cpp/tests/test_date.cpp index 2c350be264..6d442a3f9f 100644 --- a/cpp/tests/test_date.cpp +++ b/cpp/tests/test_date.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Martin J. Kuehn diff --git a/cpp/tests/test_dynamic_npis.cpp b/cpp/tests/test_dynamic_npis.cpp index 2b5c9a5a12..52e9503f58 100644 --- a/cpp/tests/test_dynamic_npis.cpp +++ b/cpp/tests/test_dynamic_npis.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -38,9 +38,9 @@ TEST(DynamicNPIs, set_threshold) { mio::DynamicNPIs npis; npis.set_threshold(0.5, {mio::DampingSampling(123.0, mio::DampingLevel(0), mio::DampingType(0), - mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); + mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); npis.set_threshold(1.0, {mio::DampingSampling(543.0, mio::DampingLevel(0), mio::DampingType(0), - mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); + mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); EXPECT_EQ(npis.get_thresholds()[0].first, 1.0); EXPECT_EQ(npis.get_thresholds()[0].second[0].get_value().value(), 543.0); @@ -52,9 +52,9 @@ TEST(DynamicNPIs, get_threshold) { mio::DynamicNPIs npis; npis.set_threshold(0.5, {mio::DampingSampling(0.5, mio::DampingLevel(0), mio::DampingType(0), - mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); + mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); npis.set_threshold(1.0, {mio::DampingSampling(0.5, mio::DampingLevel(0), mio::DampingType(0), - mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); + mio::SimulationTime(0.0), {}, Eigen::VectorXd(1))}); EXPECT_EQ(npis.get_max_exceeded_threshold(2.0), npis.get_thresholds().begin()); EXPECT_EQ(npis.get_max_exceeded_threshold(0.75), npis.get_thresholds().begin() + 1); @@ -65,71 +65,77 @@ TEST(DynamicNPIs, get_threshold) TEST(DynamicNPIs, get_damping_indices) { - using Damping = mio::Damping; - using DampingMatrixExpression = mio::DampingMatrixExpression>; + using Damping = mio::Damping>; + using DampingMatrixExpression = mio::DampingMatrixExpression>; DampingMatrixExpression dampexpr(1, 1); - dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3)); - dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.4)); - dampexpr.add_damping(0.0, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.6)); - dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5)); - dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7)); - dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9)); + dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3)); + dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.4)); + dampexpr.add_damping(0.0, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.6)); + dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5)); + dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7)); + dampexpr.add_damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9)); auto indices = - mio::get_damping_indices(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3), - mio::SimulationTime(0.9)); //time period is open interval! + mio::get_damping_indices(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3), + mio::SimulationTime(0.9)); //time period is open interval! EXPECT_THAT(indices, testing::ElementsAre(1, 4)); } TEST(DynamicNPIs, get_active_damping) { - using Damping = mio::Damping; - using DampingMatrixExpression = mio::DampingMatrixExpression>; + using Damping = mio::Damping>; + using DampingMatrixExpression = mio::DampingMatrixExpression>; DampingMatrixExpression dampexpr(1, 1); - dampexpr.add_damping(0.3, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3)); - dampexpr.add_damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.4)); - dampexpr.add_damping(0.6, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.6)); - dampexpr.add_damping(0.5, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5)); - dampexpr.add_damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7)); - dampexpr.add_damping(0.5, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9)); - - auto a = mio::get_active_damping(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6)); + dampexpr.add_damping(0.3, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3)); + dampexpr.add_damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.4)); + dampexpr.add_damping(0.6, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.6)); + dampexpr.add_damping(0.5, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5)); + dampexpr.add_damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7)); + dampexpr.add_damping(0.5, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9)); + + auto a = + mio::get_active_damping(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6)); EXPECT_EQ(print_wrap(a), print_wrap(Eigen::MatrixXd::Constant(1, 1, 0.4))); - auto b = mio::get_active_damping(dampexpr, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.6)); + auto b = + mio::get_active_damping(dampexpr, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.6)); EXPECT_EQ(print_wrap(b), print_wrap(Eigen::MatrixXd::Constant(1, 1, 0.6))); - auto c = mio::get_active_damping(dampexpr, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.4)); + auto c = + mio::get_active_damping(dampexpr, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.4)); EXPECT_EQ(print_wrap(c), print_wrap(Eigen::MatrixXd::Constant(1, 1, 0.0))); - auto d = mio::get_active_damping(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.8)); + auto d = + mio::get_active_damping(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.8)); EXPECT_EQ(print_wrap(d), print_wrap(Eigen::MatrixXd::Constant(1, 1, 0.4))); } TEST(DynamicNPIs, get_active_damping_empty) { - using Damping = mio::Damping; - using DampingMatrixExpression = mio::DampingMatrixExpression>; + using Damping = mio::Damping>; + using DampingMatrixExpression = mio::DampingMatrixExpression>; DampingMatrixExpression dampexpr(1, 1); - auto d = mio::get_active_damping(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6)); + auto d = + mio::get_active_damping(dampexpr, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6)); EXPECT_EQ(print_wrap(d), print_wrap(Eigen::MatrixXd::Constant(1, 1, 0.0))); } TEST(DynamicNPIs, implement_empty) { - using Damping = mio::Damping; - using DampingMatrixExpression = mio::DampingMatrixExpression>; - mio::DampingMatrixExpressionGroup dampexprs(2, 2); + using Damping = mio::Damping>; + using DampingMatrixExpression = mio::DampingMatrixExpression>; + mio::DampingMatrixExpressionGroup dampexprs(2, 2); auto make_mask = [](auto& g) { return g; }; - auto dynamic_npis = std::vector>({mio::DampingSampling( - 0.8, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0, 1}, Eigen::VectorXd::Ones(2))}); - mio::implement_dynamic_npis(dampexprs, dynamic_npis, mio::SimulationTime(0.45), mio::SimulationTime(0.6), - make_mask); + auto dynamic_npis = std::vector>( + {mio::DampingSampling(0.8, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), + {0, 1}, Eigen::VectorXd::Ones(2))}); + mio::implement_dynamic_npis(dampexprs, dynamic_npis, mio::SimulationTime(0.45), + mio::SimulationTime(0.6), make_mask); EXPECT_EQ(dampexprs[0].get_dampings().size(), 2); EXPECT_DOUBLE_EQ(dampexprs[0].get_dampings()[0].get_time().get(), 0.45); @@ -145,74 +151,83 @@ TEST(DynamicNPIs, implement_empty) TEST(DynamicNPIs, implement) { - using Damping = mio::Damping; - using DampingMatrixExpression = mio::DampingMatrixExpression>; - mio::DampingMatrixExpressionGroup dampexprs(2, 3, 1); + using Damping = mio::Damping>; + using DampingMatrixExpression = mio::DampingMatrixExpression>; + mio::DampingMatrixExpressionGroup dampexprs(2, 3, 1); auto make_mask = [](auto& g) { return (Eigen::MatrixXd(3, 1) << g(0, 0), g(1, 0), g(2, 0)).finished(); }; - dampexprs[0].add_damping(0.15, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3)); - dampexprs[0].add_damping(0.2, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.4)); - dampexprs[0].add_damping(0.25, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5)); - dampexprs[0].add_damping(0.35, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7)); + dampexprs[0].add_damping(0.15, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3)); + dampexprs[0].add_damping(0.2, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.4)); + dampexprs[0].add_damping(0.25, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5)); + dampexprs[0].add_damping(0.35, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7)); - dampexprs[1].add_damping(0.25, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.5)); - dampexprs[1].add_damping(0.45, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9)); + dampexprs[1].add_damping(0.25, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.5)); + dampexprs[1].add_damping(0.45, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9)); { auto dynamic_npis = std::vector>( - {mio::DampingSampling(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), - {0, 1}, Eigen::MatrixXd::Ones(3, 1))}); - mio::implement_dynamic_npis(dampexprs, dynamic_npis, mio::SimulationTime(0.45), mio::SimulationTime(0.6), - make_mask); + {mio::DampingSampling(0.4, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0), {0, 1}, Eigen::MatrixXd::Ones(3, 1))}); + mio::implement_dynamic_npis(dampexprs, dynamic_npis, mio::SimulationTime(0.45), + mio::SimulationTime(0.6), make_mask); } - EXPECT_THAT( - dampexprs[0].get_dampings(), - testing::ElementsAre( - Damping(0.15, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3), 3, 1), //before npi - Damping(0.2, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.4), 3, 1), //before npi - Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.45), 3, 1), //npi begins - Damping(0.25, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5), 3, 1), //other type - Damping(0.2, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6), 3, 1), //npi ends - Damping(0.35, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7), 3, 1))); //after npi - - EXPECT_THAT( - dampexprs[1].get_dampings(), - testing::ElementsAre( - Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.45), 3, 1), //npi begins - Damping(0.25, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.5), 3, 1), //other type/level - Damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6), 3, 1), //npi ends - Damping(0.45, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9), 3, 1))); //after npi + EXPECT_THAT(dampexprs[0].get_dampings(), + testing::ElementsAre(Damping(0.15, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.3), 3, 1), //before npi + Damping(0.2, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.4), 3, 1), //before npi + Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.45), 3, 1), //npi begins + Damping(0.25, mio::DampingLevel(0), mio::DampingType(1), + mio::SimulationTime(0.5), 3, 1), //other type + Damping(0.2, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.6), 3, 1), //npi ends + Damping(0.35, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.7), 3, 1))); //after npi + + EXPECT_THAT(dampexprs[1].get_dampings(), + testing::ElementsAre(Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.45), 3, 1), //npi begins + Damping(0.25, mio::DampingLevel(1), mio::DampingType(1), + mio::SimulationTime(0.5), 3, 1), //other type/level + Damping(0.0, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.6), 3, 1), //npi ends + Damping(0.45, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0.9), 3, 1))); //after npi { - auto dynamic_npis = std::vector>({mio::DampingSampling( - 0.3, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::MatrixXd::Ones(3, 1))}); - mio::implement_dynamic_npis(dampexprs, dynamic_npis, mio::SimulationTime(0.3), mio::SimulationTime(0.9), - make_mask); + auto dynamic_npis = std::vector>( + {mio::DampingSampling(0.3, mio::DampingLevel(0), mio::DampingType(0), + mio::SimulationTime(0), {0}, Eigen::MatrixXd::Ones(3, 1))}); + mio::implement_dynamic_npis(dampexprs, dynamic_npis, mio::SimulationTime(0.3), + mio::SimulationTime(0.9), make_mask); } EXPECT_THAT( dampexprs[0].get_dampings(), testing::ElementsAre( - Damping(0.3, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3), 3, 1), //new npi begins - Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.45), 3, + Damping(0.3, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.3), 3, + 1), //new npi begins + Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.45), 3, 1), //old npi begins, is kept because it's bigger - Damping(0.25, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5), 3, 1), //other type - Damping(0.3, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6), 3, + Damping(0.25, mio::DampingLevel(0), mio::DampingType(1), mio::SimulationTime(0.5), 3, + 1), //other type + Damping(0.3, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6), 3, 1), //old npi ends, down to value of new npi Damping( - 0.35, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7), 3, + 0.35, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.7), 3, 1))); //bigger than new npi, new npi ends at t = 0.9, but is already overwritten here by a higher value //second matrix not changed by the new npi - EXPECT_THAT( - dampexprs[1].get_dampings(), - testing::ElementsAre(Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.45), 3, 1), - Damping(0.25, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.5), 3, 1), - Damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6), 3, 1), - Damping(0.45, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9), 3, 1))); + EXPECT_THAT(dampexprs[1].get_dampings(), + testing::ElementsAre( + Damping(0.4, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.45), 3, 1), + Damping(0.25, mio::DampingLevel(1), mio::DampingType(1), mio::SimulationTime(0.5), 3, 1), + Damping(0.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.6), 3, 1), + Damping(0.45, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0.9), 3, 1))); } namespace mio_test @@ -249,14 +264,14 @@ struct DummySim { DummyModel model; }; -//overload required for dynamic NPIs +// free‐function overload required for dynamic NPIs (used in forwarding) template double get_infections_relative(const DummySim&, double, const Eigen::Ref& y) { return y[0] / y.sum(); } -//overload required for because the mock is not a compartment model simulation +// free‐function overload required because the mock is not a compartment‐model sim template void calculate_mobility_returns(Eigen::Ref::Vector>, const DummySim&, Eigen::Ref::Vector>, double, double) @@ -265,24 +280,44 @@ void calculate_mobility_returns(Eigen::Ref::Vector> } // namespace mio_test +//------------------------------------------------------------------------------+ +// Explicit specialization to resolve ambiguity of +// mio::get_infections_relative for Sim = NiceMock. +//------------------------------------------------------------------------------+ +namespace mio +{ +template <> +double get_infections_relative>( + const SimulationNode>& node, double t, + const Eigen::Ref>& y) +{ + // forward to test‐provided implementation + return mio_test::get_infections_relative(node.get_simulation(), t, y); +} +} // namespace mio + TEST(DynamicNPIs, mobility) { - mio::SimulationNode> node_from((Eigen::VectorXd(2) << 0.0, 1.0).finished()); - mio::SimulationNode> node_to((Eigen::VectorXd(2) << 0.0, 1.0).finished()); + mio::SimulationNode> node_from( + (Eigen::VectorXd(2) << 0.0, 1.0).finished()); + mio::SimulationNode> node_to( + (Eigen::VectorXd(2) << 0.0, 1.0).finished()); auto last_state_safe = (Eigen::VectorXd(2) << 0.01, 0.99).finished(); auto last_state_crit = (Eigen::VectorXd(2) << 0.02, 0.98).finished(); mio::DynamicNPIs npis; - npis.set_threshold( - 0.015 * 100'000, - {mio::DampingSampling{ - 1.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::VectorXd::Ones(2)}}); - npis.set_duration(mio::SimulationTime(5.0)); + npis.set_threshold(0.015 * 100'000, {mio::DampingSampling{1.0, + mio::DampingLevel(0), + mio::DampingType(0), + mio::SimulationTime(0), + {0}, + Eigen::VectorXd::Ones(2)}}); + npis.set_duration(mio::SimulationTime(5.0)); npis.set_base_value(100'000); - npis.set_interval(mio::SimulationTime(3.0)); + npis.set_interval(mio::SimulationTime(3.0)); - mio::MobilityCoefficientGroup coeffs(1, 2); + mio::MobilityCoefficientGroup coeffs(1, 2); mio::MobilityParameters parameters(coeffs); parameters.set_dynamic_npis_infected(npis); @@ -371,11 +406,13 @@ TEST(DynamicNPIs, secir_threshold_safe) model.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, 100.0); mio::DynamicNPIs npis; - npis.set_threshold( - 0.05 * 23'000, - {mio::DampingSampling{ - 1.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::VectorXd::Ones(1)}}); - npis.set_duration(mio::SimulationTime(5.0)); + npis.set_threshold(0.05 * 23'000, {mio::DampingSampling{1.0, + mio::DampingLevel(0), + mio::DampingType(0), + mio::SimulationTime(0), + {0}, + Eigen::VectorXd::Ones(1)}}); + npis.set_duration(mio::SimulationTime(5.0)); npis.set_base_value(23'000); model.parameters.get>() = npis; @@ -400,11 +437,13 @@ TEST(DynamicNPIs, secir_threshold_exceeded) model.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, 100); mio::DynamicNPIs npis; - npis.set_threshold( - 0.05 * 50'000, - {mio::DampingSampling{ - 1.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::VectorXd::Ones(1)}}); - npis.set_duration(mio::SimulationTime(5.0)); + npis.set_threshold(0.05 * 50'000, {mio::DampingSampling{1.0, + mio::DampingLevel(0), + mio::DampingType(0), + mio::SimulationTime(0), + {0}, + Eigen::VectorXd::Ones(1)}}); + npis.set_duration(mio::SimulationTime(5.0)); npis.set_base_value(50'000); model.parameters.get>() = npis; model.parameters.get>() = 0.0; @@ -429,15 +468,17 @@ TEST(DynamicNPIs, secir_delayed_implementation) model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = 10; model.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, 100); - mio::ContactMatrixGroup& cm = model.parameters.get>(); - cm[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 1.0)); + mio::ContactMatrixGroup& cm = model.parameters.get>(); + cm[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 1.0)); mio::DynamicNPIs npis; - npis.set_threshold( - 0.05 * 50'000, - {mio::DampingSampling{ - 0.5, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::VectorXd::Ones(1)}}); - npis.set_duration(mio::SimulationTime(5.0)); + npis.set_threshold(0.05 * 50'000, {mio::DampingSampling{0.5, + mio::DampingLevel(0), + mio::DampingType(0), + mio::SimulationTime(0), + {0}, + Eigen::VectorXd::Ones(1)}}); + npis.set_duration(mio::SimulationTime(5.0)); npis.set_base_value(50'000); model.parameters.get>() = npis; @@ -448,29 +489,29 @@ TEST(DynamicNPIs, secir_delayed_implementation) model.parameters.get>() = 3.0; mio::osecir::Simulation> sim(model, 0.0); sim.advance(3.0); - mio::ContactMatrixGroup const& contact_matrix = + mio::ContactMatrixGroup const& contact_matrix = sim.get_model().parameters.template get>(); - EXPECT_EQ(contact_matrix.get_matrix_at(2.0)(0, 0), 1.0); - EXPECT_EQ(contact_matrix.get_matrix_at(3.0)(0, 0), 0.5); + EXPECT_EQ(contact_matrix.get_matrix_at(mio::SimulationTime(2.0))(0, 0), 1.0); + EXPECT_EQ(contact_matrix.get_matrix_at(mio::SimulationTime(3.0))(0, 0), 0.5); // second simulation with t0 = 1.0, so the NPIs are implemented at tmax + delay = 6.0 const auto tmax = 4.0; model.parameters.get>() = 2.0; mio::osecir::Simulation> sim_2(model, 1.0); sim_2.advance(tmax); - mio::ContactMatrixGroup const& contact_matrix_sim_2 = + mio::ContactMatrixGroup const& contact_matrix_sim_2 = sim_2.get_model().parameters.template get>(); - EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(5.0)(0, 0), 1.0); - EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(6.0)(0, 0), 0.5); + EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(mio::SimulationTime(5.0))(0, 0), 1.0); + EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(mio::SimulationTime(6.0))(0, 0), 0.5); // third simulation with t0 = 1.0, so the NPIs are implemented at tmax + delay = 14.0 model.parameters.get>() = 10.0; mio::osecir::Simulation> sim_3(model, 1.0); sim_3.advance(4.0); - mio::ContactMatrixGroup const& contact_matrix_sim_3 = + mio::ContactMatrixGroup const& contact_matrix_sim_3 = sim_3.get_model().parameters.template get>(); - EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(13.0)(0, 0), 1.0); - EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(14.0)(0, 0), 0.5); + EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(mio::SimulationTime(13.0))(0, 0), 1.0); + EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(mio::SimulationTime(14.0))(0, 0), 0.5); } TEST(DynamicNPIs, secirvvs_threshold_safe) @@ -486,11 +527,13 @@ TEST(DynamicNPIs, secirvvs_threshold_safe) model.parameters.get>().array().setConstant(0); mio::DynamicNPIs npis; - npis.set_threshold( - 0.05 * 23'000, - {mio::DampingSampling{ - 1.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::VectorXd::Ones(1)}}); - npis.set_duration(mio::SimulationTime(5.0)); + npis.set_threshold(0.05 * 23'000, {mio::DampingSampling{1.0, + mio::DampingLevel(0), + mio::DampingType(0), + mio::SimulationTime(0), + {0}, + Eigen::VectorXd::Ones(1)}}); + npis.set_duration(mio::SimulationTime(5.0)); npis.set_base_value(23'000); model.parameters.get>() = npis; @@ -522,11 +565,13 @@ TEST(DynamicNPIs, secirvvs_threshold_exceeded) model.parameters.get>().array().setConstant(0); mio::DynamicNPIs npis; - npis.set_threshold( - 0.05 * 50'000, - {mio::DampingSampling{ - 1.0, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::VectorXd::Ones(1)}}); - npis.set_duration(mio::SimulationTime(5.0)); + npis.set_threshold(0.05 * 50'000, {mio::DampingSampling{1.0, + mio::DampingLevel(0), + mio::DampingType(0), + mio::SimulationTime(0), + {0}, + Eigen::VectorXd::Ones(1)}}); + npis.set_duration(mio::SimulationTime(5.0)); npis.set_base_value(50'000); model.parameters.get>() = npis; model.parameters.get>() = 0.0; @@ -558,15 +603,17 @@ TEST(DynamicNPIs, secirvvs_delayed_implementation) model.parameters.get>().resize(mio::SimulationDay(size_t(1000))); model.parameters.get>().array().setConstant(0); - mio::ContactMatrixGroup& cm = model.parameters.get>(); - cm[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 1.0)); + mio::ContactMatrixGroup& cm = model.parameters.get>(); + cm[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 1.0)); mio::DynamicNPIs npis; - npis.set_threshold( - 0.05 * 50'000, - {mio::DampingSampling{ - 0.5, mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(0), {0}, Eigen::VectorXd::Ones(1)}}); - npis.set_duration(mio::SimulationTime(5.0)); + npis.set_threshold(0.05 * 50'000, {mio::DampingSampling{0.5, + mio::DampingLevel(0), + mio::DampingType(0), + mio::SimulationTime(0), + {0}, + Eigen::VectorXd::Ones(1)}}); + npis.set_duration(mio::SimulationTime(5.0)); npis.set_base_value(50'000); model.parameters.get>() = npis; @@ -577,27 +624,27 @@ TEST(DynamicNPIs, secirvvs_delayed_implementation) // start with t0 = 0.0 mio::osecirvvs::Simulation> sim(model, 0.0); sim.advance(3.0); - mio::ContactMatrixGroup const& contact_matrix = + mio::ContactMatrixGroup const& contact_matrix = sim.get_model().parameters.template get>(); - EXPECT_EQ(contact_matrix.get_matrix_at(2.0)(0, 0), 1.0); - EXPECT_EQ(contact_matrix.get_matrix_at(3.0)(0, 0), 0.5); + EXPECT_EQ(contact_matrix.get_matrix_at(mio::SimulationTime(2.0))(0, 0), 1.0); + EXPECT_EQ(contact_matrix.get_matrix_at(mio::SimulationTime(3.0))(0, 0), 0.5); // second simulation with t0 = 1.0, so the NPIs are implemented at tmax + delay = 6.0 const auto tmax = 4.0; model.parameters.get>() = 2.0; mio::osecirvvs::Simulation> sim_2(model, 1.0); sim_2.advance(tmax); - mio::ContactMatrixGroup const& contact_matrix_sim_2 = + mio::ContactMatrixGroup const& contact_matrix_sim_2 = sim_2.get_model().parameters.template get>(); - EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(5.0)(0, 0), 1.0); - EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(6.0)(0, 0), 0.5); + EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(mio::SimulationTime(5.0))(0, 0), 1.0); + EXPECT_EQ(contact_matrix_sim_2.get_matrix_at(mio::SimulationTime(6.0))(0, 0), 0.5); // third simulation with t0 = 1.0, so the NPIs are implemented at tmax + delay = 14.0 model.parameters.get>() = 10.0; mio::osecirvvs::Simulation> sim_3(model, 1.0); sim_3.advance(4.0); - mio::ContactMatrixGroup const& contact_matrix_sim_3 = + mio::ContactMatrixGroup const& contact_matrix_sim_3 = sim_3.get_model().parameters.template get>(); - EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(13.0)(0, 0), 1.0); - EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(14.0)(0, 0), 0.5); + EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(mio::SimulationTime(13.0))(0, 0), 1.0); + EXPECT_EQ(contact_matrix_sim_3.get_matrix_at(mio::SimulationTime(14.0))(0, 0), 0.5); } diff --git a/cpp/tests/test_feedback.cpp b/cpp/tests/test_feedback.cpp index df41d78df3..d19b1b28b6 100644 --- a/cpp/tests/test_feedback.cpp +++ b/cpp/tests/test_feedback.cpp @@ -43,10 +43,10 @@ class TestFeedbackSimulation : public ::testing::Test model.parameters.set>(5.2); model.parameters.set>(2); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(2.7); - contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); + contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); // ICU compartment index icu_indices = {2}; diff --git a/cpp/tests/test_flows.cpp b/cpp/tests/test_flows.cpp index 563817294b..606c3e9a05 100755 --- a/cpp/tests/test_flows.cpp +++ b/cpp/tests/test_flows.cpp @@ -58,7 +58,7 @@ class TestModel : public mio::FlowModel(mio::AgeGroup(1))) { } void get_flows(Eigen::Ref /*pop*/, Eigen::Ref /*y*/, double /*t*/, @@ -156,7 +156,7 @@ TEST(TestFlows, CompareSimulations) model.parameters.set>(5.2); model.parameters.set>(6); model.parameters.set>(0.04); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(10); diff --git a/cpp/tests/test_glct_secir.cpp b/cpp/tests/test_glct_secir.cpp index 334f4aac41..0f135ff976 100755 --- a/cpp/tests/test_glct_secir.cpp +++ b/cpp/tests/test_glct_secir.cpp @@ -37,7 +37,7 @@ TEST(TestGLCTSecir, testEvalRightHandSide) // Define initial values, parameters and numbers of subcompartments according to the choices of the // testEvalRightHandSide of the LCT testing suite. For more details, // we refer to the example glct_secir.cpp. - using Model = mio::glsecir::Model<2, 6, 4, 4, 4>; + using Model = mio::glsecir::Model; using LctState = Model::LctState; using InfectionState = LctState::InfectionState; @@ -46,11 +46,11 @@ TEST(TestGLCTSecir, testEvalRightHandSide) // Set parameters such that the stay times are Erlang-distributed as in the corresponding LCT model. // Exposed. // Default functions are used to set the parameters but the corresponding dimensions have to be set manually. - model.parameters.get() = - mio::glsecir::StartingProbabilitiesExposed().get_default( + model.parameters.get>() = + mio::glsecir::StartingProbabilitiesExposed().get_default( LctState::get_num_subcompartments()); - model.parameters.get() = - mio::glsecir::TransitionMatrixExposedToInfectedNoSymptoms().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixExposedToInfectedNoSymptoms().get_default( LctState::get_num_subcompartments(), 3.2); // InfectedNoSymptoms. Eigen::VectorX StartingProbabilitiesInfectedNoSymptoms = Eigen::VectorX::Zero( @@ -58,13 +58,13 @@ TEST(TestGLCTSecir, testEvalRightHandSide) StartingProbabilitiesInfectedNoSymptoms[0] = 1 - 0.09; StartingProbabilitiesInfectedNoSymptoms[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 0.09; - model.parameters.get() = + model.parameters.get>() = StartingProbabilitiesInfectedNoSymptoms; - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedNoSymptomsToInfectedSymptoms().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedNoSymptomsToInfectedSymptoms().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 2.); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedNoSymptomsToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedNoSymptomsToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 2.); // InfectedSymptoms. Eigen::VectorX StartingProbabilitiesInfectedSymptoms = Eigen::VectorX::Zero( @@ -72,12 +72,13 @@ TEST(TestGLCTSecir, testEvalRightHandSide) StartingProbabilitiesInfectedSymptoms[0] = 0.2; StartingProbabilitiesInfectedSymptoms[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - 0.2; - model.parameters.get() = StartingProbabilitiesInfectedSymptoms; - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSymptomsToInfectedSevere().get_default( + model.parameters.get>() = + StartingProbabilitiesInfectedSymptoms; + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSymptomsToInfectedSevere().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 5.8); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSymptomsToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSymptomsToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 5.8); // InfectedSevere. Eigen::VectorX StartingProbabilitiesInfectedSevere = Eigen::VectorX::Zero( @@ -85,12 +86,13 @@ TEST(TestGLCTSecir, testEvalRightHandSide) StartingProbabilitiesInfectedSevere[0] = 0.25; StartingProbabilitiesInfectedSevere[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - 0.25; - model.parameters.get() = StartingProbabilitiesInfectedSevere; - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSevereToInfectedCritical().get_default( + model.parameters.get>() = + StartingProbabilitiesInfectedSevere; + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSevereToInfectedCritical().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 9.5); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedSevereToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSevereToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 9.5); // InfectedCritical. Eigen::VectorX StartingProbabilitiesInfectedCritical = Eigen::VectorX::Zero( @@ -98,23 +100,25 @@ TEST(TestGLCTSecir, testEvalRightHandSide) StartingProbabilitiesInfectedCritical[0] = 0.3; StartingProbabilitiesInfectedCritical[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - 0.3; - model.parameters.get() = StartingProbabilitiesInfectedCritical; - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedCriticalToDead().get_default( + model.parameters.get>() = + StartingProbabilitiesInfectedCritical; + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedCriticalToDead().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 7.1); - model.parameters.get() = - mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( + model.parameters.get>() = + mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 7.1); - model.parameters.get() = 0.05; + model.parameters.get>() = 0.05; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); - model.parameters.get() = 0.7; - model.parameters.get() = 0.25; - model.parameters.get() = 0.; - model.parameters.get() = 0; + model.parameters.get>() = 0.7; + model.parameters.get>() = 0.25; + model.parameters.get>() = 0.; + model.parameters.get>() = 0; // Define initial population distribution in infection states, one entry per subcompartment. std::vector> initial_populations = { @@ -158,7 +162,7 @@ TEST(TestGLCTSecir, testEvalRightHandSide) class ModelTestGLCTSecir : public testing::Test { public: - using Model = mio::glsecir::Model<2, 6, 2, 2, 10>; + using Model = mio::glsecir::Model; using LctState = Model::LctState; using InfectionState = LctState::InfectionState; @@ -168,11 +172,11 @@ class ModelTestGLCTSecir : public testing::Test model = new Model(); // --- Set parameters. --- // Exposed. - model->parameters.get() = - mio::glsecir::StartingProbabilitiesExposed().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesExposed().get_default( LctState::get_num_subcompartments()); - model->parameters.get() = - mio::glsecir::TransitionMatrixExposedToInfectedNoSymptoms().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixExposedToInfectedNoSymptoms().get_default( LctState::get_num_subcompartments(), 3.2); // InfectedNoSymptoms. Eigen::VectorX StartingProbabilitiesInfectedNoSymptoms = Eigen::VectorX::Zero( @@ -180,13 +184,13 @@ class ModelTestGLCTSecir : public testing::Test StartingProbabilitiesInfectedNoSymptoms[0] = 1 - 0.09; StartingProbabilitiesInfectedNoSymptoms[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 0.09; - model->parameters.get() = + model->parameters.get>() = StartingProbabilitiesInfectedNoSymptoms; - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedNoSymptomsToInfectedSymptoms().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedNoSymptomsToInfectedSymptoms().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 2.); - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedNoSymptomsToRecovered().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedNoSymptomsToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 2.); // InfectedSymptoms. Eigen::VectorX StartingProbabilitiesInfectedSymptoms = Eigen::VectorX::Zero( @@ -194,13 +198,13 @@ class ModelTestGLCTSecir : public testing::Test StartingProbabilitiesInfectedSymptoms[0] = 0.2; StartingProbabilitiesInfectedSymptoms[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - 0.2; - model->parameters.get() = + model->parameters.get>() = StartingProbabilitiesInfectedSymptoms; - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedSymptomsToInfectedSevere().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSymptomsToInfectedSevere().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 5.8); - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedSymptomsToRecovered().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSymptomsToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 5.8); // InfectedSevere. Eigen::VectorX StartingProbabilitiesInfectedSevere = Eigen::VectorX::Zero( @@ -208,13 +212,13 @@ class ModelTestGLCTSecir : public testing::Test StartingProbabilitiesInfectedSevere[0] = 0.25; StartingProbabilitiesInfectedSevere[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - 0.25; - model->parameters.get() = + model->parameters.get>() = StartingProbabilitiesInfectedSevere; - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedSevereToInfectedCritical().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSevereToInfectedCritical().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 9.5); - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedSevereToRecovered().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedSevereToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 9.5); // InfectedCritical. Eigen::VectorX StartingProbabilitiesInfectedCritical = Eigen::VectorX::Zero( @@ -222,25 +226,26 @@ class ModelTestGLCTSecir : public testing::Test StartingProbabilitiesInfectedCritical[0] = 0.3; StartingProbabilitiesInfectedCritical[(Eigen::Index)( LctState::get_num_subcompartments() / 2.)] = 1 - 0.3; - model->parameters.get() = + model->parameters.get>() = StartingProbabilitiesInfectedCritical; - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedCriticalToDead().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedCriticalToDead().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 7.1); - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 7.1); - model->parameters.get() = 0.05; + model->parameters.get>() = 0.05; - mio::ContactMatrixGroup& contact_matrix = model->parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(2.)); + mio::ContactMatrixGroup& contact_matrix = + model->parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(2.)); - model->parameters.get() = 0.7; - model->parameters.get() = 0.25; - model->parameters.get() = 0.; - model->parameters.get() = 0; + model->parameters.get>() = 0.7; + model->parameters.get>() = 0.25; + model->parameters.get>() = 0.; + model->parameters.get>() = 0; // --- Define initial distribution of the population in the subcompartments. --- std::vector> initial_populations = { @@ -317,39 +322,39 @@ TEST_F(ModelTestGLCTSecir, testConstraintsModel) Eigen::VectorX wrong_size = Eigen::VectorX::Zero(3); wrong_size[0] = 1; // Exposed. - model->parameters.get() = wrong_size; - constraint_check = model->check_constraints(); + model->parameters.get>() = wrong_size; + constraint_check = model->check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesExposed().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesExposed().get_default( LctState::get_num_subcompartments()); // InfectedNoSymptoms. - model->parameters.get() = wrong_size; - constraint_check = model->check_constraints(); + model->parameters.get>() = wrong_size; + constraint_check = model->check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedNoSymptoms().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedNoSymptoms().get_default( LctState::get_num_subcompartments()); // InfectedSymptoms. - model->parameters.get() = wrong_size; - constraint_check = model->check_constraints(); + model->parameters.get>() = wrong_size; + constraint_check = model->check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedSymptoms().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedSymptoms().get_default( LctState::get_num_subcompartments()); // InfectedSevere. - model->parameters.get() = wrong_size; - constraint_check = model->check_constraints(); + model->parameters.get>() = wrong_size; + constraint_check = model->check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedSevere().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedSevere().get_default( LctState::get_num_subcompartments()); // InfectedCritical. - model->parameters.get() = wrong_size; - constraint_check = model->check_constraints(); + model->parameters.get>() = wrong_size; + constraint_check = model->check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedCritical().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedCritical().get_default( LctState::get_num_subcompartments()); } @@ -365,135 +370,136 @@ TEST_F(ModelTestGLCTSecir, testConstraintsParameters) // --- Parameters affecting the transmission of the virus. --- // Check TransmissionProbabilityOnContact. - model->parameters.get() = 5.1; - constraint_check = model->parameters.check_constraints(); + model->parameters.get>() = 5.1; + constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = 0.05; + model->parameters.get>() = 0.05; // Check RelativeTransmissionNoSymptoms. - model->parameters.get() = -0.05; - constraint_check = model->parameters.check_constraints(); + model->parameters.get>() = -0.05; + constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = 0.7; + model->parameters.get>() = 0.7; // Check RiskOfInfectionFromSymptomatic. - model->parameters.get() = 1.1; - constraint_check = model->parameters.check_constraints(); + model->parameters.get>() = 1.1; + constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = 0.25; + model->parameters.get>() = 0.25; // Check Seasonality. - model->parameters.get() = 0.6; - constraint_check = model->parameters.check_constraints(); + model->parameters.get>() = 0.6; + constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = 0.; + model->parameters.get>() = 0.; // --- Check with incorrect dimensions. --- // Check non-quadratic TransitionMatrixInfectedCriticalToRecovered. - model->parameters.get() = Eigen::MatrixXd::Zero(2, 3); + model->parameters.get>() = + Eigen::MatrixX::Zero(2, 3); constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( + model->parameters.get>() = + mio::glsecir::TransitionMatrixInfectedCriticalToRecovered().get_default( (size_t)(LctState::get_num_subcompartments() / 2.), 7.1); // Check non matching dimensions of TransitionMatrix and vector with StartingProbabilities. Eigen::VectorX wrong_size = Eigen::VectorX::Zero(3); wrong_size[0] = 1; // Exposed. - model->parameters.get() = wrong_size; - constraint_check = model->parameters.check_constraints(); + model->parameters.get>() = wrong_size; + constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesExposed().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesExposed().get_default( LctState::get_num_subcompartments()); // InfectedNoSymptoms. - model->parameters.get() = wrong_size; + model->parameters.get>() = wrong_size; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedNoSymptoms().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedNoSymptoms().get_default( LctState::get_num_subcompartments()); // InfectedSymptoms. - model->parameters.get() = wrong_size; + model->parameters.get>() = wrong_size; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedSymptoms().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedSymptoms().get_default( LctState::get_num_subcompartments()); // InfectedSevere. - model->parameters.get() = wrong_size; - constraint_check = model->parameters.check_constraints(); + model->parameters.get>() = wrong_size; + constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedSevere().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedSevere().get_default( LctState::get_num_subcompartments()); // InfectedCritical. - model->parameters.get() = wrong_size; + model->parameters.get>() = wrong_size; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get() = - mio::glsecir::StartingProbabilitiesInfectedCritical().get_default( + model->parameters.get>() = + mio::glsecir::StartingProbabilitiesInfectedCritical().get_default( LctState::get_num_subcompartments()); // --- Check constraints of the starting probability vectors. --- - model->parameters.get()[1] = 1.5; + model->parameters.get>()[1] = 1.5; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()[0] = 1.1; - model->parameters.get()[1] = -0.1; + model->parameters.get>()[0] = 1.1; + model->parameters.get>()[1] = -0.1; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()[0] = 1.; - model->parameters.get()[1] = 0.; + model->parameters.get>()[0] = 1.; + model->parameters.get>()[1] = 0.; // --- Check with invalid transition matrices. --- // ExposedToInfectedNoSymptoms. - model->parameters.get()(1, 0) = 10; + model->parameters.get>()(1, 0) = 10; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(1, 0) = 0.1; + model->parameters.get>()(1, 0) = 0.1; // InfectedNoSymptomsToInfectedSymptoms. - model->parameters.get()(2, 1) = 50; + model->parameters.get>()(2, 1) = 50; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(2, 1) = 0.1; + model->parameters.get>()(2, 1) = 0.1; // InfectedNoSymptomsToRecovered. - model->parameters.get()(1, 1) = -1.45; + model->parameters.get>()(1, 1) = -1.45; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(1, 1) = -1.5; + model->parameters.get>()(1, 1) = -1.5; // InfectedSymptomsToInfectedSevere. - model->parameters.get()(0, 0) = 1.; + model->parameters.get>()(0, 0) = 1.; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(0, 0) = -1.; + model->parameters.get>()(0, 0) = -1.; // InfectedSymptomsToRecovered. - model->parameters.get()(0, 0) = 0.1; + model->parameters.get>()(0, 0) = 0.1; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(0, 0) = -0.1; + model->parameters.get>()(0, 0) = -0.1; // InfectedSevereToInfectedCritical. - model->parameters.get()(0, 0) = 0.01; + model->parameters.get>()(0, 0) = 0.01; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(0, 0) = -0.01; + model->parameters.get>()(0, 0) = -0.01; // InfectedSevereToRecovered. - model->parameters.get()(0, 0) = 50; + model->parameters.get>()(0, 0) = 50; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(0, 0) = -0.1; + model->parameters.get>()(0, 0) = -0.1; // InfectedCriticalToDead. - model->parameters.get()(3, 1) = 6; + model->parameters.get>()(3, 1) = 6; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(3, 1) = 0.; + model->parameters.get>()(3, 1) = 0.; // InfectedCriticalToRecovered. - model->parameters.get()(0, 4) = 3; + model->parameters.get>()(0, 4) = 3; constraint_check = model->parameters.check_constraints(); EXPECT_TRUE(constraint_check); - model->parameters.get()(0, 0) = -3.; - model->parameters.get()(0, 4) = 1.2; + model->parameters.get>()(0, 0) = -3.; + model->parameters.get>()(0, 4) = 1.2; // --- Check with correct parameters. --- constraint_check = model->parameters.check_constraints(); diff --git a/cpp/tests/test_graph.cpp b/cpp/tests/test_graph.cpp index 666d3068ed..65bdb71c42 100644 --- a/cpp/tests/test_graph.cpp +++ b/cpp/tests/test_graph.cpp @@ -170,10 +170,10 @@ TEST(TestGraph, set_nodes_secir) const fs::path& dir = " "; auto result = - mio::set_nodes, mio::osecir::ContactPatterns, + mio::set_nodes, mio::osecir::ContactPatterns, mio::osecir::Model, mio::MobilityParameters, mio::osecir::Parameters, decltype(read_function_nodes), decltype(node_id_function)>( - params, mio::Date(2020, 5, 10), mio::Date(2020, 5, 11), dir, " ", 0, params_graph, read_function_nodes, + params, mio::Date(2020, 5, 10), mio::Date(2020, 5, 11), dir, " ", false, params_graph, read_function_nodes, node_id_function, std::vector(size_t(1), 1.0), 1.0, 0.01); EXPECT_EQ(params_graph.nodes().size(), 2); @@ -196,10 +196,10 @@ TEST(TestGraph, set_nodes_secirvvs) const fs::path& dir = " "; auto result = - mio::set_nodes, mio::osecirvvs::ContactPatterns, + mio::set_nodes, mio::osecirvvs::ContactPatterns, mio::osecirvvs::Model, mio::MobilityParameters, mio::osecirvvs::Parameters, decltype(read_function_nodes), decltype(node_id_function)>( - params, mio::Date(2020, 5, 10), mio::Date(2020, 5, 11), dir, " ", 0, params_graph, read_function_nodes, + params, mio::Date(2020, 5, 10), mio::Date(2020, 5, 11), dir, " ", false, params_graph, read_function_nodes, node_id_function, std::vector(size_t(1), 1.0), 1.0, 0.01); EXPECT_EQ(params_graph.nodes().size(), 2); @@ -228,11 +228,11 @@ TEST(TestGraph, set_edges) const auto& read_function_edges = mock_read_mobility; - auto result = - mio::set_edges, mio::MobilityParameters, - mio::MobilityCoefficientGroup, mio::osecir::InfectionState, decltype(read_function_edges)>( - dir, params_graph, mobile_compartments, size_t(2), read_function_edges, - std::vector{0., 0., 1.0, 1.0, 0.33, 0., 0.}); + auto result = mio::set_edges, + mio::MobilityParameters, mio::MobilityCoefficientGroup, + mio::osecir::InfectionState, decltype(read_function_edges)>( + dir, params_graph, mobile_compartments, size_t(2), read_function_edges, + std::vector{0., 0., 1.0, 1.0, 0.33, 0., 0.}); auto e_work = (Eigen::ArrayXd(6 * Eigen::Index(mio::osecir::InfectionState::Count)) << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 2, 0, 0, 0, 2, 0, 2, 2, 2, 0, 2, 0, 0, 0, 2, 0, 0.66, 0.66, @@ -274,7 +274,7 @@ TEST(TestGraph, set_edges) TEST(TestGraph, set_edges_saving_edges) { const size_t num_groups = 6; - mio::osecir::Model model(num_groups); + mio::osecir::Model model(num_groups); model.populations[{mio::AgeGroup(3), mio::osecir::InfectionState::Exposed}] = 1; mio::Graph, mio::MobilityParameters> params_graph; TempFileRegister file_register; @@ -310,11 +310,11 @@ TEST(TestGraph, set_edges_saving_edges) const auto& read_function_edges = mock_read_mobility; - auto result = - mio::set_edges, mio::MobilityParameters, - mio::MobilityCoefficientGroup, mio::osecir::InfectionState, decltype(read_function_edges)>( - dir, params_graph, mobile_compartments, size_t(2), read_function_edges, - std::vector{0., 0., 1.0, 1.0, 0.33, 0., 0.}, indices_save_edges); + auto result = mio::set_edges, + mio::MobilityParameters, mio::MobilityCoefficientGroup, + mio::osecir::InfectionState, decltype(read_function_edges)>( + dir, params_graph, mobile_compartments, size_t(2), read_function_edges, + std::vector{0., 0., 1.0, 1.0, 0.33, 0., 0.}, indices_save_edges); EXPECT_EQ(params_graph.edges().size(), 2); @@ -349,10 +349,10 @@ namespace struct MoveOnly { MoveOnly(); - MoveOnly(const MoveOnly&) = delete; + MoveOnly(const MoveOnly&) = delete; MoveOnly& operator=(const MoveOnly&) = delete; MoveOnly(MoveOnly&&) = default; - MoveOnly& operator=(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; }; using MoveOnlyGraph = mio::Graph; diff --git a/cpp/tests/test_graph_simulation.cpp b/cpp/tests/test_graph_simulation.cpp index 46c7074e8c..ea06ff6f54 100644 --- a/cpp/tests/test_graph_simulation.cpp +++ b/cpp/tests/test_graph_simulation.cpp @@ -93,7 +93,7 @@ TEST(TestGraphSimulation, simulate) EXPECT_CALL(edge_func, invoke(3, 1, Eq(1), Eq(1), Eq(2))).Times(1).After(node_func_calls); EXPECT_CALL(edge_func, invoke(3, 1, Eq(3), Eq(3), Eq(0))).Times(1).After(node_func_calls); - auto sim = mio::make_graph_sim( + auto sim = mio::make_graph_sim( t0, dt, g, [&node_func](auto&& t, auto&& dt_, auto&& n) { node_func(t, dt_, n); @@ -121,7 +121,7 @@ TEST(TestGraphSimulation, stopsAtTmax) const auto tmax = 3.123; const auto dt = 0.076; - auto sim = mio::make_graph_sim( + auto sim = mio::make_graph_sim( t0, dt, g, [](auto&&, auto&&, auto&&) {}, [](auto&&, auto&&, auto&&, auto&&, auto&&) {}); sim.advance(tmax); @@ -143,7 +143,9 @@ TEST(TestGraphSimulation, stopsAtTmaxStochastic) model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] = 0.1; model.populations.set_total(1000); - mio::Graph>>, mio::MobilityEdgeStochastic> g; + mio::Graph>>, + mio::MobilityEdgeStochastic> + g; g.add_node(0, model, t0); g.add_node(1, model, t0); g.add_edge(0, 1, Eigen::VectorXd::Constant(4, 0.001)); @@ -175,7 +177,7 @@ TEST(TestGraphSimulation, persistentChangesDuringSimulation) auto t0 = 0; auto dt = 1; - auto sim = mio::make_graph_sim(t0, dt, g, node_func, edge_func); + auto sim = mio::make_graph_sim(t0, dt, g, node_func, edge_func); int num_steps = 2; sim.advance(t0 + num_steps * dt); @@ -199,7 +201,9 @@ TEST(TestGraphSimulation, consistencyStochasticMobility) model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] = 0.3; model.populations.set_total(1000); - mio::Graph>>, mio::MobilityEdgeStochastic> g; + mio::Graph>>, + mio::MobilityEdgeStochastic> + g; g.add_node(0, model, t0); g.add_node(1, model, t0); g.add_edge(0, 1, Eigen::VectorXd::Constant(4, 0.001)); @@ -250,8 +254,8 @@ TEST(TestGraphSimulation, consistencyStochasticMobility) } template -mio::GraphSimulation create_simulation(Graph&& g, mio::oseir::Model& model, double t0, double tmax, - double dt) +mio::GraphSimulation create_simulation(Graph&& g, mio::oseir::Model& model, + double t0, double tmax, double dt) { g.add_node(0, model, t0); g.add_node(1, model, t0); @@ -264,7 +268,7 @@ mio::GraphSimulation create_simulation(Graph&& g, mio::oseir::Model(t0, dt, std::move(g)); sim.advance(tmax); @@ -289,21 +293,21 @@ TEST(TestGraphSimulation, consistencyFlowMobility) model.parameters.set>(5.2); model.parameters.set>(6); model.parameters.set>(0.04); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(10); model.check_constraints(); auto sim_no_flows = - create_simulation(mio::Graph>>, + create_simulation(mio::Graph>>, mio::MobilityEdge>(), model, t0, tmax, dt); - auto sim_flows = - create_simulation(mio::Graph>>, - mio::MobilityEdge>(), - model, t0, tmax, dt); + auto sim_flows = create_simulation( + mio::Graph>>, + mio::MobilityEdge>(), + model, t0, tmax, dt); //test if all results of both simulations are equal for all nodes for (size_t node_id = 0; node_id < sim_no_flows.get_graph().nodes().size(); ++node_id) { @@ -339,7 +343,7 @@ TEST(TestGraphSimulation, feedbackSimulation) using Model = mio::oseir::Model; using Simulation = mio::Simulation; using FeedbackSim = mio::FeedbackSimulation>; - using Node = mio::SimulationNode; + using Node = mio::SimulationNode; using Edge = mio::MobilityEdge; using Graph = mio::Graph; using GraphFeedback = mio::FeedbackGraphSimulation; @@ -406,13 +410,13 @@ namespace struct MoveOnly { MoveOnly(); - MoveOnly(const MoveOnly&) = delete; + MoveOnly(const MoveOnly&) = delete; MoveOnly& operator=(const MoveOnly&) = delete; MoveOnly(MoveOnly&&) = default; - MoveOnly& operator=(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; }; using MoveOnlyGraph = mio::Graph; -using MoveOnlyGraphSim = mio::GraphSimulation; +using MoveOnlyGraphSim = mio::GraphSimulation; } // namespace diff --git a/cpp/tests/test_history.cpp b/cpp/tests/test_history.cpp index 901f24654b..977d00cc93 100644 --- a/cpp/tests/test_history.cpp +++ b/cpp/tests/test_history.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding, Sascha Korf diff --git a/cpp/tests/test_ide_parameters_io.cpp b/cpp/tests/test_ide_parameters_io.cpp index 2ff4deaf43..52167a7105 100644 --- a/cpp/tests/test_ide_parameters_io.cpp +++ b/cpp/tests/test_ide_parameters_io.cpp @@ -64,29 +64,31 @@ TEST(TestIDEParametersIo, RKIcompareWithPreviousRun) // Set the model parameters so that if the default values are changed, the test is still valid. mio::SmootherCosine smoothcos(2.0); mio::StateAgeFunctionWrapper delaydistribution(smoothcos); - std::vector vec_delaydistrib((int)mio::isecir::InfectionTransition::Count, - delaydistribution); + std::vector> vec_delaydistrib((int)mio::isecir::InfectionTransition::Count, + delaydistribution); vec_delaydistrib[(int)mio::isecir::InfectionTransition::InfectedSymptomsToInfectedSevere] .set_distribution_parameter(1.7); - model.parameters.get()[group] = vec_delaydistrib; + model.parameters.template get()[group] = vec_delaydistrib; std::vector vec_prob((int)mio::isecir::InfectionTransition::Count, 0.5); vec_prob[Eigen::Index(mio::isecir::InfectionTransition::SusceptibleToExposed)] = 1; vec_prob[Eigen::Index(mio::isecir::InfectionTransition::ExposedToInfectedNoSymptoms)] = 1; - model.parameters.get()[group] = vec_prob; + model.parameters.template get()[group] = vec_prob; - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 10.)); - model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 10.)); + model.parameters.template get() = + mio::UncertainContactMatrix(contact_matrix); - mio::ConstantFunction constfunc(1.0); - mio::StateAgeFunctionWrapper prob(constfunc); + mio::ConstantFunction constfunc(1.0); + mio::StateAgeFunctionWrapper prob(constfunc); - model.parameters.get()[group] = prob; - model.parameters.get()[group] = prob; - model.parameters.get()[group] = prob; + model.parameters.template get()[group] = prob; + model.parameters.template get()[group] = prob; + model.parameters.template get()[group] = prob; mio::CustomIndexArray scale_confirmed_cases = mio::CustomIndexArray(mio::AgeGroup(num_agegroups), 1.); @@ -144,33 +146,35 @@ TEST(TestIDEParametersIo, RKIcompareWithPreviousRunAgeRes) num_agegroups); // Set the model parameters so that if the default values are changed, the test is still valid. - mio::SmootherCosine smoothcos(2.0); - mio::StateAgeFunctionWrapper delaydistribution(smoothcos); - std::vector vec_delaydistrib((int)mio::isecir::InfectionTransition::Count, - delaydistribution); + mio::SmootherCosine smoothcos(2.0); + mio::StateAgeFunctionWrapper delaydistribution(smoothcos); + std::vector> vec_delaydistrib((int)mio::isecir::InfectionTransition::Count, + delaydistribution); vec_delaydistrib[(int)mio::isecir::InfectionTransition::InfectedSymptomsToInfectedSevere] .set_distribution_parameter(1.7); for (mio::AgeGroup group = mio::AgeGroup(0); group < mio::AgeGroup(num_agegroups); ++group) { - model.parameters.get()[group] = vec_delaydistrib; + model.parameters.template get()[group] = vec_delaydistrib; } std::vector vec_prob((int)mio::isecir::InfectionTransition::Count, 0.5); vec_prob[Eigen::Index(mio::isecir::InfectionTransition::SusceptibleToExposed)] = 1; vec_prob[Eigen::Index(mio::isecir::InfectionTransition::ExposedToInfectedNoSymptoms)] = 1; for (auto group = mio::AgeGroup(0); group < mio::AgeGroup(num_agegroups); ++group) { - model.parameters.get()[group] = vec_prob; + model.parameters.template get()[group] = vec_prob; } - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 10.)); - model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 10.)); + model.parameters.template get() = + mio::UncertainContactMatrix(contact_matrix); - mio::ConstantFunction constfunc(1.0); - mio::StateAgeFunctionWrapper prob(constfunc); + mio::ConstantFunction constfunc(1.0); + mio::StateAgeFunctionWrapper prob(constfunc); for (auto group = mio::AgeGroup(0); group < mio::AgeGroup(num_agegroups); ++group) { - model.parameters.get()[group] = prob; - model.parameters.get()[group] = prob; - model.parameters.get()[group] = prob; + model.parameters.template get()[group] = prob; + model.parameters.template get()[group] = prob; + model.parameters.template get()[group] = prob; } mio::CustomIndexArray scale_confirmed_cases = @@ -263,14 +267,14 @@ TEST(TestIDEParametersIo, ParametersIoRKIFailure) // --- Case where not all needed dates from the future are provided. start_date = mio::Date(2020, 06, 07); status = mio::isecir::set_initial_flows(model, dt, test_data, start_date, - scale_confirmed_cases); + scale_confirmed_cases); ASSERT_THAT(print_wrap(status), IsFailure(mio::StatusCode::OutOfRange)); // --- Case where not all needed dates from the past are provided. start_date = mio::Date(2020, 05, 24); status = mio::isecir::set_initial_flows(model, dt, test_data, start_date, - scale_confirmed_cases); + scale_confirmed_cases); // Check that status is Success as just a warning is logged. ASSERT_THAT(print_wrap(status), IsSuccess()); // Check that the flow InfectedNoSymptomsToInfectedSymptoms has actually been set to 0. @@ -289,7 +293,7 @@ TEST(TestIDEParametersIo, ParametersIoRKIFailure) // --- Valid case. start_date = mio::Date(2020, 06, 02); status = mio::isecir::set_initial_flows(model, dt, test_data, start_date, - scale_confirmed_cases); + scale_confirmed_cases); ASSERT_THAT(print_wrap(status), IsSuccess()); } @@ -337,14 +341,14 @@ TEST(TestIDEParametersIo, ParametersIoRKIFailureAgeRes) // --- Case where not all needed dates from the future are provided. start_date = mio::Date(2020, 12, 31); status = mio::isecir::set_initial_flows(model, dt, test_data, start_date, - scale_confirmed_cases); + scale_confirmed_cases); ASSERT_THAT(print_wrap(status), IsFailure(mio::StatusCode::OutOfRange)); // --- Case where not all needed dates from the past are provided. start_date = mio::Date(2020, 10, 1); status = mio::isecir::set_initial_flows(model, dt, test_data, start_date, - scale_confirmed_cases); + scale_confirmed_cases); // Check that status is Success as just a warning is logged. ASSERT_THAT(print_wrap(status), IsSuccess()); // Check that the flow InfectedNoSymptomsToInfectedSymptoms has actually been set to 0. @@ -365,7 +369,7 @@ TEST(TestIDEParametersIo, ParametersIoRKIFailureAgeRes) // --- Valid case. start_date = mio::Date(2020, 11, 01); status = mio::isecir::set_initial_flows(model, dt, test_data, start_date, - scale_confirmed_cases); + scale_confirmed_cases); ASSERT_THAT(print_wrap(status), IsSuccess()); } diff --git a/cpp/tests/test_ide_secir.cpp b/cpp/tests/test_ide_secir.cpp index 93e031ea90..c139f46bb4 100755 --- a/cpp/tests/test_ide_secir.cpp +++ b/cpp/tests/test_ide_secir.cpp @@ -72,9 +72,9 @@ class ModelTestIdeSecir : public testing::Test // Set working parameters. - mio::SmootherCosine smoothcos(2.0); - mio::StateAgeFunctionWrapper delaydistribution(smoothcos); - std::vector vec_delaydistrib(num_transitions, delaydistribution); + mio::SmootherCosine smoothcos(2.0); + mio::StateAgeFunctionWrapper delaydistribution(smoothcos); + std::vector> vec_delaydistrib(num_transitions, delaydistribution); model->parameters.get()[mio::AgeGroup(0)] = vec_delaydistrib; std::vector vec_prob((int)mio::isecir::InfectionTransition::Count, 0.5); @@ -82,12 +82,13 @@ class ModelTestIdeSecir : public testing::Test vec_prob[Eigen::Index(mio::isecir::InfectionTransition::ExposedToInfectedNoSymptoms)] = 1; model->parameters.get()[mio::AgeGroup(0)] = vec_prob; - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 10.)); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 10.)); model->parameters.get() = mio::UncertainContactMatrix(contact_matrix); mio::ExponentialSurvivalFunction exponential(0.5); - mio::StateAgeFunctionWrapper prob(exponential); + mio::StateAgeFunctionWrapper prob(exponential); model->parameters.get()[mio::AgeGroup(0)] = prob; model->parameters.get()[mio::AgeGroup(0)] = prob; model->parameters.get()[mio::AgeGroup(0)] = prob; @@ -244,9 +245,9 @@ TEST(IdeSecir, checkSimulationFunctions) // Set working parameters. // In this example, SmootherCosine with parameter 1 (and thus with a maximum support of 1) // is used for all TransitionDistribution%s. - mio::SmootherCosine smoothcos(1.0); - mio::StateAgeFunctionWrapper delaydistribution(smoothcos); - std::vector vec_delaydistrib(num_transitions, delaydistribution); + mio::SmootherCosine smoothcos(1.0); + mio::StateAgeFunctionWrapper delaydistribution(smoothcos); + std::vector> vec_delaydistrib(num_transitions, delaydistribution); model.parameters.get()[mio::AgeGroup(0)] = vec_delaydistrib; std::vector vec_prob((int)mio::isecir::InfectionTransition::Count, 0.5); @@ -254,12 +255,13 @@ TEST(IdeSecir, checkSimulationFunctions) vec_prob[Eigen::Index(mio::isecir::InfectionTransition::ExposedToInfectedNoSymptoms)] = 1; model.parameters.get()[mio::AgeGroup(0)] = vec_prob; - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 4.)); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 4.)); model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); - mio::SmootherCosine smoothcos_prob(1.0); - mio::StateAgeFunctionWrapper prob(smoothcos_prob); + mio::SmootherCosine smoothcos_prob(1.0); + mio::StateAgeFunctionWrapper prob(smoothcos_prob); model.parameters.get()[mio::AgeGroup(0)] = prob; model.parameters.get()[mio::AgeGroup(0)] = prob; model.parameters.get()[mio::AgeGroup(0)] = prob; @@ -339,8 +341,9 @@ TEST(IdeSecir, checkInitializations) /* !! For the other tests, the contact rate is set to 0 so that the force of infection is zero. The forceofinfection initialization method is therefore not used for these tests.*/ - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 0)); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 0)); mio::TimeSeries init_copy2(init); mio::isecir::Model model2(std::move(init_copy2), N, deaths, num_agegroups, @@ -498,8 +501,8 @@ TEST(IdeSecir, testModelConstraints) mio::isecir::Model model(std::move(init_few_timepoints), N, deaths, num_agegroups); mio::ExponentialSurvivalFunction exponential(4.0); - mio::StateAgeFunctionWrapper delaydistribution(exponential); - std::vector vec_delaydistrib(num_transitions, delaydistribution); + mio::StateAgeFunctionWrapper delaydistribution(exponential); + std::vector> vec_delaydistrib(num_transitions, delaydistribution); model.parameters.set(vec_delaydistrib); // Return true for not enough time points given for the initial transitions. @@ -611,10 +614,10 @@ TEST(IdeSecir, testParametersConstraints) // Set wrong parameters and test if check_constraints() reports them correctly. // Test in the same order as in check_constraints(). // Create invalid and valid function. - mio::ConstantFunction constant_func_neg(-1); - mio::StateAgeFunctionWrapper prob_neg(constant_func_neg); - mio::ConstantFunction constant_func_pos(1); - mio::StateAgeFunctionWrapper prob_pos(constant_func_pos); + mio::ConstantFunction constant_func_neg(-1); + mio::StateAgeFunctionWrapper prob_neg(constant_func_neg); + mio::ConstantFunction constant_func_pos(1); + mio::StateAgeFunctionWrapper prob_pos(constant_func_pos); // Warn, i.e., return true for wrong TransmissionProbabilityOnContact. parameters.get()[mio::AgeGroup(0)] = prob_neg; @@ -702,18 +705,18 @@ TEST(IdeSecir, testParametersConstraints) parameters.get()[mio::AgeGroup(0)][( int)mio::isecir::InfectionTransition::InfectedCriticalToDead] = 0.6; mio::ConstantFunction const_func(1.0); - mio::StateAgeFunctionWrapper delaydistribution(const_func); - std::vector vec_delaydistrib((int)mio::isecir::InfectionTransition::Count, - delaydistribution); + mio::StateAgeFunctionWrapper delaydistribution(const_func); + std::vector> vec_delaydistrib((int)mio::isecir::InfectionTransition::Count, + delaydistribution); parameters.get()[mio::AgeGroup(0)] = vec_delaydistrib; constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); // Check wrong value for Seasonality. mio::ExponentialSurvivalFunction exponential(4.0); - mio::StateAgeFunctionWrapper delaydistribution2(exponential); - std::vector vec_delaydistrib2((int)mio::isecir::InfectionTransition::Count, - delaydistribution2); + mio::StateAgeFunctionWrapper delaydistribution2(exponential); + std::vector> vec_delaydistrib2( + (int)mio::isecir::InfectionTransition::Count, delaydistribution2); parameters.get()[mio::AgeGroup(0)] = vec_delaydistrib2; parameters.set(2.); constraint_check = parameters.check_constraints(); @@ -766,8 +769,8 @@ TEST(IdeSecir, checkProportionRecoveredDeath) // For all TransitionDistribution%s init_parameter=2 is used except for InfectedCriticalToRecovered // where init_parameter=3 is used. mio::ExponentialSurvivalFunction exponential(4.0); - mio::StateAgeFunctionWrapper delaydistribution(exponential); - std::vector vec_delaydistrib(num_transitions, delaydistribution); + mio::StateAgeFunctionWrapper delaydistribution(exponential); + std::vector> vec_delaydistrib(num_transitions, delaydistribution); vec_delaydistrib[(int)mio::isecir::InfectionTransition::InfectedCriticalToRecovered].set_distribution_parameter( 3.0); model.parameters.get()[mio::AgeGroup(0)] = (vec_delaydistrib); @@ -782,12 +785,13 @@ TEST(IdeSecir, checkProportionRecoveredDeath) vec_prob[Eigen::Index(mio::isecir::InfectionTransition::InfectedCriticalToDead)] = 0.6; model.parameters.get()[mio::AgeGroup(0)] = vec_prob; - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 1.)); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 1.)); model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); mio::ExponentialSurvivalFunction exponential2(0.5); - mio::StateAgeFunctionWrapper prob(exponential2); + mio::StateAgeFunctionWrapper prob(exponential2); model.parameters.get()[mio::AgeGroup(0)] = prob; model.parameters.get()[mio::AgeGroup(0)] = prob; model.parameters.get()[mio::AgeGroup(0)] = prob; @@ -866,17 +870,17 @@ TEST(IdeSecir, compareEquilibria) // For the Model model. // All TransitionDistribution%s have parameter=2. - mio::SmootherCosine smoothcos(2.0); - mio::StateAgeFunctionWrapper delaydistribution(smoothcos); - std::vector vec_delaydistrib(num_transitions, delaydistribution); + mio::SmootherCosine smoothcos(2.0); + mio::StateAgeFunctionWrapper delaydistribution(smoothcos); + std::vector> vec_delaydistrib(num_transitions, delaydistribution); model.parameters.get()[mio::AgeGroup(0)] = (vec_delaydistrib); // For the Model model2. // All TransitionDistribution%s have parameter=2 except for InfectedCriticalToRecovered // which has parameter=7. - mio::SmootherCosine smoothcos2(2.0); - mio::StateAgeFunctionWrapper delaydistribution2(smoothcos); - std::vector vec_delaydistrib2(num_transitions, delaydistribution2); + mio::SmootherCosine smoothcos2(2.0); + mio::StateAgeFunctionWrapper delaydistribution2(smoothcos); + std::vector> vec_delaydistrib2(num_transitions, delaydistribution2); vec_delaydistrib2[(int)mio::isecir::InfectionTransition::InfectedCriticalToRecovered].set_distribution_parameter( 7.0); model2.parameters.get()[mio::AgeGroup(0)] = (vec_delaydistrib2); @@ -893,13 +897,14 @@ TEST(IdeSecir, compareEquilibria) model.parameters.get()[mio::AgeGroup(0)] = vec_prob; model2.parameters.get()[mio::AgeGroup(0)] = vec_prob; - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 1.)); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 1.)); model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); model2.parameters.get() = mio::UncertainContactMatrix(contact_matrix); mio::ExponentialSurvivalFunction exponential(0.5); - mio::StateAgeFunctionWrapper prob(exponential); + mio::StateAgeFunctionWrapper prob(exponential); model.parameters.get()[mio::AgeGroup(0)] = (prob); model.parameters.get()[mio::AgeGroup(0)] = (prob); model.parameters.get()[mio::AgeGroup(0)] = (prob); diff --git a/cpp/tests/test_ide_secir_ageres.cpp b/cpp/tests/test_ide_secir_ageres.cpp index 945aeee03e..a63502265d 100644 --- a/cpp/tests/test_ide_secir_ageres.cpp +++ b/cpp/tests/test_ide_secir_ageres.cpp @@ -83,9 +83,9 @@ TEST(TestIdeAgeres, compareWithPreviousRun) // Set working parameters. // First AgeGroup for TransitionDistributions. - mio::SmootherCosine smoothcos1(2.0); - mio::StateAgeFunctionWrapper delaydistribution1(smoothcos1); - std::vector vec_delaydistrib1(num_transitions, delaydistribution1); + mio::SmootherCosine smoothcos1(2.0); + mio::StateAgeFunctionWrapper delaydistribution1(smoothcos1); + std::vector> vec_delaydistrib1(num_transitions, delaydistribution1); // TransitionDistribution is not used for SusceptibleToExposed. Therefore, the parameter can be set to any value. vec_delaydistrib1[(int)mio::isecir::InfectionTransition::SusceptibleToExposed].set_distribution_parameter(-1.); vec_delaydistrib1[(int)mio::isecir::InfectionTransition::InfectedNoSymptomsToInfectedSymptoms] @@ -94,9 +94,9 @@ TEST(TestIdeAgeres, compareWithPreviousRun) model.parameters.get()[mio::AgeGroup(0)] = vec_delaydistrib1; // Second AgeGroup for TransitionDistributions. - mio::SmootherCosine smoothcos2(3.0); - mio::StateAgeFunctionWrapper delaydistribution2(smoothcos2); - std::vector vec_delaydistrib2(num_transitions, delaydistribution2); + mio::SmootherCosine smoothcos2(3.0); + mio::StateAgeFunctionWrapper delaydistribution2(smoothcos2); + std::vector> vec_delaydistrib2(num_transitions, delaydistribution2); // TransitionDistribution is not used for SusceptibleToExposed. Therefore, the parameter can be set to any value. vec_delaydistrib2[(int)mio::isecir::InfectionTransition::SusceptibleToExposed].set_distribution_parameter(-1.); vec_delaydistrib2[(int)mio::isecir::InfectionTransition::InfectedNoSymptomsToInfectedSymptoms] @@ -105,9 +105,9 @@ TEST(TestIdeAgeres, compareWithPreviousRun) model.parameters.get()[mio::AgeGroup(1)] = vec_delaydistrib2; // Third AgeGroup for TransitionDistributions. - mio::SmootherCosine smoothcos3(2.5); - mio::StateAgeFunctionWrapper delaydistribution3(smoothcos3); - std::vector vec_delaydistrib3(num_transitions, delaydistribution3); + mio::SmootherCosine smoothcos3(2.5); + mio::StateAgeFunctionWrapper delaydistribution3(smoothcos3); + std::vector> vec_delaydistrib3(num_transitions, delaydistribution3); // TransitionDistribution is not used for SusceptibleToExposed. Therefore, the parameter can be set to any value. vec_delaydistrib1[(int)mio::isecir::InfectionTransition::SusceptibleToExposed].set_distribution_parameter(-1.); vec_delaydistrib1[(int)mio::isecir::InfectionTransition::InfectedNoSymptomsToInfectedSymptoms] @@ -125,12 +125,13 @@ TEST(TestIdeAgeres, compareWithPreviousRun) } // Contact matrix. - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_agegroups, num_agegroups, 10.)); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, num_agegroups); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_agegroups, num_agegroups, 10.)); model.parameters.get() = mio::UncertainContactMatrix(contact_matrix); mio::ExponentialSurvivalFunction exponential(0.5); - mio::StateAgeFunctionWrapper prob(exponential); + mio::StateAgeFunctionWrapper prob(exponential); for (auto group = mio::AgeGroup(0); group < mio::AgeGroup(num_agegroups); ++group) { model.parameters.get()[group] = prob; model.parameters.get()[group] = prob; diff --git a/cpp/tests/test_ide_seir.cpp b/cpp/tests/test_ide_seir.cpp index 942fb83f67..631666cde1 100755 --- a/cpp/tests/test_ide_seir.cpp +++ b/cpp/tests/test_ide_seir.cpp @@ -38,7 +38,7 @@ TEST(ModelTestIdeSeirMin, simulateDefault) init.add_time_point(init.get_last_time() + dt, Vec::Constant(1, 10.)); } - mio::iseir::Model model(std::move(init), dt, 10); + mio::iseir::Model model(std::move(init), dt, 10); model.simulate(tmax); auto result = model.calculate_EIR(); @@ -63,16 +63,15 @@ class ModelTestIdeSeir : public testing::Test Vec::Constant(1, (double)result.get_last_value()[0] + result.get_last_time())); } - model = new mio::iseir::Model(std::move(result), dt, N); + model = new mio::iseir::Model(std::move(result), dt, N); model->parameters.set(3.3); model->parameters.set(8.2); model->parameters.set(0.015); - mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, 1); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10.)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(10.)); - model->parameters.get>() = - mio::UncertainContactMatrix(contact_matrix); + mio::ContactMatrixGroup contact_matrix = mio::ContactMatrixGroup(1, 1); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10.)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(10.)); + model->parameters.get() = mio::UncertainContactMatrix(contact_matrix); } virtual void TearDown() @@ -81,7 +80,7 @@ class ModelTestIdeSeir : public testing::Test } public: - mio::iseir::Model* model = nullptr; + mio::iseir::Model* model = nullptr; }; TEST_F(ModelTestIdeSeir, compareWithPreviousRun) diff --git a/cpp/tests/test_io_cli.cpp b/cpp/tests/test_io_cli.cpp index 86bf765908..d5559b3842 100644 --- a/cpp/tests/test_io_cli.cpp +++ b/cpp/tests/test_io_cli.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: René Schmieding diff --git a/cpp/tests/test_io_framework.cpp b/cpp/tests/test_io_framework.cpp index 9dd85f4a12..33e09c3893 100644 --- a/cpp/tests/test_io_framework.cpp +++ b/cpp/tests/test_io_framework.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Wadim Koslow diff --git a/cpp/tests/test_lct_initializer_flows.cpp b/cpp/tests/test_lct_initializer_flows.cpp index b0922fe3e3..21f41a4151 100644 --- a/cpp/tests/test_lct_initializer_flows.cpp +++ b/cpp/tests/test_lct_initializer_flows.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Lena Ploetzke @@ -35,8 +35,8 @@ TEST(TestInitializer, compareWithPrevious) ScalarType dt = 0.5; // Use one group. using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; // Previous result. Eigen::VectorX compare(LctState::Count); @@ -47,24 +47,25 @@ TEST(TestInitializer, compareWithPrevious) Model model; // Define parameters. - model.parameters.get()[0] = 3.1; - model.parameters.get()[0] = 3.1; - model.parameters.get()[0] = 6.1; - model.parameters.get()[0] = 11.1; - model.parameters.get()[0] = 17.1; - model.parameters.get()[0] = 0.01; - - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - - model.parameters.get()[0] = 1; - model.parameters.get()[0] = 1; - model.parameters.get() = 0; - model.parameters.get() = 0; - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 0.1; + model.parameters.get>()[0] = 3.1; + model.parameters.get>()[0] = 3.1; + model.parameters.get>()[0] = 6.1; + model.parameters.get>()[0] = 11.1; + model.parameters.get>()[0] = 17.1; + model.parameters.get>()[0] = 0.01; + + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); + + model.parameters.get>()[0] = 1; + model.parameters.get>()[0] = 1; + model.parameters.get>() = 0; + model.parameters.get>() = 0; + model.parameters.get>()[0] = 0.1; + model.parameters.get>()[0] = 0.1; + model.parameters.get>()[0] = 0.1; + model.parameters.get>()[0] = 0.1; Eigen::VectorX total_confirmed_cases = Eigen::VectorX::Constant(1, 341223.); Eigen::VectorX deaths = Eigen::VectorX::Constant(1, 9710.); @@ -88,7 +89,7 @@ TEST(TestInitializer, compareWithPrevious) } // Calculate initial vector and compare with previous result. - mio::lsecir::Initializer initializer(std::move(init), model); + mio::lsecir::Initializer initializer(std::move(init), model); initializer.set_tol_for_support_max(1e-6); initializer.compute_initialization_vector(total_population, deaths, total_confirmed_cases); @@ -98,7 +99,7 @@ TEST(TestInitializer, compareWithPrevious) } /* Test compares a calculation of an initial vector using data for flows with a previous result. -* Here, the population is divided into three identical groups with equal LctStates and parameters. +* Here, the population is divided into three identical groups with equal LctStates and parameters. * The sum of the initial values should be the same as the result with one single group. */ TEST(TestInitializer, compareWithPreviousThreeGroups) @@ -106,8 +107,8 @@ TEST(TestInitializer, compareWithPreviousThreeGroups) ScalarType dt = 0.5; // Use one group. using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; // Previous result. Eigen::VectorX compare(LctState::Count); @@ -120,24 +121,26 @@ TEST(TestInitializer, compareWithPreviousThreeGroups) //Define parameters. for (size_t group = 0; group < num_groups; group++) { - model.parameters.get()[group] = 3.1; - model.parameters.get()[group] = 3.1; - model.parameters.get()[group] = 6.1; - model.parameters.get()[group] = 11.1; - model.parameters.get()[group] = 17.1; - model.parameters.get()[group] = 0.01; - - model.parameters.get()[group] = 1.; - model.parameters.get()[group] = 1.; - model.parameters.get() = 0.; - model.parameters.get() = 0.; - model.parameters.get()[group] = 0.1; - model.parameters.get()[group] = 0.1; - model.parameters.get()[group] = 0.1; - model.parameters.get()[group] = 0.1; + model.parameters.get>()[group] = 3.1; + model.parameters.get>()[group] = 3.1; + model.parameters.get>()[group] = 6.1; + model.parameters.get>()[group] = 11.1; + model.parameters.get>()[group] = 17.1; + model.parameters.get>()[group] = 0.01; + + model.parameters.get>()[group] = 1.; + model.parameters.get>()[group] = 1.; + model.parameters.get>() = 0.; + model.parameters.get>() = 0.; + model.parameters.get>()[group] = 0.1; + model.parameters.get>()[group] = 0.1; + model.parameters.get>()[group] = 0.1; + model.parameters.get>()[group] = 0.1; } - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, 10.)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>(); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_groups, num_groups, 10.)); Eigen::VectorX total_confirmed_cases = Eigen::VectorX::Constant(num_groups, 341223. / (ScalarType)num_groups); @@ -172,7 +175,7 @@ TEST(TestInitializer, compareWithPreviousThreeGroups) } // Calculate initial vector and compare with previous result. - mio::lsecir::Initializer initializer(std::move(init), model); + mio::lsecir::Initializer initializer(std::move(init), model); initializer.set_tol_for_support_max(1e-6); initializer.compute_initialization_vector(total_population, deaths, total_confirmed_cases); ScalarType sum_groups = 0; @@ -197,8 +200,8 @@ TEST(TestInitializer, testConstraints) Eigen::VectorX total_population = Eigen::VectorX::Constant(2, 83155031.); // Use a model with two groups. using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; int infectionTransition_count = 2 * (int)mio::lsecir::InfectionTransition::Count; // Initialize a model. @@ -212,7 +215,7 @@ TEST(TestInitializer, testConstraints) init_wrong_size.add_time_point(init_wrong_size.get_last_time() + dt, vec_wrong_size); } - mio::lsecir::Initializer initializer_init_wrong_size(std::move(init_wrong_size), model); + mio::lsecir::Initializer initializer_init_wrong_size(std::move(init_wrong_size), model); bool status = initializer_init_wrong_size.compute_initialization_vector(total_population, deaths, total_confirmed_cases); @@ -227,7 +230,7 @@ TEST(TestInitializer, testConstraints) } mio::TimeSeries init_copy(init_wrong); - mio::lsecir::Initializer initializer_init_wrong_last_time(std::move(init_copy), model); + mio::lsecir::Initializer initializer_init_wrong_last_time(std::move(init_copy), model); status = initializer_init_wrong_last_time.compute_initialization_vector(total_population, deaths, total_confirmed_cases); @@ -239,7 +242,7 @@ TEST(TestInitializer, testConstraints) init_wrong.add_time_point(init_wrong.get_last_time() + dt, vec_init); } - mio::lsecir::Initializer initializer_init_wrong_equidistant(std::move(init_wrong), model); + mio::lsecir::Initializer initializer_init_wrong_equidistant(std::move(init_wrong), model); status = initializer_init_wrong_equidistant.compute_initialization_vector(total_population, deaths, total_confirmed_cases); @@ -253,7 +256,7 @@ TEST(TestInitializer, testConstraints) init_wrong_step.add_time_point(init_wrong_step.get_last_time() + 10 * dt, vec_init); } - mio::lsecir::Initializer initializer_init_wrong_step(std::move(init_wrong_step), model); + mio::lsecir::Initializer initializer_init_wrong_step(std::move(init_wrong_step), model); status = initializer_init_wrong_step.compute_initialization_vector(total_population, deaths, total_confirmed_cases); EXPECT_TRUE(status); @@ -265,7 +268,7 @@ TEST(TestInitializer, testConstraints) init_short.add_time_point(init_short.get_last_time() + dt, vec_init); } - mio::lsecir::Initializer initializer_init_short(std::move(init_short), model); + mio::lsecir::Initializer initializer_init_short(std::move(init_short), model); status = initializer_init_short.compute_initialization_vector(total_population, deaths, total_confirmed_cases); EXPECT_TRUE(status); @@ -279,8 +282,8 @@ TEST(TestInitializer, testConstraints) vec_init); } - mio::lsecir::Initializer initializer_negative_InfectedNoSymptoms(std::move(init_negative_InfectedNoSymptoms), - model); + mio::lsecir::Initializer initializer_negative_InfectedNoSymptoms( + std::move(init_negative_InfectedNoSymptoms), model); status = initializer_negative_InfectedNoSymptoms.compute_initialization_vector(total_population, deaths, total_confirmed_cases); EXPECT_TRUE(status); @@ -295,8 +298,8 @@ TEST(TestInitializer, testConstraints) init_negative_InfectedSymptoms.add_time_point(init_negative_InfectedSymptoms.get_last_time() + dt, vec_init); } - mio::lsecir::Initializer initializer_negative_InfectedSymptoms(std::move(init_negative_InfectedSymptoms), - model); + mio::lsecir::Initializer initializer_negative_InfectedSymptoms( + std::move(init_negative_InfectedSymptoms), model); status = initializer_negative_InfectedSymptoms.compute_initialization_vector(total_population, deaths, total_confirmed_cases); EXPECT_TRUE(status); @@ -311,7 +314,8 @@ TEST(TestInitializer, testConstraints) init_negative_InfectedSevere.add_time_point(init_negative_InfectedSevere.get_last_time() + dt, vec_init); } - mio::lsecir::Initializer initializer_negative_InfectedSevere(std::move(init_negative_InfectedSevere), model); + mio::lsecir::Initializer initializer_negative_InfectedSevere( + std::move(init_negative_InfectedSevere), model); status = initializer_negative_InfectedSevere.compute_initialization_vector(total_population, deaths, total_confirmed_cases); EXPECT_TRUE(status); @@ -326,8 +330,8 @@ TEST(TestInitializer, testConstraints) init_negative_InfectedCritical.add_time_point(init_negative_InfectedCritical.get_last_time() + dt, vec_init); } - mio::lsecir::Initializer initializer_negative_InfectedCritical(std::move(init_negative_InfectedCritical), - model); + mio::lsecir::Initializer initializer_negative_InfectedCritical( + std::move(init_negative_InfectedCritical), model); status = initializer_negative_InfectedCritical.compute_initialization_vector(total_population, deaths, total_confirmed_cases); EXPECT_TRUE(status); @@ -341,7 +345,7 @@ TEST(TestInitializer, testConstraints) init_negative_deaths.add_time_point(init_negative_deaths.get_last_time() + dt, vec_init); } - mio::lsecir::Initializer initializer_negative_deaths(std::move(init_negative_deaths), model); + mio::lsecir::Initializer initializer_negative_deaths(std::move(init_negative_deaths), model); status = initializer_negative_deaths.compute_initialization_vector(total_population, deaths, total_confirmed_cases); EXPECT_TRUE(status); @@ -353,7 +357,7 @@ TEST(TestInitializer, testConstraints) init_right.add_time_point(init_right.get_last_time() + dt, vec_init); } - mio::lsecir::Initializer initializer_right(std::move(init_right), model); + mio::lsecir::Initializer initializer_right(std::move(init_right), model); initializer_right.set_tol_for_support_max(1e-6); status = initializer_right.compute_initialization_vector(total_population, deaths, total_confirmed_cases); diff --git a/cpp/tests/test_lct_parameters_io.cpp b/cpp/tests/test_lct_parameters_io.cpp index d27bbf5ec7..40216359d7 100644 --- a/cpp/tests/test_lct_parameters_io.cpp +++ b/cpp/tests/test_lct_parameters_io.cpp @@ -60,8 +60,8 @@ std::vector get_synthetic_rki_data_age() const int num_agegroups = 6; Json::Value js(Json::arrayValue); std::vector dates = {"2020-05-26", "2020-05-27", "2020-05-28", "2020-05-29", - "2020-05-30", "2020-05-31", "2020-06-01", "2020-06-02", - "2020-06-03", "2020-06-04", "2020-06-05", "2020-06-06"}; + "2020-05-30", "2020-05-31", "2020-06-01", "2020-06-02", + "2020-06-03", "2020-06-04", "2020-06-05", "2020-06-06"}; std::vector age_group_names = {"A00-A04", "A05-A14", "A15-A34", "A35-A59", "A60-A79", "A80+"}; for (int day = 0; day < 12; day++) { for (int age = 0; age < num_agegroups; age++) { @@ -98,21 +98,21 @@ TEST(TestLCTParametersIo, ReadPopulationDataRKI) auto start_date = mio::Date(2020, 6, 1); using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; // Define parameters used for simulation and initialization. - model.parameters.get()[0] = 2.3; - model.parameters.get()[0] = 1.3; - model.parameters.get()[0] = 2.4; - model.parameters.get()[0] = 1.8; - model.parameters.get()[0] = 1.0; + model.parameters.template get>()[0] = 2.3; + model.parameters.template get>()[0] = 1.3; + model.parameters.template get>()[0] = 2.4; + model.parameters.template get>()[0] = 1.8; + model.parameters.template get>()[0] = 1.0; - model.parameters.get()[0] = 0.2; - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 0.3; - model.parameters.get()[0] = 0.2; + model.parameters.template get>()[0] = 0.2; + model.parameters.template get>()[0] = 0.1; + model.parameters.template get>()[0] = 0.3; + model.parameters.template get>()[0] = 0.2; // Calculate initial value vector for subcompartments with RKI data. auto read_result = @@ -142,23 +142,23 @@ TEST(TestLCTParametersIo, ReadPopulationDataRKIAgeres) auto start_date = mio::Date(2020, 6, 1); using InfState = mio::lsecir::InfectionState; - using LctState1 = mio::LctInfectionState; - using LctState2 = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState1 = mio::LctInfectionState; + using LctState2 = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; // Define parameters used for simulation and initialization. for (size_t i = 0; i < num_agegroups; i++) { - model.parameters.get()[i] = 2.3; - model.parameters.get()[i] = 1.3; - model.parameters.get()[i] = 2.4; - model.parameters.get()[i] = 1.8; - model.parameters.get()[i] = 1.0; - - model.parameters.get()[i] = 0.2; - model.parameters.get()[i] = 0.1; - model.parameters.get()[i] = 0.3; - model.parameters.get()[i] = 0.2; + model.parameters.template get>()[i] = 2.3; + model.parameters.template get>()[i] = 1.3; + model.parameters.template get>()[i] = 2.4; + model.parameters.template get>()[i] = 1.8; + model.parameters.template get>()[i] = 1.0; + + model.parameters.template get>()[i] = 0.2; + model.parameters.template get>()[i] = 0.1; + model.parameters.template get>()[i] = 0.3; + model.parameters.template get>()[i] = 0.2; } // Calculate initial value vector for subcompartments with RKI data. @@ -175,20 +175,20 @@ TEST(TestLCTParametersIo, ReadPopulationDataRKIAgeres) Eigen::VectorX compare(num_populations); for (size_t age = 0; age < num_agegroups; age++) { compare[(size_t)InfState::Count * age + (size_t)InfState::Exposed] = - 1. / (1. - model.parameters.get()[age]) * - model.parameters.get()[age] * (ScalarType)age; + 1. / (1. - model.parameters.template get>()[age]) * + model.parameters.template get>()[age] * (ScalarType)age; compare[(size_t)InfState::Count * age + (size_t)InfState::InfectedNoSymptoms] = - 1. / (1. - model.parameters.get()[age]) * - model.parameters.get()[age] * (ScalarType)age; + 1. / (1. - model.parameters.template get>()[age]) * + model.parameters.template get>()[age] * (ScalarType)age; compare[(size_t)InfState::Count * age + (size_t)InfState::InfectedSymptoms] = - model.parameters.get()[age] * (ScalarType)age; + model.parameters.template get>()[age] * (ScalarType)age; compare[(size_t)InfState::Count * age + (size_t)InfState::InfectedSevere] = - model.parameters.get()[age] * - model.parameters.get()[age] * (ScalarType)age; + model.parameters.template get>()[age] * + model.parameters.template get>()[age] * (ScalarType)age; compare[(size_t)InfState::Count * age + (size_t)InfState::InfectedCritical] = - model.parameters.get()[age] * - model.parameters.get()[age] * - model.parameters.get()[age] * (ScalarType)age; + model.parameters.template get>()[age] * + model.parameters.template get>()[age] * + model.parameters.template get>()[age] * (ScalarType)age; compare[(size_t)InfState::Count * age + (size_t)InfState::Dead] = (ScalarType)age; compare[(size_t)InfState::Count * age + (size_t)InfState::Recovered] = 100. + (ScalarType)age * 5. - @@ -219,23 +219,23 @@ TEST(TestLCTParametersIo, CheckScalingDIVI) auto start_date = mio::Date(2020, 6, 1); using InfState = mio::lsecir::InfectionState; - using LctState1 = mio::LctInfectionState; - using LctState2 = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState1 = mio::LctInfectionState; + using LctState2 = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; // Define parameters used for simulation and initialization. for (size_t i = 0; i < num_agegroups; i++) { - model.parameters.get()[i] = 2.3; - model.parameters.get()[i] = 1.3; - model.parameters.get()[i] = 2.4; - model.parameters.get()[i] = 1.8; - model.parameters.get()[i] = 1.0; - - model.parameters.get()[i] = 0.2; - model.parameters.get()[i] = 0.1; - model.parameters.get()[i] = 0.3; - model.parameters.get()[i] = 0.2; + model.parameters.template get>()[i] = 2.3; + model.parameters.template get>()[i] = 1.3; + model.parameters.template get>()[i] = 2.4; + model.parameters.template get>()[i] = 1.8; + model.parameters.template get>()[i] = 1.0; + + model.parameters.template get>()[i] = 0.2; + model.parameters.template get>()[i] = 0.1; + model.parameters.template get>()[i] = 0.3; + model.parameters.template get>()[i] = 0.2; } // Check that the function get_icu_from_divi_data to get the DIVI data works as expected. @@ -292,10 +292,10 @@ TEST(TestLCTParametersIo, CheckRescaleToDIVIDataFunctionCases) { const size_t num_agegroups = 6; using InfState = mio::lsecir::InfectionState; - using LctState1 = mio::LctInfectionState; - using LctState2 = mio::LctInfectionState; - using Model = mio::lsecir::Model; - using Populations = Model::Populations; + using LctState1 = mio::LctInfectionState; + using LctState2 = mio::LctInfectionState; + using Model = mio::lsecir::Model; + using Populations = Model::Populations; // Initialize a population with zero values. Populations pop; @@ -355,21 +355,21 @@ TEST(TestLCTParametersIo, ReadPopulationDataRKIFailure) { std::vector total_population(1, 1000.); using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; // Define parameters. - model.parameters.get()[0] = 2.3; - model.parameters.get()[0] = 1.3; - model.parameters.get()[0] = 2.4; - model.parameters.get()[0] = 1.8; - model.parameters.get()[0] = 1.0; + model.parameters.template get>()[0] = 2.3; + model.parameters.template get>()[0] = 1.3; + model.parameters.template get>()[0] = 2.4; + model.parameters.template get>()[0] = 1.8; + model.parameters.template get>()[0] = 1.0; - model.parameters.get()[0] = 0.2; - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 0.3; - model.parameters.get()[0] = 0.2; + model.parameters.template get>()[0] = 0.2; + model.parameters.template get>()[0] = 0.1; + model.parameters.template get>()[0] = 0.3; + model.parameters.template get>()[0] = 0.2; // Deactivate temporarily log output for next tests. mio::set_log_level(mio::LogLevel::off); @@ -420,22 +420,22 @@ TEST(TestLCTParametersIo, ReadPopulationDataRKIFailureAgeres) std::vector total_population(num_agegroups, 1e6); using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; // Define parameters. for (size_t i = 0; i < num_agegroups; i++) { - model.parameters.get()[i] = 2.3; - model.parameters.get()[i] = 1.3; - model.parameters.get()[i] = 2.4; - model.parameters.get()[i] = 1.8; - model.parameters.get()[i] = 1.0; - - model.parameters.get()[i] = 0.2; - model.parameters.get()[i] = 0.1; - model.parameters.get()[i] = 0.3; - model.parameters.get()[i] = 0.2; + model.parameters.template get>()[i] = 2.3; + model.parameters.template get>()[i] = 1.3; + model.parameters.template get>()[i] = 2.4; + model.parameters.template get>()[i] = 1.8; + model.parameters.template get>()[i] = 1.0; + + model.parameters.template get>()[i] = 0.2; + model.parameters.template get>()[i] = 0.1; + model.parameters.template get>()[i] = 0.3; + model.parameters.template get>()[i] = 0.2; } // Deactivate temporarily log output for next tests. diff --git a/cpp/tests/test_lct_secir.cpp b/cpp/tests/test_lct_secir.cpp index 9bc8502c22..71525b2d7f 100755 --- a/cpp/tests/test_lct_secir.cpp +++ b/cpp/tests/test_lct_secir.cpp @@ -40,8 +40,8 @@ TEST(TestLCTSecir, simulateDefault) { using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; ScalarType t0 = 0; ScalarType tmax = 1; ScalarType dt = 0.1; @@ -70,8 +70,8 @@ TEST(TestLCTSecir, simulateDefault) TEST(TestLCTSecir, compareWithOdeSecir) { using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; ScalarType t0 = 0; ScalarType tmax = 5; ScalarType dt = 0.1; @@ -90,26 +90,27 @@ TEST(TestLCTSecir, compareWithOdeSecir) } // Set Parameters. - model_lct.parameters.get()[0] = 3.2; - model_lct.parameters.get()[0] = 2; - model_lct.parameters.get()[0] = 5.8; - model_lct.parameters.get()[0] = 9.5; - model_lct.parameters.get()[0] = 7.1; - - model_lct.parameters.get()[0] = 0.05; - - mio::ContactMatrixGroup& contact_matrix_lct = model_lct.parameters.get(); - contact_matrix_lct[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - contact_matrix_lct[0].add_damping(0.7, mio::SimulationTime(2.)); - - model_lct.parameters.get()[0] = 0.7; - model_lct.parameters.get()[0] = 0.25; - model_lct.parameters.get() = 50; - model_lct.parameters.get() = 0.1; - model_lct.parameters.get()[0] = 0.09; - model_lct.parameters.get()[0] = 0.2; - model_lct.parameters.get()[0] = 0.25; - model_lct.parameters.get()[0] = 0.3; + model_lct.parameters.template get>()[0] = 3.2; + model_lct.parameters.template get>()[0] = 2; + model_lct.parameters.template get>()[0] = 5.8; + model_lct.parameters.template get>()[0] = 9.5; + model_lct.parameters.template get>()[0] = 7.1; + + model_lct.parameters.template get>()[0] = 0.05; + + mio::ContactMatrixGroup& contact_matrix_lct = + model_lct.parameters.template get>(); + contact_matrix_lct[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); + contact_matrix_lct[0].add_damping(0.7, mio::SimulationTime(2.)); + + model_lct.parameters.template get>()[0] = 0.7; + model_lct.parameters.template get>()[0] = 0.25; + model_lct.parameters.template get>() = 50; + model_lct.parameters.template get>() = 0.1; + model_lct.parameters.template get>()[0] = 0.09; + model_lct.parameters.template get>()[0] = 0.2; + model_lct.parameters.template get>()[0] = 0.25; + model_lct.parameters.template get>()[0] = 0.3; // Simulate. mio::TimeSeries result_lct = mio::simulate( @@ -117,7 +118,7 @@ TEST(TestLCTSecir, compareWithOdeSecir) std::make_unique>()); // Initialize ODE model with one age group. - mio::osecir::Model model_ode(1); + mio::osecir::Model model_ode(1); // Set initial distribution of the population. model_ode.populations[{(mio::AgeGroup)0, mio::osecir::InfectionState::Exposed}] = init[(Eigen::Index)InfState::Exposed]; @@ -139,33 +140,39 @@ TEST(TestLCTSecir, compareWithOdeSecir) // Set parameters according to the parameters of the LCT model. // No restrictions by additional parameters. - model_ode.parameters.get>() = std::numeric_limits::max(); - model_ode.parameters.get>() = std::numeric_limits::max(); - - model_ode.parameters.set(50); - model_ode.parameters.set>(0.1); - model_ode.parameters.get>()[(mio::AgeGroup)0] = 3.2; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 2.0; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 5.8; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 9.5; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 7.1; - - mio::ContactMatrixGroup& contact_matrix_ode = model_ode.parameters.get>(); - contact_matrix_ode[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - contact_matrix_ode[0].add_damping(0.7, mio::SimulationTime(2.)); - - model_ode.parameters.get>()[(mio::AgeGroup)0] = 0.05; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 0.7; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 0.09; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 0.25; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 0.2; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 0.25; - model_ode.parameters.get>()[(mio::AgeGroup)0] = 0.3; + model_ode.parameters.template get>() = + std::numeric_limits::max(); + model_ode.parameters.template get>() = std::numeric_limits::max(); + + model_ode.parameters.set>(50); + model_ode.parameters.set>(0.1); + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 3.2; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 2.0; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 5.8; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 9.5; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 7.1; + + mio::ContactMatrixGroup& contact_matrix_ode = + model_ode.parameters.template get>(); + contact_matrix_ode[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); + contact_matrix_ode[0].add_damping(0.7, mio::SimulationTime(2.)); + + model_ode.parameters.template get>()[(mio::AgeGroup)0] = + 0.05; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = + 0.7; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = + 0.09; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = + 0.25; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 0.2; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 0.25; + model_ode.parameters.template get>()[(mio::AgeGroup)0] = 0.3; // Simulate. - mio::TimeSeries result_ode = mio::osecir::simulate( + mio::TimeSeries result_ode = mio::osecir::simulate( t0, tmax, dt, model_ode, - std::make_unique>()); + std::make_unique>()); // Simulation results should be equal. ASSERT_EQ(result_lct.get_num_time_points(), result_ode.get_num_time_points()); @@ -199,8 +206,8 @@ TEST(TestLCTSecir, testEvalRightHandSide) // Define model. // Chose more than one subcompartment for all compartments except S, R, D so that the function is correct for all selections. using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; // Define initial population distribution in infection states, one entry per subcompartment. @@ -217,30 +224,31 @@ TEST(TestLCTSecir, testEvalRightHandSide) } // Set parameters. - model.parameters.get()[0] = 3.2; - model.parameters.get()[0] = 2; - model.parameters.get()[0] = 5.8; - model.parameters.get()[0] = 9.5; - model.parameters.get()[0] = 7.1; - - model.parameters.get()[0] = 0.05; - - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - - model.parameters.get()[0] = 0.7; - model.parameters.get()[0] = 0.25; - model.parameters.get()[0] = 0.09; - model.parameters.set(0.); - model.parameters.set(0); - model.parameters.get()[0] = 0.2; - model.parameters.get()[0] = 0.25; - model.parameters.get()[0] = 0.3; + model.parameters.template get>()[0] = 3.2; + model.parameters.template get>()[0] = 2; + model.parameters.template get>()[0] = 5.8; + model.parameters.template get>()[0] = 9.5; + model.parameters.template get>()[0] = 7.1; + + model.parameters.template get>()[0] = 0.05; + + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); + + model.parameters.template get>()[0] = 0.7; + model.parameters.template get>()[0] = 0.25; + model.parameters.template get>()[0] = 0.09; + model.parameters.set>(0.); + model.parameters.set>(0); + model.parameters.template get>()[0] = 0.2; + model.parameters.template get>()[0] = 0.25; + model.parameters.template get>()[0] = 0.3; // Compare the result of get_derivatives() with a hand calculated result. size_t num_subcompartments = LctState::Count; Eigen::VectorX dydt(num_subcompartments); - model.get_derivatives(model.get_initial_values(), model.get_initial_values(), 0, dydt); + model.get_derivatives(model.get_initial_values(), model.get_initial_values(), 0.0, dydt); Eigen::VectorX compare(num_subcompartments); compare << -15.3409, -3.4091, 6.25, -17.5, 15, 0, 3.3052, 3.4483, -7.0417, 6.3158, -2.2906, -2.8169, 12.3899, @@ -257,9 +265,9 @@ TEST(TestLCTSecir, testEvalRightHandSideGroups) // Define model. // Choose more than one subcompartment for all compartments except S, R, D so that the function is correct for all selections. using InfState = mio::lsecir::InfectionState; - using LctState1 = mio::LctInfectionState; - using LctState2 = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState1 = mio::LctInfectionState; + using LctState2 = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; size_t num_groups = Model::num_groups; size_t num_subcompartments = model.populations.get_num_compartments(); @@ -282,42 +290,44 @@ TEST(TestLCTSecir, testEvalRightHandSideGroups) } // Set parameters. - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, 10)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>(); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixX::Constant(num_groups, num_groups, 10)); // Group 1. - model.parameters.get()[0] = 5.; - model.parameters.get()[0] = 5.; - model.parameters.get()[0] = 5.; - model.parameters.get()[0] = 5.; - model.parameters.get()[0] = 5.; - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 1.; - model.parameters.get()[0] = 1.; - model.parameters.get()[0] = 0.1; - model.parameters.set(0.); - model.parameters.set(0); - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 0.1; - model.parameters.get()[0] = 0.1; + model.parameters.template get>()[0] = 5.; + model.parameters.template get>()[0] = 5.; + model.parameters.template get>()[0] = 5.; + model.parameters.template get>()[0] = 5.; + model.parameters.template get>()[0] = 5.; + model.parameters.template get>()[0] = 0.1; + model.parameters.template get>()[0] = 1.; + model.parameters.template get>()[0] = 1.; + model.parameters.template get>()[0] = 0.1; + model.parameters.set>(0.); + model.parameters.set>(0); + model.parameters.template get>()[0] = 0.1; + model.parameters.template get>()[0] = 0.1; + model.parameters.template get>()[0] = 0.1; // Group 2. - model.parameters.get()[1] = 2.; - model.parameters.get()[1] = 2.; - model.parameters.get()[1] = 2.; - model.parameters.get()[1] = 2.; - model.parameters.get()[1] = 2.; - model.parameters.get()[1] = 0.1; - model.parameters.get()[1] = 0.5; - model.parameters.get()[1] = 0.5; - model.parameters.get()[1] = 0.5; - model.parameters.set(0.); - model.parameters.set(0); - model.parameters.get()[1] = 0.5; - model.parameters.get()[1] = 0.5; - model.parameters.get()[1] = 0.5; + model.parameters.template get>()[1] = 2.; + model.parameters.template get>()[1] = 2.; + model.parameters.template get>()[1] = 2.; + model.parameters.template get>()[1] = 2.; + model.parameters.template get>()[1] = 2.; + model.parameters.template get>()[1] = 0.1; + model.parameters.template get>()[1] = 0.5; + model.parameters.template get>()[1] = 0.5; + model.parameters.template get>()[1] = 0.5; + model.parameters.set>(0.); + model.parameters.set>(0); + model.parameters.template get>()[1] = 0.5; + model.parameters.template get>()[1] = 0.5; + model.parameters.template get>()[1] = 0.5; // Compare the result of get_derivatives() with a hand calculated result. Eigen::VectorX dydt(num_subcompartments); - model.get_derivatives(model.get_initial_values(), model.get_initial_values(), 0, dydt); + model.get_derivatives(model.get_initial_values(), model.get_initial_values(), 0.0, dydt); Eigen::VectorX compare(num_subcompartments); compare << -90.6818, 78.6818, 4, -4, 6, 0, -6.6, 4, -15.2, 12, -3.6, -4, 18.6, 0.8, -90.6818, 85.6818, -20, 12, @@ -359,8 +369,8 @@ TEST(TestLCTSecir, testEvalRightHandSideThreeGroupsEqual) // Define model. // Chose more than one subcompartment for all compartments except S, R, D so that the function is correct for all selections. using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; size_t num_groups = Model::num_groups; size_t num_subcompartments = LctState::Count; @@ -381,31 +391,32 @@ TEST(TestLCTSecir, testEvalRightHandSideThreeGroupsEqual) } // Set parameters. for (size_t group = 0; group < num_groups; group++) { - model.parameters.get()[group] = 3.2; - model.parameters.get()[group] = 2; - model.parameters.get()[group] = 5.8; - model.parameters.get()[group] = 9.5; - model.parameters.get()[group] = 7.1; - model.parameters.get()[group] = 0.05; - - model.parameters.get()[group] = 0.7; - model.parameters.get()[group] = 0.25; - model.parameters.get()[group] = 0.09; - model.parameters.get()[group] = 0.2; - model.parameters.get()[group] = 0.25; - model.parameters.get()[group] = 0.3; + model.parameters.template get>()[group] = 3.2; + model.parameters.template get>()[group] = 2; + model.parameters.template get>()[group] = 5.8; + model.parameters.template get>()[group] = 9.5; + model.parameters.template get>()[group] = 7.1; + model.parameters.template get>()[group] = 0.05; + + model.parameters.template get>()[group] = 0.7; + model.parameters.template get>()[group] = 0.25; + model.parameters.template get>()[group] = 0.09; + model.parameters.template get>()[group] = 0.2; + model.parameters.template get>()[group] = 0.25; + model.parameters.template get>()[group] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, 10. / (ScalarType)num_groups)); - model.parameters.set(0.); - model.parameters.set(0); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant(num_groups, num_groups, 10. / (ScalarType)num_groups)); + model.parameters.set>(0.); + model.parameters.set>(0); // Compare the result of get_derivatives() with a hand calculated result. Eigen::VectorX dydt(num_groups * num_subcompartments); - model.get_derivatives(model.get_initial_values(), model.get_initial_values(), 0, dydt); + model.get_derivatives(model.get_initial_values(), model.get_initial_values(), 0.0, dydt); Eigen::VectorX compare(num_subcompartments); compare << -15.3409, -3.4091, 6.25, -17.5, 15, 0, 3.3052, 3.4483, -7.0417, 6.3158, -2.2906, -2.8169, 12.3899, @@ -425,8 +436,8 @@ class ModelTestLCTSecir : public testing::Test { public: using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; protected: virtual void SetUp() @@ -445,23 +456,24 @@ class ModelTestLCTSecir : public testing::Test } // Set parameters. - model->parameters.get()[0] = 3.2; - model->parameters.get()[0] = 2; - model->parameters.get()[0] = 5.8; - model->parameters.get()[0] = 9.5; - model->parameters.get()[0] = 7.1; - model->parameters.get()[0] = 0.05; - - mio::ContactMatrixGroup& contact_matrix = model->parameters.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(2.)); - - model->parameters.get()[0] = 0.7; - model->parameters.get()[0] = 0.25; - model->parameters.get()[0] = 0.09; - model->parameters.get()[0] = 0.2; - model->parameters.get()[0] = 0.25; - model->parameters.get()[0] = 0.3; + model->parameters.get>()[0] = 3.2; + model->parameters.get>()[0] = 2; + model->parameters.get>()[0] = 5.8; + model->parameters.get>()[0] = 9.5; + model->parameters.get>()[0] = 7.1; + model->parameters.get>()[0] = 0.05; + + mio::ContactMatrixGroup& contact_matrix = + model->parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(2.)); + + model->parameters.get>()[0] = 0.7; + model->parameters.get>()[0] = 0.25; + model->parameters.get>()[0] = 0.09; + model->parameters.get>()[0] = 0.2; + model->parameters.get>()[0] = 0.25; + model->parameters.get>()[0] = 0.3; } virtual void TearDown() @@ -515,8 +527,8 @@ TEST_F(ModelTestLCTSecir, compareWithPreviousRun) TEST(TestLCTSecir, compareWithPreviousRunThreeGroups) { using InfState = mio::lsecir::InfectionState; - using LctState = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctState = mio::LctInfectionState; + using Model = mio::lsecir::Model; // Initialize a model. Model model; @@ -538,31 +550,32 @@ TEST(TestLCTSecir, compareWithPreviousRunThreeGroups) } // Set parameters. for (size_t group = 0; group < num_groups; group++) { - model.parameters.get()[group] = 3.2; - model.parameters.get()[group] = 2; - model.parameters.get()[group] = 5.8; - model.parameters.get()[group] = 9.5; - model.parameters.get()[group] = 7.1; - model.parameters.get()[group] = 0.05; - - model.parameters.get()[group] = 0.7; - model.parameters.get()[group] = 0.25; - model.parameters.get()[group] = 0.09; - model.parameters.get()[group] = 0.2; - model.parameters.get()[group] = 0.25; - model.parameters.get()[group] = 0.3; + model.parameters.template get>()[group] = 3.2; + model.parameters.template get>()[group] = 2; + model.parameters.template get>()[group] = 5.8; + model.parameters.template get>()[group] = 9.5; + model.parameters.template get>()[group] = 7.1; + model.parameters.template get>()[group] = 0.05; + + model.parameters.template get>()[group] = 0.7; + model.parameters.template get>()[group] = 0.25; + model.parameters.template get>()[group] = 0.09; + model.parameters.template get>()[group] = 0.2; + model.parameters.template get>()[group] = 0.25; + model.parameters.template get>()[group] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = model.parameters.get(); - contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, 10. / (ScalarType)num_groups)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(2.)); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>(); + contact_matrix[0] = mio::ContactMatrix( + Eigen::MatrixX::Constant(num_groups, num_groups, 10. / (ScalarType)num_groups)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(2.)); // Compare just one step as the adaptive methods does not necessarily produce the same step sizes. auto compare = load_test_data_csv("lct-secir-subcompartments-compare.csv"); ScalarType dt = compare[1][0] - compare[0][0]; ScalarType tmax = 2 * dt; - auto integrator = + auto integrator = std::make_unique>(); // Choose dt_min = dt_max so that we have a fixed time step and can compare to the result with one group. integrator->set_dt_min(dt); @@ -623,99 +636,100 @@ TEST(TestLCTSecir, testConstraintsParameters) mio::set_log_level(mio::LogLevel::off); // Check for exceptions of parameters. - mio::lsecir::Parameters parameters_lct(1); - parameters_lct.get()[0] = 0; - parameters_lct.get()[0] = 3.1; - parameters_lct.get()[0] = 6.1; - parameters_lct.get()[0] = 11.1; - parameters_lct.get()[0] = 17.1; - parameters_lct.get()[0] = 0.01; - mio::ContactMatrixGroup& contact_matrix = parameters_lct.get(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - - parameters_lct.get()[0] = 1; - parameters_lct.get()[0] = 1; - parameters_lct.get()[0] = 0.1; - parameters_lct.get()[0] = 0.1; - parameters_lct.get()[0] = 0.1; - parameters_lct.get()[0] = 0.1; + mio::lsecir::Parameters parameters_lct(1); + parameters_lct.get>()[0] = 0; + parameters_lct.get>()[0] = 3.1; + parameters_lct.get>()[0] = 6.1; + parameters_lct.get>()[0] = 11.1; + parameters_lct.get>()[0] = 17.1; + parameters_lct.get>()[0] = 0.01; + mio::ContactMatrixGroup& contact_matrix = + parameters_lct.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixX::Constant(1, 1, 10)); + + parameters_lct.get>()[0] = 1; + parameters_lct.get>()[0] = 1; + parameters_lct.get>()[0] = 0.1; + parameters_lct.get>()[0] = 0.1; + parameters_lct.get>()[0] = 0.1; + parameters_lct.get>()[0] = 0.1; // Check improper TimeExposed. bool constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 3.1; + parameters_lct.get>()[0] = 3.1; // Check TimeInfectedNoSymptoms. - parameters_lct.get()[0] = 0.1; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = 0.1; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 3.1; + parameters_lct.get>()[0] = 3.1; // Check TimeInfectedSymptoms. - parameters_lct.get()[0] = -0.1; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = -0.1; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 6.1; + parameters_lct.get>()[0] = 6.1; // Check TimeInfectedSevere. - parameters_lct.get()[0] = 0.5; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = 0.5; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 11.1; + parameters_lct.get>()[0] = 11.1; // Check TimeInfectedCritical. - parameters_lct.get()[0] = 0.; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = 0.; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 17.1; + parameters_lct.get>()[0] = 17.1; // Check TransmissionProbabilityOnContact. - parameters_lct.get()[0] = -1; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = -1; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 0.01; + parameters_lct.get>()[0] = 0.01; // Check RelativeTransmissionNoSymptoms. - parameters_lct.get()[0] = 1.5; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = 1.5; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 1; + parameters_lct.get>()[0] = 1; // Check RiskOfInfectionFromSymptomatic. - parameters_lct.get()[0] = 1.5; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = 1.5; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 1; + parameters_lct.get>()[0] = 1; // Check RecoveredPerInfectedNoSymptoms. - parameters_lct.get()[0] = 1.5; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = 1.5; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 0.1; + parameters_lct.get>()[0] = 0.1; // Check SeverePerInfectedSymptoms. - parameters_lct.get()[0] = -1; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = -1; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 0.1; + parameters_lct.get>()[0] = 0.1; // Check CriticalPerSevere. - parameters_lct.get()[0] = -1; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = -1; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 0.1; + parameters_lct.get>()[0] = 0.1; // Check DeathsPerCritical. - parameters_lct.get()[0] = -1; - constraint_check = parameters_lct.check_constraints(); + parameters_lct.get>()[0] = -1; + constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.get()[0] = 0.1; + parameters_lct.get>()[0] = 0.1; // Check Seasonality. - parameters_lct.set(1); + parameters_lct.set>(1); constraint_check = parameters_lct.check_constraints(); EXPECT_TRUE(constraint_check); - parameters_lct.set(0.1); + parameters_lct.set>(0.1); // Check with correct parameters. constraint_check = parameters_lct.check_constraints(); @@ -732,32 +746,32 @@ TEST(TestLCTSecir, testConstraintsModel) mio::set_log_level(mio::LogLevel::off); using InfState = mio::lsecir::InfectionState; - using LctState1 = mio::LctInfectionState; + using LctState1 = mio::LctInfectionState; // Check for improper number of subcompartments for Susceptible. - using LctStatewrongSusceptibles = mio::LctInfectionState; - using ModelwrongSusceptibles = mio::lsecir::Model; + using LctStatewrongSusceptibles = mio::LctInfectionState; + using ModelwrongSusceptibles = mio::lsecir::Model; ModelwrongSusceptibles modelwrongSusceptibles; bool constraint_check = modelwrongSusceptibles.check_constraints(); EXPECT_TRUE(constraint_check); // Check for improper number of subcompartments for Recovered. - using LctStatewrongRecovered = mio::LctInfectionState; - using ModelwrongRecovered = mio::lsecir::Model; + using LctStatewrongRecovered = mio::LctInfectionState; + using ModelwrongRecovered = mio::lsecir::Model; ModelwrongRecovered modelwrongRecovered; constraint_check = modelwrongRecovered.check_constraints(); EXPECT_TRUE(constraint_check); // Check for improper number of subcompartments for Dead. - using LctStatewrongDead = mio::LctInfectionState; - using ModelwrongDead = mio::lsecir::Model; + using LctStatewrongDead = mio::LctInfectionState; + using ModelwrongDead = mio::lsecir::Model; ModelwrongDead modelwrongDead; constraint_check = modelwrongDead.check_constraints(); EXPECT_TRUE(constraint_check); // Check with a negative number in the initial population distribution. - using LctStatevalid = mio::LctInfectionState; - using Model = mio::lsecir::Model; + using LctStatevalid = mio::LctInfectionState; + using Model = mio::lsecir::Model; Model model; model.populations[0] = -1000; constraint_check = model.check_constraints(); diff --git a/cpp/tests/test_math_floating_point.cpp b/cpp/tests/test_math_floating_point.cpp index 3653b919f9..df50cea60f 100644 --- a/cpp/tests/test_math_floating_point.cpp +++ b/cpp/tests/test_math_floating_point.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Rene Schmieding @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "ad/ad.hpp" +#include "memilio/ad/ad.h" #include "memilio/math/floating_point.h" #include diff --git a/cpp/tests/test_math_time_series_functor.cpp b/cpp/tests/test_math_time_series_functor.cpp index 360df6ee12..9719892115 100644 --- a/cpp/tests/test_math_time_series_functor.cpp +++ b/cpp/tests/test_math_time_series_functor.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: René Schmieding diff --git a/cpp/tests/test_matrix_shape.cpp b/cpp/tests/test_matrix_shape.cpp index a22f8a2bbe..d9906f86f7 100644 --- a/cpp/tests/test_matrix_shape.cpp +++ b/cpp/tests/test_matrix_shape.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -22,23 +22,26 @@ TEST(TestMatrixShape, Rect) { - EXPECT_EQ(mio::RectMatrixShape(3, 2).rows(), 3); - EXPECT_EQ(mio::RectMatrixShape(3, 2).cols(), 2); - EXPECT_EQ(mio::RectMatrixShape::get_shape_of(Eigen::MatrixXd::Zero(2, 3)), mio::RectMatrixShape(2, 3)); + EXPECT_EQ(mio::RectMatrixShape(3, 2).rows(), 3); + EXPECT_EQ(mio::RectMatrixShape(3, 2).cols(), 2); + EXPECT_EQ(mio::RectMatrixShape::get_shape_of(Eigen::MatrixXd::Zero(2, 3)), + mio::RectMatrixShape(2, 3)); } TEST(TestMatrixShape, Square) { - EXPECT_EQ(mio::SquareMatrixShape(3).rows(), 3); - EXPECT_EQ(mio::SquareMatrixShape(3).cols(), 3); - EXPECT_EQ(mio::SquareMatrixShape(3).size(), 3); - EXPECT_EQ(mio::SquareMatrixShape::get_shape_of(Eigen::MatrixXd::Zero(3, 3)), mio::SquareMatrixShape(3)); + EXPECT_EQ(mio::SquareMatrixShape(3).rows(), 3); + EXPECT_EQ(mio::SquareMatrixShape(3).cols(), 3); + EXPECT_EQ(mio::SquareMatrixShape(3).size(), 3); + EXPECT_EQ(mio::SquareMatrixShape::get_shape_of(Eigen::MatrixXd::Zero(3, 3)), + mio::SquareMatrixShape(3)); } TEST(TestMatrixShape, Vector) { - EXPECT_EQ(mio::ColumnVectorShape(3).rows(), 3); - EXPECT_EQ(mio::ColumnVectorShape(3).cols(), 1); - EXPECT_EQ(mio::ColumnVectorShape(3).size(), 3); - EXPECT_EQ(mio::ColumnVectorShape::get_shape_of(Eigen::VectorXd::Zero(3)), mio::ColumnVectorShape(3)); + EXPECT_EQ(mio::ColumnVectorShape(3).rows(), 3); + EXPECT_EQ(mio::ColumnVectorShape(3).cols(), 1); + EXPECT_EQ(mio::ColumnVectorShape(3).size(), 3); + EXPECT_EQ(mio::ColumnVectorShape::get_shape_of(Eigen::VectorXd::Zero(3)), + mio::ColumnVectorShape(3)); } diff --git a/cpp/tests/test_metaprogramming.cpp b/cpp/tests/test_metaprogramming.cpp index d854ea9990..b8d9b0dbd4 100644 --- a/cpp/tests/test_metaprogramming.cpp +++ b/cpp/tests/test_metaprogramming.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/test_mobility.cpp b/cpp/tests/test_mobility.cpp index 1f18acef93..7ef58fcb23 100755 --- a/cpp/tests/test_mobility.cpp +++ b/cpp/tests/test_mobility.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -29,9 +29,9 @@ TEST(TestMobility, compareNoMobilityWithSingleIntegration) { - auto t0 = 0.0; - auto tmax = 5.0; - auto dt = 0.5; + ScalarType t0 = 0.0; + ScalarType tmax = 5.0; + ScalarType dt = 0.5; mio::oseir::Model model1(1); model1.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = 0.9; @@ -48,10 +48,10 @@ TEST(TestMobility, compareNoMobilityWithSingleIntegration) // Define graph simulation that inherently allows mobility but edges are defined such that there is no mobility // between nodes. - auto graph_sim_mobility = - mio::make_mobility_sim(t0, dt, - mio::Graph>>, - mio::MobilityEdge>()); + auto graph_sim_mobility = mio::make_mobility_sim( + t0, dt, + mio::Graph>>, + mio::MobilityEdge>()); auto& g_mobility = graph_sim_mobility.get_graph(); g_mobility.add_node(0, model1, t0); @@ -67,10 +67,9 @@ TEST(TestMobility, compareNoMobilityWithSingleIntegration) g_mobility.add_edge(1, 0, Eigen::VectorXd::Constant(4, 0)); // Define graph simulation that doesn't consider mobility even if we have edges that allow mobility. - auto graph_sim_no_mobility = - mio::make_no_mobility_sim(t0, - mio::Graph>>, - mio::MobilityEdge>()); + auto graph_sim_no_mobility = mio::make_no_mobility_sim( + t0, mio::Graph>>, + mio::MobilityEdge>()); auto& g_no_mobility = graph_sim_no_mobility.get_graph(); @@ -133,7 +132,8 @@ TEST(TestMobility, nodeAdvance) Model model(1); auto& params = model.parameters; - auto& cm = static_cast(model.parameters.get>()); + auto& cm = + static_cast&>(model.parameters.get>()); cm[0].get_minimum()(0, 0) = 5.0; model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Exposed}] = 100; @@ -145,7 +145,7 @@ TEST(TestMobility, nodeAdvance) double t0 = 2.835; double dt = 0.5; - mio::SimulationNode> node(model, t0); + mio::SimulationNode> node(model, t0); node.advance(t0, dt); ASSERT_DOUBLE_EQ(node.get_result().get_last_time(), t0 + dt); ASSERT_EQ(print_wrap(node.get_result().get_last_value()), print_wrap(node.get_last_state())); @@ -158,7 +158,8 @@ TEST(TestMobility, edgeApplyMobility) //setup nodes Model model(1); auto& params = model.parameters; - auto& cm = static_cast(model.parameters.get>()); + auto& cm = + static_cast&>(model.parameters.get>()); cm[0].get_baseline()(0, 0) = 5.0; model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = 10; @@ -173,8 +174,8 @@ TEST(TestMobility, edgeApplyMobility) params.get>()[(mio::AgeGroup)0] = 1.; params.apply_constraints(); double t = 3.125; - mio::SimulationNode> node1(model, t); - mio::SimulationNode> node2(model, t); + mio::SimulationNode> node1(model, t); + mio::SimulationNode> node2(model, t); //setup edge mio::MobilityEdge edge(Eigen::VectorXd::Constant(10, 0.1)); @@ -221,7 +222,8 @@ TEST(TestMobility, add_mobility_result_time_point) const size_t num_groups = 1; Model model(num_groups); auto& params = model.parameters; - auto& cm = static_cast(model.parameters.get>()); + auto& cm = + static_cast&>(model.parameters.get>()); cm[0].get_baseline()(0, 0) = 5.0; model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedNoSymptoms}] = 10; @@ -260,9 +262,9 @@ TEST(TestMobility, add_mobility_result_time_point) //setup different edges double t = 0.; - mio::SimulationNode> node1(model, t); - mio::SimulationNode> node2(model, t); - mio::MobilityEdge edge1(Eigen::VectorXd::Constant(10, 0.1), indices_save_edges); + mio::SimulationNode> node1(model, t); + mio::SimulationNode> node2(model, t); + mio::MobilityEdge edge1(Eigen::VectorXd::Constant(10, 0.1), indices_save_edges); edge1.apply_mobility(t, 0.0, node1, node2); auto mobility = edge1.get_mobility_results().get_last_value(); EXPECT_NEAR(mobility[0], 1.0, 1e-12); @@ -271,9 +273,9 @@ TEST(TestMobility, add_mobility_result_time_point) model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedNoSymptomsConfirmed}] = 100; model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptomsConfirmed}] = 30; - mio::SimulationNode> node3(model, t); - mio::SimulationNode> node4(model, t); - mio::MobilityEdge edge2(Eigen::VectorXd::Constant(10, 0.1), indices_save_edges); + mio::SimulationNode> node3(model, t); + mio::SimulationNode> node4(model, t); + mio::MobilityEdge edge2(Eigen::VectorXd::Constant(10, 0.1), indices_save_edges); edge2.apply_mobility(t, 0.5, node3, node4); mobility = edge2.get_mobility_results().get_last_value(); EXPECT_NEAR(mobility[0], 11.0, 1e-12); diff --git a/cpp/tests/test_mobility_io.cpp b/cpp/tests/test_mobility_io.cpp index f593a3a2c2..b1df3625da 100644 --- a/cpp/tests/test_mobility_io.cpp +++ b/cpp/tests/test_mobility_io.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Wadim Koslow diff --git a/cpp/tests/test_model.cpp b/cpp/tests/test_model.cpp index 636b2f283c..2606013e63 100644 --- a/cpp/tests/test_model.cpp +++ b/cpp/tests/test_model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert diff --git a/cpp/tests/test_numericalIntegration.cpp b/cpp/tests/test_numericalIntegration.cpp index 040b619432..fcc5ef4b99 100755 --- a/cpp/tests/test_numericalIntegration.cpp +++ b/cpp/tests/test_numericalIntegration.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -33,6 +33,7 @@ #include #include +#include void sin_deriv(Eigen::Ref /*y*/, const double t, Eigen::Ref dydt) { @@ -46,7 +47,7 @@ class TestVerifyNumericalIntegrator : public testing::Test void SetUp() override { t0 = 0.; - tmax = 2 * std::acos(-1); // 2PI + tmax = 2 * std::numbers::pi_v; // 2PI err = 0; mio::set_log_level(mio::LogLevel::off); } @@ -268,8 +269,8 @@ class MockIntegratorCore : public mio::OdeIntegratorCore (const mio::DerivFunction& f, Eigen::Ref yt, double& t, double& dt, Eigen::Ref ytp1), (const)); - - std::unique_ptr> clone() const override + + std::unique_ptr> clone() const override { throw std::runtime_error("MockIntegratorCore clone() called unexpectedly"); } @@ -354,11 +355,11 @@ TEST(TestOdeIntegrator, integratorContinuesAtLastState) using testing::_; using testing::Eq; - double dt0 = 0.25; - double dt = dt0; - auto dy = Eigen::VectorXd::Constant(1, 1); - auto y0 = Eigen::VectorXd::Constant(1, 0); - auto mock_core = std::make_unique>(); + double dt0 = 0.25; + double dt = dt0; + auto dy = Eigen::VectorXd::Constant(1, 1); + auto y0 = Eigen::VectorXd::Constant(1, 0); + auto mock_core = std::make_unique>(); { testing::InSequence seq; @@ -381,24 +382,23 @@ TEST(TestOdeIntegrator, integratorForcesLastStepSize) using testing::_; using testing::Eq; - const double dt_min = 0.7; - double dt = 0.5; // this is on purpose < dt_min - const double t_max = 3.0; // must not be an integer multiple of dt_min - auto mock_core = std::make_unique>(dt_min, t_max); - auto f = [](auto&&, auto&&, auto&&) {}; - auto step_fct = [&mock = *mock_core](auto&&, auto&&, auto& t_, auto& dt_, auto&&) { + const double dt_min = 0.7; + double dt = 0.5; // this is on purpose < dt_min + const double t_max = 3.0; // must not be an integer multiple of dt_min + auto mock_core = std::make_unique>(dt_min, t_max); + auto f = [](auto&&, auto&&, auto&&) {}; + auto step_fct = [&mock = *mock_core](auto&&, auto&&, auto& t_, auto& dt_, auto&&) { dt_ = std::max(dt_, mock.get_dt_min()); t_ += dt_; return true; }; - const auto num_calls = Eigen::Index(t_max / dt_min) + 1; EXPECT_CALL(*mock_core, step).Times(num_calls).WillRepeatedly(testing::Invoke(step_fct)); // run a mock integration to examine whether only the last step is forced mio::TimeSeries mock_result(0, Eigen::VectorXd::Constant(1, 0)); - auto integrator = mio::OdeIntegrator(std::move(mock_core)); + auto integrator = mio::OdeIntegrator(std::move(mock_core)); integrator.advance(f, t_max, dt, mock_result); EXPECT_EQ(mock_result.get_num_time_points(), num_calls + 1); for (Eigen::Index i = 0; i < mock_result.get_num_time_points(); i++) { diff --git a/cpp/tests/test_odeseair.cpp b/cpp/tests/test_odeseair.cpp index 0d8e022d1d..07c6686921 100755 --- a/cpp/tests/test_odeseair.cpp +++ b/cpp/tests/test_odeseair.cpp @@ -177,7 +177,7 @@ TEST(TestOdeSeair, testParameterConstraints) { // Deactivate temporarily log output for next tests. mio::set_log_level(mio::LogLevel::off); - mio::oseair::Parameters<> parameters; + mio::oseair::Parameters parameters; double negative_value = -0.5; // Check if default parameters are valid. @@ -185,52 +185,52 @@ TEST(TestOdeSeair, testParameterConstraints) EXPECT_FALSE(constraint_check); // Check improper SocialDistancing. - parameters.get>() = negative_value; - constraint_check = parameters.check_constraints(); + parameters.get>() = negative_value; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 0.5; + parameters.get>() = 0.5; // Check improper Quarantined. - parameters.get>() = negative_value; - constraint_check = parameters.check_constraints(); + parameters.get>() = negative_value; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 0.5; + parameters.get>() = 0.5; // Check improper TimeExposed. - parameters.get>() = 0; - constraint_check = parameters.check_constraints(); + parameters.get>() = 0; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 3.1; + parameters.get>() = 3.1; // Check improper RecoveryRateFromAsymptomatic. - parameters.get>() = negative_value; - constraint_check = parameters.check_constraints(); + parameters.get>() = negative_value; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 0.7 / 5.; + parameters.get>() = 0.7 / 5.; // Check improper TestingRate. - parameters.get>() = negative_value; - constraint_check = parameters.check_constraints(); + parameters.get>() = negative_value; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 0.3 / 5.; + parameters.get>() = 0.3 / 5.; // Check improper RecoveryRate. - parameters.get>() = negative_value; - constraint_check = parameters.check_constraints(); + parameters.get>() = negative_value; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 0.5 / 7.; + parameters.get>() = 0.5 / 7.; // Check improper DeathRate. - parameters.get>() = negative_value; - constraint_check = parameters.check_constraints(); + parameters.get>() = negative_value; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 0.5 / 7.; + parameters.get>() = 0.5 / 7.; // Check improper TimeRecoveredInv. - parameters.get>() = negative_value; - constraint_check = parameters.check_constraints(); + parameters.get>() = negative_value; + constraint_check = parameters.check_constraints(); EXPECT_TRUE(constraint_check); - parameters.get>() = 0.; + parameters.get>() = 0.; // Reactive log output. mio::set_log_level(mio::LogLevel::warn); diff --git a/cpp/tests/test_odesecir.cpp b/cpp/tests/test_odesecir.cpp index b59da559b1..24533cc68d 100755 --- a/cpp/tests/test_odesecir.cpp +++ b/cpp/tests/test_odesecir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -31,6 +31,8 @@ #include "memilio/math/adapt_rk.h" #include "memilio/geography/regions.h" +#include + #include TEST(TestOdeSecir, compareWithPreviousRun) @@ -50,7 +52,7 @@ TEST(TestOdeSecir, compareWithPreviousRun) mio::osecir::Model model(1); - model.parameters.set(60); + model.parameters.set>(60); model.parameters.set>(0.2); model.parameters.get>()[(mio::AgeGroup)0] = 3.2; @@ -59,9 +61,9 @@ TEST(TestOdeSecir, compareWithPreviousRun) model.parameters.get>()[(mio::AgeGroup)0] = 9.5; model.parameters.get>()[(mio::AgeGroup)0] = 7.1; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); model.populations.set_total(nb_total_t0); model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Exposed}] = nb_exp_t0; @@ -115,7 +117,7 @@ TEST(TestOdeSecir, simulateDefault) double dt = 0.1; mio::osecir::Model model(1); - mio::TimeSeries result = simulate(t0, tmax, dt, model); + mio::TimeSeries result = mio::simulate(t0, tmax, dt, model); EXPECT_NEAR(result.get_last_time(), tmax, 1e-10); } @@ -140,9 +142,9 @@ TEST(TestOdeSecir, checkPopulationConservation) model.parameters.get>()[(mio::AgeGroup)0] = 9.5; model.parameters.get>()[(mio::AgeGroup)0] = 7.1; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); model.populations.set_total(nb_total_t0); model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Exposed}] = 10; @@ -168,7 +170,7 @@ TEST(TestOdeSecir, checkPopulationConservation) model.apply_constraints(); - mio::TimeSeries secir = simulate(t0, tmax, dt, model); + mio::TimeSeries secir = mio::simulate(t0, tmax, dt, model); double num_persons = 0.0; for (auto i = 0; i < secir.get_last_value().size(); i++) { @@ -192,7 +194,7 @@ TEST(TestOdeSecir, testParamConstructors) model.parameters.set>(icu_cap); - model.parameters.set(start_day); + model.parameters.set>(start_day); model.parameters.set>(seasonality); model.parameters.get>()[(mio::AgeGroup)0] = 3.2; @@ -220,15 +222,16 @@ TEST(TestOdeSecir, testParamConstructors) model.parameters.get>()[(mio::AgeGroup)0] = 0.24; model.parameters.get>()[(mio::AgeGroup)0] = 0.3; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); mio::osecir::Model model2{model}; // copy constructor EXPECT_EQ(model.parameters.get>(), model2.parameters.get>()); - EXPECT_EQ(model.parameters.get(), model2.parameters.get()); + EXPECT_EQ(model.parameters.get>(), + model2.parameters.get>()); EXPECT_EQ(model.parameters.get>(), model2.parameters.get>()); @@ -284,7 +287,8 @@ TEST(TestOdeSecir, testParamConstructors) EXPECT_EQ(model.parameters.get>(), model3.parameters.get>()); - EXPECT_EQ(model.parameters.get(), model3.parameters.get()); + EXPECT_EQ(model.parameters.get>(), + model3.parameters.get>()); EXPECT_EQ(model.parameters.get>(), model3.parameters.get>()); @@ -339,7 +343,8 @@ TEST(TestOdeSecir, testParamConstructors) EXPECT_EQ(model4.parameters.get>(), model3.parameters.get>()); - EXPECT_EQ(model4.parameters.get(), model3.parameters.get()); + EXPECT_EQ(model4.parameters.get>(), + model3.parameters.get>()); EXPECT_EQ(model4.parameters.get>(), model3.parameters.get>()); @@ -394,7 +399,8 @@ TEST(TestOdeSecir, testParamConstructors) EXPECT_EQ(model5.parameters.get>(), model3.parameters.get>()); - EXPECT_EQ(model5.parameters.get(), model3.parameters.get()); + EXPECT_EQ(model5.parameters.get>(), + model3.parameters.get>()); EXPECT_EQ(model5.parameters.get>(), model3.parameters.get>()); @@ -490,7 +496,7 @@ TEST(TestOdeSecir, testSettersAndGetters) check_distribution(*vec[0].get_distribution(), *model.parameters.get>().get_distribution()); - model.parameters.set(vec[20]); + model.parameters.set>(vec[20]); model.parameters.set>(vec[21]); check_distribution(*vec[1].get_distribution(), @@ -571,7 +577,7 @@ TEST(TestOdeSecir, testSettersAndGetters) EXPECT_EQ(vec[17], model.parameters.get>()[(mio::AgeGroup)0]); EXPECT_EQ(vec[18], model.parameters.get>()[(mio::AgeGroup)0]); EXPECT_EQ(vec[19], model.parameters.get>()[(mio::AgeGroup)0]); - EXPECT_EQ(vec[20], model.parameters.get()); + EXPECT_EQ(vec[20], model.parameters.get>()); EXPECT_EQ(vec[21], model.parameters.get>()); } @@ -610,51 +616,59 @@ TEST(TestOdeSecir, testDamping) double nb_total_t0 = 1000, nb_inf_t0 = 10; // default model run to be compared against - mio::osecir::Model model_a(1); + mio::osecir::Model model_a(1); model_a.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_a.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_a = model_a.parameters.get>(); - contact_matrix_a[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + mio::ContactMatrixGroup& contact_matrix_a = + model_a.parameters.get>(); + contact_matrix_a[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); // set probability of transmission and risk of infection to 1. model_a.parameters.get>() = 1.0; model_a.parameters.get>() = 1.0; - auto result_a = mio::simulate_flows(t0, tmax, dt, model_a, std::make_unique>()); + auto result_a = mio::simulate_flows(t0, tmax, dt, model_a, + std::make_unique>()); // reduced transmission - mio::osecir::Model model_b{model_a}; + mio::osecir::Model model_b{model_a}; model_b.populations.set_total(nb_total_t0); model_b.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_b.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_b = model_b.parameters.get>(); - contact_matrix_b[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix_b[0].add_damping(0.5, mio::SimulationTime(0.)); - auto result_b = mio::simulate_flows(t0, tmax, dt, model_b, std::make_unique>()); + mio::ContactMatrixGroup& contact_matrix_b = + model_b.parameters.get>(); + contact_matrix_b[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + contact_matrix_b[0].add_damping(0.5, mio::SimulationTime(0.)); + auto result_b = mio::simulate_flows(t0, tmax, dt, model_b, + std::make_unique>()); EXPECT_EQ(2 * result_b[1].get_last_value()[0], result_a[1].get_last_value()[0]); // no transmission - mio::osecir::Model model_c{model_a}; + mio::osecir::Model model_c{model_a}; model_c.populations.set_total(nb_total_t0); model_c.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_c.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_c = model_c.parameters.get>(); - contact_matrix_c[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix_c[0].add_damping(1., mio::SimulationTime(0.)); - auto result_c = mio::simulate_flows(t0, tmax, dt, model_c, std::make_unique>()); + mio::ContactMatrixGroup& contact_matrix_c = + model_c.parameters.get>(); + contact_matrix_c[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + contact_matrix_c[0].add_damping(1., mio::SimulationTime(0.)); + auto result_c = mio::simulate_flows(t0, tmax, dt, model_c, + std::make_unique>()); EXPECT_EQ(result_c[1].get_last_value()[0], 0.0); // increased transmission to a factor of two (by +1) - mio::osecir::Model model_d{model_a}; + mio::osecir::Model model_d{model_a}; model_d.populations.set_total(nb_total_t0); model_d.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedSymptoms}] = nb_inf_t0; model_d.populations.set_difference_from_total({mio::AgeGroup(0), mio::osecir::InfectionState::Susceptible}, nb_total_t0); - mio::ContactMatrixGroup& contact_matrix_d = model_d.parameters.get>(); - contact_matrix_d[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); - contact_matrix_d[0].add_damping(-1., mio::SimulationTime(0.)); - auto result_d = mio::simulate_flows(t0, tmax, dt, model_d, std::make_unique>()); + mio::ContactMatrixGroup& contact_matrix_d = + model_d.parameters.get>(); + contact_matrix_d[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + contact_matrix_d[0].add_damping(-1., mio::SimulationTime(0.)); + auto result_d = mio::simulate_flows(t0, tmax, dt, model_d, + std::make_unique>()); EXPECT_EQ(2 * result_a[1].get_last_value()[0], result_d[1].get_last_value()[0]); } @@ -702,12 +716,12 @@ TEST(TestOdeSecir, testModelConstraints) model.parameters.get>()[(mio::AgeGroup)0] = 0.25; model.parameters.get>()[(mio::AgeGroup)0] = 0.3; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); model.apply_constraints(); - mio::TimeSeries secihurd = simulate(t0, tmax, dt, model); + mio::TimeSeries secihurd = mio::simulate(t0, tmax, dt, model); double max_icu_cap = 0; for (Eigen::Index i = 0; i < secihurd.get_num_time_points(); i++) { if (secihurd.get_value(i)[5] > max_icu_cap) { @@ -718,19 +732,19 @@ TEST(TestOdeSecir, testModelConstraints) mio::TimeSeries secihurd_interp = mio::interpolate_simulation_result(secihurd); // Tests that infection numbers are higher in Winter season - model.parameters.set(100); + model.parameters.set>(100); model.parameters.set>(0.5); - mio::TimeSeries secihurd_season = simulate(t0, tmax, dt, model); + mio::TimeSeries secihurd_season = mio::simulate(t0, tmax, dt, model); mio::TimeSeries secihurd_season_interp = mio::interpolate_simulation_result(secihurd_season); for (Eigen::Index i = 0; i < secihurd_interp.get_num_time_points(); i++) { EXPECT_LE(secihurd_season_interp.get_value(i)[3], secihurd_interp.get_value(i)[3]) << " at row " << i; } - model.parameters.set(280); + model.parameters.set>(280); - mio::TimeSeries secihurd_season2 = simulate(t0, tmax, dt, model); + mio::TimeSeries secihurd_season2 = mio::simulate(t0, tmax, dt, model); mio::TimeSeries secihurd_season2_interp = mio::interpolate_simulation_result(secihurd_season2); for (Eigen::Index i = 0; i < secihurd_interp.get_num_time_points(); i++) { @@ -739,11 +753,11 @@ TEST(TestOdeSecir, testModelConstraints) // temporary test for random variables set_params_distributions_normal(model, t0, tmax, 0.2); - model.parameters.set>(mio::UncertainValue(0.0)); - model.parameters.set>(mio::UncertainValue(8000)); + model.parameters.set>(mio::UncertainValue(0.0)); + model.parameters.set>(mio::UncertainValue(8000)); for (size_t j = 0; j < 10; j++) { draw_sample(model); - secihurd = simulate(t0, tmax, dt, model); + secihurd = mio::simulate(t0, tmax, dt, model); for (Eigen::Index i = 0; i < secihurd.get_num_time_points(); i++) { EXPECT_LE(secihurd.get_value(i)[5], 9000) << " at row " << i; } @@ -764,8 +778,8 @@ TEST(TestOdeSecir, testAndTraceCapacity) params.get>()[(mio::AgeGroup)0] = 2.0; params.get>()[(mio::AgeGroup)0] = 6.; - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, cont_freq)); model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::Exposed}] = nb_exp_t0; model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedNoSymptoms}] = nb_car_t0; @@ -815,7 +829,7 @@ TEST(TestOdeSecir, getInfectionsRelative) model.populations.set_difference_from_group_total( {mio::AgeGroup(2), mio::osecir::InfectionState::Susceptible}, 40'000.0); - mio::osecir::Simulation<> sim(model, 0.0); + mio::osecir::Simulation sim(model, 0.0); ASSERT_EQ(mio::osecir::get_infections_relative(sim, 0.0, sim.get_result().get_last_value()), (100. + 50. + 25.) / (10'000 + 20'000 + 40'000)); } @@ -823,12 +837,12 @@ TEST(TestOdeSecir, getInfectionsRelative) TEST(TestOdeSecir, get_reproduction_number) { const size_t num_groups = 3; - mio::osecir::Model model((int)num_groups); + mio::osecir::Model model((int)num_groups); - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 10)); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(3, 3, 10)); - model.parameters.set(60); + model.parameters.set>(60); model.parameters.set>(0.2); //total population of 10.000 @@ -924,7 +938,7 @@ TEST(TestOdeSecir, get_reproduction_number) time_series1.add_time_point(0.8000000000000000000, result_5); time_series1.add_time_point(1.0, result_6); - mio::osecir::Simulation<> sim(model, 0.0); + mio::osecir::Simulation sim(model, 0.0); sim.get_result() = time_series1; EXPECT_FALSE( @@ -946,12 +960,13 @@ TEST(TestOdeSecir, get_reproduction_number) //Test handling non-invertibility of V for certain values mio::TimeSeries::Vector result_7((int)mio::osecir::InfectionState::Count * num_groups); double icu_occupancy = 0.95 * model.parameters.get>(); - double severe1 = model.parameters.get>()[(mio::AgeGroup)0] / - (model.parameters.get>()[(mio::AgeGroup)0] * 5 * - model.parameters.get>()[(mio::AgeGroup)1] * - 3.141592653589793 / (model.parameters.get>()) * - std::sin(3.141592653589793 / (0.1 * model.parameters.get>()) * - (icu_occupancy - 0.9 * model.parameters.get>()))); + double severe1 = + model.parameters.get>()[(mio::AgeGroup)0] / + (model.parameters.get>()[(mio::AgeGroup)0] * 5 * + model.parameters.get>()[(mio::AgeGroup)1] * + std::numbers::pi_v / (model.parameters.get>()) * + std::sin(std::numbers::pi_v / (0.1 * model.parameters.get>()) * + (icu_occupancy - 0.9 * model.parameters.get>()))); mio::TimeSeries time_series2((int)mio::osecir::InfectionState::Count * num_groups); result_7 << 1000, 0, 0, 0, 0, 0, severe1, 0.95 * model.parameters.get>(), 0, 0, @@ -964,13 +979,13 @@ TEST(TestOdeSecir, get_reproduction_number) //Test for small test and trace model.parameters.get>() = 0; - mio::osecir::Simulation<> sim2(model, 0.0); + mio::osecir::Simulation sim2(model, 0.0); sim2.get_result() = time_series1; EXPECT_NEAR(mio::osecir::get_reproduction_number((size_t)0, sim2).value(), 5.1941804908632792, 1e-12); // Test special domain for test-and-trace capacity/requirement: model.parameters.get>() = 1; - mio::osecir::Simulation<> sim3(model, 0.0); + mio::osecir::Simulation sim3(model, 0.0); mio::TimeSeries time_series3((int)mio::osecir::InfectionState::Count * num_groups); mio::TimeSeries::Vector result_8((int)mio::osecir::InfectionState::Count * num_groups); result_8 << 100, 0, 10, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0; @@ -989,16 +1004,16 @@ TEST(TestOdeSecir, get_reproduction_number) TEST(Secir, get_mobility_factors) { - auto beta = 0.25; - auto max_beta = 0.5; - auto model = mio::osecir::Model(1); - model.parameters.get>().array() = 3.; - model.parameters.get>().array() = 2.; - model.parameters.get>().array() = 0.1; - model.parameters.get>().array() = beta; + auto beta = 0.25; + auto max_beta = 0.5; + auto model = mio::osecir::Model(1); + model.parameters.get>().array() = 3.; + model.parameters.get>().array() = 2.; + model.parameters.get>().array() = 0.1; + model.parameters.get>().array() = beta; model.parameters.get>().array() = max_beta; model.populations[{mio::AgeGroup(0), mio::osecir::InfectionState::InfectedNoSymptoms}] = 100; - mio::osecir::Simulation<> sim(model, 0.0); + mio::osecir::Simulation sim(model, 0.0); { sim.get_model().parameters.get>() = 45.; auto factors = mio::osecir::get_mobility_factors(sim, 0.0, sim.get_result().get_last_value()); @@ -1023,13 +1038,13 @@ TEST(Secir, get_mobility_factors) TEST(TestOdeSecir, test_commuters) { - auto model = mio::osecir::Model(2); + auto model = mio::osecir::Model(2); auto mobility_factor = 0.1; auto non_detection_factor = 0.4; model.parameters.get_start_commuter_detection() = 0.0; model.parameters.get_end_commuter_detection() = 20.0; model.parameters.get_commuter_nondetection() = non_detection_factor; - auto sim = mio::osecir::Simulation<>(model); + auto sim = mio::osecir::Simulation(model); auto before_testing = sim.get_result().get_last_value().eval(); auto mobile_population = (sim.get_result().get_last_value() * mobility_factor).eval(); auto mobile_population_tested = mobile_population.eval(); @@ -1135,7 +1150,7 @@ TEST(TestOdeSecir, check_constraints_parameters) TEST(TestOdeSecir, apply_constraints_parameters) { - auto model = mio::osecir::Model(1); + auto model = mio::osecir::Model(1); auto indx_agegroup = mio::AgeGroup(0); const double tol_times = 1e-1; @@ -1233,7 +1248,7 @@ class ModelTestOdeSecir : public testing::Test protected: void SetUp() override { - model.parameters.set(60); + model.parameters.set>(60); model.parameters.set>(0.2); for (auto i = mio::AgeGroup(0); i < (mio::AgeGroup)num_age_groups; i++) { diff --git a/cpp/tests/test_odesecir_ageres.cpp b/cpp/tests/test_odesecir_ageres.cpp index b437921f3e..4d8d4f0846 100755 --- a/cpp/tests/test_odesecir_ageres.cpp +++ b/cpp/tests/test_odesecir_ageres.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -45,7 +45,7 @@ TEST(TestOdeSecir, compareAgeResWithPreviousRun) auto& params = model.parameters; - params.set(60); + params.set>(60); params.set>(0.2); params.get>() = 35; @@ -78,10 +78,10 @@ TEST(TestOdeSecir, compareAgeResWithPreviousRun) params.apply_constraints(); - mio::ContactMatrixGroup& contact_matrix = params.get>(); + mio::ContactMatrixGroup& contact_matrix = params.get>(); contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); auto integrator = std::make_unique>(); integrator->set_dt_min(0.3); @@ -122,7 +122,7 @@ TEST(TestOdeSecir, compareAgeResWithPreviousRunCashKarp) mio::AgeGroup nb_groups = model.parameters.get_num_groups(); double fact = 1.0 / (double)(size_t)nb_groups; - model.parameters.set(60); + model.parameters.set>(60); model.parameters.set>(0.2); auto& params = model.parameters; @@ -158,10 +158,10 @@ TEST(TestOdeSecir, compareAgeResWithPreviousRunCashKarp) params.apply_constraints(); - mio::ContactMatrixGroup& contact_matrix = params.get>(); + mio::ContactMatrixGroup& contact_matrix = params.get>(); contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); auto integrator = std::make_unique>(); diff --git a/cpp/tests/test_odesecirts.cpp b/cpp/tests/test_odesecirts.cpp index 5a1f72f34f..4bf0d0e1a8 100755 --- a/cpp/tests/test_odesecirts.cpp +++ b/cpp/tests/test_odesecirts.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker @@ -108,11 +108,11 @@ const mio::osecirts::Model& osecirts_testing_model() } } - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - const double cont_freq = 1; - const double fact = 1.0 / (double)(size_t)nb_groups; + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + const double cont_freq = 1; + const double fact = 1.0 / (double)(size_t)nb_groups; contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, fact * cont_freq)); return model; } @@ -289,7 +289,7 @@ TEST(TestOdeSECIRTS, smooth_vaccination_rate) const ScalarType tmax = 2.; // init simple model - mio::osecirts::Model model(1); + mio::osecirts::Model model(1); auto& daily_vaccinations = model.parameters.get>(); daily_vaccinations.resize(mio::SimulationDay(static_cast(tmax + 1))); @@ -350,7 +350,7 @@ void assign_uniform_distribution(mio::UncertainValue& p, double min, dou auto invalid_initial = max == 0 ? 1.0 : max * 1.001; auto valid_initial = (max + min) * 0.5; auto initial = set_invalid_initial_value ? invalid_initial : valid_initial; - p = mio::UncertainValue(initial); + p = mio::UncertainValue(initial); p.set_distribution(mio::ParameterDistributionUniform(min, max)); } @@ -453,17 +453,17 @@ void set_contact_parameters(mio::osecirts::Model::ParameterSet& paramete auto& contact_matrix = contacts.get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(0.5); contact_matrix[0].get_baseline().diagonal().setConstant(5.0); - contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); + contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); auto& npis = parameters.get>(); auto npi_groups = Eigen::VectorXd::Ones(contact_matrix[0].get_num_groups()); - auto npi_value = mio::UncertainValue(0.5); + auto npi_value = mio::UncertainValue(0.5); assign_uniform_distribution(npi_value, 0.25, 0.75, set_invalid_initial_value); npis.set_threshold(10.0, {mio::DampingSampling(npi_value, mio::DampingLevel(0), mio::DampingType(0), - mio::SimulationTime(0), {0}, npi_groups)}); + mio::SimulationTime(0), {0}, npi_groups)}); npis.set_base_value(100'000); - npis.set_interval(mio::SimulationTime(3.0)); - npis.set_duration(mio::SimulationTime(14.0)); + npis.set_interval(mio::SimulationTime(3.0)); + npis.set_duration(mio::SimulationTime(14.0)); parameters.get_end_dynamic_npis() = 10.0; //required for dynamic NPIs to have effect in this model } @@ -590,7 +590,7 @@ namespace mio::osecirts::Model make_model(int num_age_groups, bool set_invalid_initial_value = false) { assert(num_age_groups <= 6 && "Provide more values in functions above to test more age groups."); - mio::osecirts::Model model(num_age_groups); + mio::osecirts::Model model(num_age_groups); set_covid_parameters(model.parameters, set_invalid_initial_value); set_synthetic_population_data(model.populations, set_invalid_initial_value); set_demographic_parameters(model.parameters, set_invalid_initial_value); @@ -1242,7 +1242,7 @@ TEST(TestOdeSECIRTS, test_commuters) model.parameters.get_start_commuter_detection() = 0.0; model.parameters.get_end_commuter_detection() = 20.0; model.parameters.get_commuter_nondetection() = non_detection_factor; - auto sim = mio::osecirts::Simulation<>(model); + auto sim = mio::osecirts::Simulation(model); auto before_testing = sim.get_result().get_last_value().eval(); auto migrated = (sim.get_result().get_last_value() * migration_factor).eval(); auto migrated_tested = migrated.eval(); @@ -1454,7 +1454,7 @@ TEST(TestOdeSECIRTS, check_constraints_parameters) TEST(TestOdeSECIRTS, apply_constraints_parameters) { const double tol_times = 1e-1; - auto model = mio::osecirts::Model(1); + auto model = mio::osecirts::Model(1); auto indx_agegroup = mio::AgeGroup(0); EXPECT_EQ(model.parameters.apply_constraints(), 0); @@ -1601,8 +1601,8 @@ TEST(TestOdeSECIRTS, apply_variant_function) auto model = mio::osecirts::Model(1); model.parameters.set>(0.2); - model.parameters.set(0); - model.parameters.set(10); + model.parameters.set>(0); + model.parameters.set>(10); model.parameters.set>(2.0); // set vaccinations diff --git a/cpp/tests/test_odesecirvvs.cpp b/cpp/tests/test_odesecirvvs.cpp index 0ca44387c4..838b15ed2b 100755 --- a/cpp/tests/test_odesecirvvs.cpp +++ b/cpp/tests/test_odesecirvvs.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -65,7 +65,7 @@ TEST(TestOdeSECIRVVS, simulateDefault) model.parameters.get>().array().setConstant(0); model.parameters.get>().resize(mio::SimulationDay(size_t(1000))); model.parameters.get>().array().setConstant(0); - mio::TimeSeries result = simulate(t0, tmax, dt, model); + mio::TimeSeries result = mio::simulate(t0, tmax, dt, model); EXPECT_NEAR(result.get_last_time(), tmax, 1e-10); } @@ -122,8 +122,8 @@ TEST(TestOdeSECIRVVS, reduceToSecirAndCompareWithPreviousRun) auto& contacts = model.parameters.get>(); auto& contact_matrix = contacts.get_cont_freq_mat(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(1, 1, 10)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); //times model.parameters.get>()[mio::AgeGroup(0)] = 3.2; @@ -163,7 +163,7 @@ TEST(TestOdeSECIRVVS, reduceToSecirAndCompareWithPreviousRun) // integrator->set_dt_max(1.0); // integrator->set_rel_tolerance(1e-4); // integrator->set_abs_tolerance(1e-1); - // mio::TimeSeries secihurd = simulate(t0, tmax, 0.1, model, std::move(integrator)); + // mio::TimeSeries secihurd = mio::simulate(t0, tmax, 0.1, model, std::move(integrator)); // auto compare = load_test_data_csv("secihurd-compare.csv"); @@ -282,17 +282,17 @@ void set_contact_parameters(mio::osecirvvs::Model::ParameterSet& paramet auto& contact_matrix = contacts.get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(0.5); contact_matrix[0].get_baseline().diagonal().setConstant(5.0); - contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); + contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); auto& npis = parameters.get>(); auto npi_groups = Eigen::VectorXd::Ones(contact_matrix[0].get_num_groups()); auto npi_value = mio::UncertainValue(0.5); assign_uniform_distribution(npi_value, 0.25, 0.75, set_invalid_initial_value); npis.set_threshold(10.0, {mio::DampingSampling(npi_value, mio::DampingLevel(0), mio::DampingType(0), - mio::SimulationTime(0), {0}, npi_groups)}); + mio::SimulationTime(0), {0}, npi_groups)}); npis.set_base_value(100'000); - npis.set_interval(mio::SimulationTime(3.0)); - npis.set_duration(mio::SimulationTime(14.0)); + npis.set_interval(mio::SimulationTime(3.0)); + npis.set_duration(mio::SimulationTime(14.0)); parameters.get_end_dynamic_npis() = 10.0; //required for dynamic NPIs to have effect in this model parameters.template get>() = 7; } @@ -1233,7 +1233,7 @@ TEST(TestOdeSECIRVVS, get_mobility_factors) { auto num_age_groups = 2; auto model = make_model(num_age_groups); - auto sim = mio::osecirvvs::Simulation<>(model); + auto sim = mio::osecirvvs::Simulation(model); auto y = sim.get_result()[0]; auto mobility_factors = mio::osecirvvs::get_mobility_factors(sim, 0.0, y); @@ -1253,7 +1253,7 @@ TEST(TestOdeSECIRVVS, test_commuters) model.parameters.get_start_commuter_detection() = 0.0; model.parameters.get_end_commuter_detection() = 20.0; model.parameters.get_commuter_nondetection() = non_detection_factor; - auto sim = mio::osecirvvs::Simulation<>(model); + auto sim = mio::osecirvvs::Simulation(model); auto before_testing = sim.get_result().get_last_value().eval(); auto mobile_population = (sim.get_result().get_last_value() * mobility_factor).eval(); auto mobile_population_tested = mobile_population.eval(); @@ -1479,7 +1479,7 @@ TEST(TestOdeSECIRVVS, check_constraints_parameters) TEST(TestOdeSECIRVVS, apply_constraints_parameters) { const double tol_times = 1e-1; - auto model = mio::osecirvvs::Model(1); + auto model = mio::osecirvvs::Model(1); auto indx_agegroup = mio::AgeGroup(0); EXPECT_EQ(model.parameters.apply_constraints(), 0); @@ -1612,13 +1612,13 @@ TEST(TestOdeSECIRVVS, apply_constraints_parameters) TEST(TestOdeSECIRVVS, apply_variant_function) { - auto model = mio::osecirvvs::Model(1); + auto model = mio::osecirvvs::Model(1); model.parameters.set>(0.2); - model.parameters.set(0); - model.parameters.set(10); + model.parameters.set>(0); + model.parameters.set>(10); model.parameters.set>(2.0); - auto sim = mio::osecirvvs::Simulation<>(model); + auto sim = mio::osecirvvs::Simulation(model); // test that the transmission probability is not changed due to calling the advance function sim.advance(0.01); diff --git a/cpp/tests/test_odeseir.cpp b/cpp/tests/test_odeseir.cpp index 0cd27e79e4..4ab0ad3df8 100755 --- a/cpp/tests/test_odeseir.cpp +++ b/cpp/tests/test_odeseir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn, Martin Siggel, Henrik Zunker @@ -78,10 +78,10 @@ class ModelTestOdeSeir : public testing::Test model.parameters.set>(5.2); model.parameters.set>(2); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(2.7); - contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); + contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); } }; @@ -127,7 +127,7 @@ TEST(TestOdeSeir, apply_constraints_parameters) model.parameters.set>(5.2); model.parameters.set>(6); model.parameters.set>(0.04); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(10); @@ -164,7 +164,7 @@ TEST(TestOdeSeir, get_reproduction_numbers) model.parameters.set>(6); model.parameters.set>(0.04); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(10); @@ -257,7 +257,7 @@ TEST(TestOdeSeir, get_reproduction_number) model.parameters.set>(6); model.parameters.set>(0.04); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(10); @@ -347,7 +347,7 @@ TEST(TestSeir, get_flows) model.parameters.set>(2); model.parameters.set>(4); model.parameters.set>(1); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(1); model.check_constraints(); @@ -389,8 +389,9 @@ TEST(TestSeir, get_flows_two_agegroups) model.parameters.get>()[i] = 1; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 1.0)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 1.0)); model.check_constraints(); auto dydt_default = Eigen::VectorXd(6); @@ -426,7 +427,7 @@ TEST(TestSeir, Simulation) model.parameters.set>(2); model.parameters.set>(4); model.parameters.set>(1); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(1); @@ -450,7 +451,7 @@ TEST(TestSeir, FlowSimulation) double tmax = 1; double dt = 1; - mio::oseir::Model model(1); + mio::oseir::Model model(1); constexpr double total_population = 400; model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] = 100; @@ -464,7 +465,7 @@ TEST(TestSeir, FlowSimulation) model.parameters.set>(2); model.parameters.set>(4); model.parameters.set>(1); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(1); diff --git a/cpp/tests/test_odesir.cpp b/cpp/tests/test_odesir.cpp index 377de331dd..6f8a465e1e 100755 --- a/cpp/tests/test_odesir.cpp +++ b/cpp/tests/test_odesir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn, Martin Siggel, Henrik Zunker @@ -36,7 +36,7 @@ TEST(TestOdeSir, simulateDefault) double dt = 0.1; mio::osir::Model model(1); - mio::TimeSeries result = simulate(t0, tmax, dt, model); + mio::TimeSeries result = mio::simulate(t0, tmax, dt, model); EXPECT_NEAR(result.get_last_time(), tmax, 1e-10); } @@ -68,10 +68,10 @@ TEST(TestOdeSir, compareWithPreviousRun) model.parameters.get>().get_cont_freq_mat()[0].get_baseline().setConstant(2.7); model.parameters.get>().get_cont_freq_mat()[0].add_damping( - 0.6, mio::SimulationTime(12.5)); + 0.6, mio::SimulationTime(12.5)); - std::vector> refData = load_test_data_csv("ode-sir-compare.csv"); - auto result = mio::simulate(t0, tmax, dt, model, std::make_unique>()); + std::vector> refData = load_test_data_csv("ode-sir-compare.csv"); + auto result = mio::simulate(t0, tmax, dt, model, std::make_unique>()); ASSERT_EQ(refData.size(), static_cast(result.get_num_time_points())); @@ -128,7 +128,7 @@ TEST(TestOdeSir, checkPopulationConservation) model.parameters.get>().get_cont_freq_mat()[0].get_baseline().setConstant(2.7); model.parameters.get>().get_cont_freq_mat()[0].add_damping( - 0.6, mio::SimulationTime(12.5)); + 0.6, mio::SimulationTime(12.5)); auto result = mio::simulate>(t0, tmax, dt, model); double num_persons = 0.0; for (auto i = 0; i < result.get_last_value().size(); i++) { @@ -195,7 +195,7 @@ TEST(Testsir, get_derivatives) model.parameters.set>(4); model.parameters.set>(1); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(1); model.check_constraints(); @@ -235,7 +235,7 @@ TEST(Testsir, Simulation) double tmax = 1; double dt = 1; - mio::osir::Model model(1); + mio::osir::Model model(1); constexpr auto total_population = 400; model.populations[{mio::AgeGroup(0), mio::osir::InfectionState::Infected}] = 100; @@ -246,12 +246,12 @@ TEST(Testsir, Simulation) model.parameters.set>(4); model.parameters.set>(1); - mio::ContactMatrixGroup& contact_matrix = + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(1); model.check_constraints(); - auto sim = simulate(t0, tmax, dt, model, std::make_unique>()); + auto sim = simulate(t0, tmax, dt, model, std::make_unique>()); EXPECT_EQ(sim.get_num_time_points(), 2); diff --git a/cpp/tests/test_parameter_set.cpp b/cpp/tests/test_parameter_set.cpp index cb4bd75e49..99b9efff30 100644 --- a/cpp/tests/test_parameter_set.cpp +++ b/cpp/tests/test_parameter_set.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert @@ -102,11 +102,11 @@ struct NoDefaultMemberFunctionParam { }; struct MoveOnly { - MoveOnly() = default; - MoveOnly(const MoveOnly& other) = delete; + MoveOnly() = default; + MoveOnly(const MoveOnly& other) = delete; MoveOnly& operator=(const MoveOnly& other) = delete; MoveOnly(MoveOnly&& other) = default; - MoveOnly& operator=(MoveOnly&& other) = default; + MoveOnly& operator=(MoveOnly&& other) = default; }; struct MoveOnlyParam { diff --git a/cpp/tests/test_parameter_studies.cpp b/cpp/tests/test_parameter_studies.cpp index 200f6fe4aa..e0f46fbc5a 100644 --- a/cpp/tests/test_parameter_studies.cpp +++ b/cpp/tests/test_parameter_studies.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -74,9 +74,9 @@ TEST(ParameterStudies, sample_from_secir_params) params.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); + mio::ContactMatrixGroup& contact_matrix = params.get>(); contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); @@ -97,7 +97,7 @@ TEST(ParameterStudies, sample_from_secir_params) EXPECT_GE(params.get>()[i], 0); } - mio::ContactMatrixGroup& contact_matrix_sample = params.get>(); + mio::ContactMatrixGroup& contact_matrix_sample = params.get>(); EXPECT_EQ(contact_matrix_sample[0].get_dampings().size(), 1); } @@ -147,8 +147,8 @@ TEST(ParameterStudies, sample_graph) params.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); @@ -157,7 +157,7 @@ TEST(ParameterStudies, sample_graph) graph.add_node(1, model); graph.add_edge(0, 1, mio::MobilityParameters(Eigen::VectorXd::Constant(Eigen::Index(num_groups * 8), 1.0))); - auto study = mio::ParameterStudy>(graph, 0.0, 0.0, 0.5, 1); + auto study = mio::ParameterStudy>(graph, 0.0, 0.0, 0.5, 1); mio::log_rng_seeds(study.get_rng(), mio::LogLevel::warn); auto results = study.run([](auto&& g) { return draw_sample(g); @@ -365,12 +365,12 @@ TEST(ParameterStudies, check_ensemble_run_result) params.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); + mio::ContactMatrixGroup& contact_matrix = params.get>(); contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); - mio::ParameterStudy> parameter_study(model, t0, tmax, 1); + mio::ParameterStudy> parameter_study(model, t0, tmax, 1); mio::log_rng_seeds(parameter_study.get_rng(), mio::LogLevel::warn); // Run parameter study diff --git a/cpp/tests/test_populations.cpp b/cpp/tests/test_populations.cpp index e7160ad742..a031e294af 100644 --- a/cpp/tests/test_populations.cpp +++ b/cpp/tests/test_populations.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Jan Kleinert diff --git a/cpp/tests/test_random_number_generator.cpp b/cpp/tests/test_random_number_generator.cpp index 9a846ac01c..49fa12c451 100644 --- a/cpp/tests/test_random_number_generator.cpp +++ b/cpp/tests/test_random_number_generator.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/test_regions.cpp b/cpp/tests/test_regions.cpp index 757d4ca609..ec58b9d45a 100644 --- a/cpp/tests/test_regions.cpp +++ b/cpp/tests/test_regions.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/test_save_parameters.cpp b/cpp/tests/test_save_parameters.cpp index cb5dcfc57e..e08324a9ed 100644 --- a/cpp/tests/test_save_parameters.cpp +++ b/cpp/tests/test_save_parameters.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Wadim Koslow @@ -79,13 +79,13 @@ TEST(TestSaveParameters, json_single_sim_write_read_compare) model.parameters.get>()[i] = delta; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); + mio::ContactMatrixGroup& contact_matrix = params.get>(); contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); - contact_matrix.add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); + contact_matrix.add_damping(0.7, mio::SimulationTime(30.)); auto damping2 = Eigen::MatrixXd::Zero((size_t)num_groups, (size_t)num_groups).eval(); damping2(0, 0) = 0.8; - contact_matrix.add_damping(damping2, mio::SimulationTime(35)); + contact_matrix.add_damping(damping2, mio::SimulationTime(35)); mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); @@ -240,8 +240,8 @@ TEST(TestSaveParameters, read_graph_without_edges) params.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); auto graph = mio::Graph, mio::MobilityParameters>(); graph.add_node(0, model); @@ -319,8 +319,8 @@ TEST(TestSaveParameters, json_uncertain_matrix_write_read_compare) }; const auto start_date = mio::Date(2020, 12, 12); - auto damping_time1 = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2020, 12, 1), start_date)); - auto damping_time2 = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2020, 12, 24), start_date)); + auto damping_time1 = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2020, 12, 1), start_date)); + auto damping_time2 = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2020, 12, 24), start_date)); mio::osecir::Model model(2); auto& contacts = model.parameters.get>(); auto& contact_dampings = contacts.get_dampings(); @@ -341,14 +341,14 @@ TEST(TestSaveParameters, json_uncertain_matrix_write_read_compare) //add school_holiday_damping auto school_holiday_value = mio::UncertainValue(1); school_holiday_value.set_distribution(mio::ParameterDistributionUniform(1, 1)); - contacts.get_school_holiday_damping() = - mio::DampingSampling(school_holiday_value, mio::DampingLevel(int(InterventionLevelMock::level)), - mio::DampingType(int(InterventionMock::intervention)), mio::SimulationTime(0.0), - {size_t(ContactLocationMock::location)}, group_weights); + contacts.get_school_holiday_damping() = mio::DampingSampling( + school_holiday_value, mio::DampingLevel(int(InterventionLevelMock::level)), + mio::DampingType(int(InterventionMock::intervention)), mio::SimulationTime(0.0), + {size_t(ContactLocationMock::location)}, group_weights); //add school holidays - auto holiday_start_time = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2020, 12, 23), start_date)); - auto holiday_end_time = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2021, 1, 6), start_date)); + auto holiday_start_time = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2020, 12, 23), start_date)); + auto holiday_end_time = mio::SimulationTime(mio::get_offset_in_days(mio::Date(2021, 1, 6), start_date)); contacts.get_school_holidays() = {std::make_pair(holiday_start_time, holiday_end_time)}; //write json @@ -416,12 +416,12 @@ TEST(TestSaveParameters, json_graphs_write_read_compare) model.parameters.get>()[i] = delta; } - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); + mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * cont_freq)); Eigen::MatrixXd m = Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, 0.7).triangularView(); - contact_matrix.add_damping(m, mio::SimulationTime(30.)); + contact_matrix.add_damping(m, mio::SimulationTime(30.)); mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.15); @@ -447,11 +447,12 @@ TEST(TestSaveParameters, json_graphs_write_read_compare) ASSERT_EQ(num_edges, graph_read.edges().size()); for (size_t node = 0; node < num_nodes; node++) { - mio::osecir::Model graph_model = graph.nodes()[0].property; - mio::ContactMatrixGroup& graph_cont_matrix = graph_model.parameters.get>(); + mio::osecir::Model graph_model = graph.nodes()[0].property; + mio::ContactMatrixGroup& graph_cont_matrix = + graph_model.parameters.get>(); mio::osecir::Model graph_read_model = graph_read.nodes()[0].property; - mio::ContactMatrixGroup& graph_read_cont_matrix = + mio::ContactMatrixGroup& graph_read_cont_matrix = graph_read_model.parameters.get>(); ASSERT_EQ(graph_read_cont_matrix.get_num_groups(), static_cast((size_t)num_groups)); @@ -919,13 +920,13 @@ TEST(TestSaveParameters, json_write_read_parameters_secirvvs) model.parameters.get>()[i] = 0.5; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); + mio::ContactMatrixGroup& contact_matrix = params.get>(); contact_matrix[0] = - mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * 10)); - contact_matrix.add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)num_groups, (size_t)num_groups, fact * 10)); + contact_matrix.add_damping(0.7, mio::SimulationTime(30.)); auto damping2 = Eigen::MatrixXd::Zero((size_t)num_groups, (size_t)num_groups).eval(); damping2(0, 0) = 0.8; - contact_matrix.add_damping(damping2, mio::SimulationTime(35)); + contact_matrix.add_damping(damping2, mio::SimulationTime(35)); TempFileRegister file_register; auto filename = file_register.get_unique_path("TestParameters-%%%%-%%%%.json"); diff --git a/cpp/tests/test_save_results.cpp b/cpp/tests/test_save_results.cpp index 622a66e7c4..a15c62d419 100644 --- a/cpp/tests/test_save_results.cpp +++ b/cpp/tests/test_save_results.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Wadim Koslow, Henrik Zunker @@ -41,7 +41,7 @@ TEST(TestSaveResult, save_result) double nb_total_t0 = 10000, nb_exp_t0 = 100, nb_inf_t0 = 50, nb_car_t0 = 50, nb_hosp_t0 = 20, nb_icu_t0 = 10, nb_rec_t0 = 10, nb_dead_t0 = 0; - mio::osecir::Model model(1); + mio::osecir::Model model(1); auto& params = model.parameters; mio::AgeGroup nb_groups = params.get_num_groups(); ; @@ -71,11 +71,12 @@ TEST(TestSaveResult, save_result) params.get>()[i] = delta; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, cont_freq)); - contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = + mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, cont_freq)); + contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); - auto result_from_sim = simulate(t0, tmax, dt, model); + auto result_from_sim = mio::simulate(t0, tmax, dt, model); std::vector> results_from_sim = {result_from_sim, result_from_sim}; std::vector ids = {1, 2}; @@ -122,7 +123,7 @@ TEST(TestSaveResult, save_result_with_params) num_rec_t0 = 10, num_dead_t0 = 0; size_t num_groups = 3; - mio::osecir::Model model((int)num_groups); + mio::osecir::Model model((int)num_groups); double fact = 1.0 / (double)num_groups; auto& params = model.parameters; @@ -157,18 +158,19 @@ TEST(TestSaveResult, save_result_with_params) params.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.); auto graph = mio::Graph, mio::MobilityParameters>(); graph.add_node(0, model); graph.add_node(1, model); - graph.add_edge(0, 1, mio::MobilityParameters(Eigen::VectorXd::Constant(Eigen::Index(num_groups * 10), 1.0))); + graph.add_edge(0, 1, + mio::MobilityParameters(Eigen::VectorXd::Constant(Eigen::Index(num_groups * 10), 1.0))); auto num_runs = 3; - auto parameter_study = mio::ParameterStudy>(graph, 0.0, 2.0, 0.5, num_runs); + auto parameter_study = mio::ParameterStudy>(graph, 0.0, 2.0, 0.5, num_runs); mio::log_rng_seeds(parameter_study.get_rng(), mio::LogLevel::warn); TempFileRegister tmp_file_register; @@ -232,7 +234,7 @@ TEST(TestSaveResult, save_percentiles_and_sums) num_rec_t0 = 10, num_dead_t0 = 0; const size_t num_groups = 3; - mio::osecir::Model model((int)num_groups); + mio::osecir::Model model((int)num_groups); double fact = 1.0 / (double)num_groups; auto& params = model.parameters; @@ -267,8 +269,8 @@ TEST(TestSaveResult, save_percentiles_and_sums) params.get>()[i] = 0.3; } - mio::ContactMatrixGroup& contact_matrix = params.get>(); - contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); + mio::ContactMatrixGroup& contact_matrix = params.get>(); + contact_matrix[0] = mio::ContactMatrix(Eigen::MatrixXd::Constant(num_groups, num_groups, fact * cont_freq)); // get indices of INS and ISy compartments. std::vector> indices_save_edges(2); @@ -301,7 +303,7 @@ TEST(TestSaveResult, save_percentiles_and_sums) indices_save_edges)); auto num_runs = 3; - auto parameter_study = mio::ParameterStudy>(graph, 0.0, 2.0, 0.5, num_runs); + auto parameter_study = mio::ParameterStudy>(graph, 0.0, 2.0, 0.5, num_runs); mio::log_rng_seeds(parameter_study.get_rng(), mio::LogLevel::warn); TempFileRegister tmp_file_register; diff --git a/cpp/tests/test_sde_seirvv.cpp b/cpp/tests/test_sde_seirvv.cpp index f071b70182..220da23d2a 100644 --- a/cpp/tests/test_sde_seirvv.cpp +++ b/cpp/tests/test_sde_seirvv.cpp @@ -25,18 +25,18 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -const mio::sseirvv::Model& sseirvv_testing_model() +const mio::sseirvv::Model& sseirvv_testing_model() { - static mio::sseirvv::Model model; + static mio::sseirvv::Model model; model.populations.array().setConstant(1); { - model.parameters.set(2); - model.parameters.set(4); - model.parameters.set(4); - model.parameters.set(2); - model.parameters.set(0.5); - model.parameters.set(0.25); - model.parameters.get().get_baseline()(0, 0) = 10; + model.parameters.set>(2); + model.parameters.set>(4); + model.parameters.set>(4); + model.parameters.set>(2); + model.parameters.set>(0.5); + model.parameters.set>(0.25); + model.parameters.get>().get_baseline()(0, 0) = 10; } return model; } @@ -89,14 +89,14 @@ TEST(TestSdeSeirvv, Simulation) TEST(TestSdeSeirvv, check_constraints_parameters) { // check parameters.check_constraints - mio::sseirvv::Model::ParameterSet parameters; - parameters.set(6); - parameters.set(6); - parameters.set(6); - parameters.set(6); - parameters.set(0.04); - parameters.set(0.04); - parameters.get().get_baseline()(0, 0) = 10; + mio::sseirvv::Model::ParameterSet parameters; + parameters.set>(6); + parameters.set>(6); + parameters.set>(6); + parameters.set>(6); + parameters.set>(0.04); + parameters.set>(0.04); + parameters.get>().get_baseline()(0, 0) = 10; // model.check_constraints() combines the functions from population and parameters. // We only want to test the functions for the parameters defined in parameters.h. @@ -104,27 +104,27 @@ TEST(TestSdeSeirvv, check_constraints_parameters) mio::set_log_level(mio::LogLevel::off); - parameters.set(0); + parameters.set>(0); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(6); - parameters.set(0); + parameters.set>(6); + parameters.set>(0); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(6); - parameters.set(0); + parameters.set>(6); + parameters.set>(0); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(6); - parameters.set(0); + parameters.set>(6); + parameters.set>(0); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(6); - parameters.set(10.); + parameters.set>(6); + parameters.set>(10.); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(0.04); - parameters.set(10.); + parameters.set>(0.04); + parameters.set>(10.); EXPECT_EQ(parameters.check_constraints(), 1); mio::set_log_level(mio::LogLevel::warn); } @@ -133,45 +133,45 @@ TEST(TestSdeSeirvv, apply_constraints_parameters) { // check parameters.apply_constraints const ScalarType tol_times = 1e-1; - mio::sseirvv::Model::ParameterSet parameters; - parameters.set(6); - parameters.set(6); - parameters.set(6); - parameters.set(6); - parameters.set(0.04); - parameters.set(0.04); - parameters.get().get_baseline()(0, 0) = 10; + mio::sseirvv::Model::ParameterSet parameters; + parameters.set>(6); + parameters.set>(6); + parameters.set>(6); + parameters.set>(6); + parameters.set>(0.04); + parameters.set>(0.04); + parameters.get>().get_baseline()(0, 0) = 10; EXPECT_EQ(parameters.apply_constraints(), 0); mio::set_log_level(mio::LogLevel::off); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(10.); + parameters.set>(10.); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_NEAR(parameters.get(), 0.0, 1e-14); + EXPECT_NEAR(parameters.get>(), 0.0, 1e-14); - parameters.set(10.); + parameters.set>(10.); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_NEAR(parameters.get(), 0.0, 1e-14); + EXPECT_NEAR(parameters.get>(), 0.0, 1e-14); mio::set_log_level(mio::LogLevel::warn); -} +} \ No newline at end of file diff --git a/cpp/tests/test_sde_sir.cpp b/cpp/tests/test_sde_sir.cpp index 7f35269209..265ad5577f 100644 --- a/cpp/tests/test_sde_sir.cpp +++ b/cpp/tests/test_sde_sir.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -24,14 +24,14 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -const mio::ssir::Model& ssir_testing_model() +const mio::ssir::Model& ssir_testing_model() { - static mio::ssir::Model model; + static mio::ssir::Model model; model.populations.array().setConstant(1); { - model.parameters.set(0.5); - model.parameters.set(1); - model.parameters.get().get_baseline()(0, 0) = 3; + model.parameters.set>(0.5); + model.parameters.set>(1); + model.parameters.get>().get_baseline()(0, 0) = 3; } return model; } @@ -84,10 +84,10 @@ TEST(TestSdeSir, Simulation) TEST(TestSdeSir, check_constraints_parameters) { // check parameters.check_constraints - mio::ssir::Model::ParameterSet parameters; - parameters.set(6); - parameters.set(0.04); - parameters.get().get_baseline()(0, 0) = 10; + mio::ssir::Model::ParameterSet parameters; + parameters.set>(6); + parameters.set>(0.04); + parameters.get>().get_baseline()(0, 0) = 10; // model.check_constraints() combines the functions from population and parameters. // We only want to test the functions for the parameters defined in parameters.h @@ -95,11 +95,11 @@ TEST(TestSdeSir, check_constraints_parameters) mio::set_log_level(mio::LogLevel::off); - parameters.set(0); + parameters.set>(0); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(6); - parameters.set(10.); + parameters.set>(6); + parameters.set>(10.); EXPECT_EQ(parameters.check_constraints(), 1); mio::set_log_level(mio::LogLevel::warn); } @@ -108,21 +108,21 @@ TEST(TestSdeSir, apply_constraints_parameters) { // check parameters.apply_constraints const double tol_times = 1e-1; - mio::ssir::Model::ParameterSet parameters; - parameters.set(6); - parameters.set(0.04); - parameters.get().get_baseline()(0, 0) = 10; + mio::ssir::Model::ParameterSet parameters; + parameters.set>(6); + parameters.set>(0.04); + parameters.get>().get_baseline()(0, 0) = 10; EXPECT_EQ(parameters.apply_constraints(), 0); mio::set_log_level(mio::LogLevel::off); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(10.); + parameters.set>(10.); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_NEAR(parameters.get(), 0.0, 1e-14); + EXPECT_NEAR(parameters.get>(), 0.0, 1e-14); mio::set_log_level(mio::LogLevel::warn); } diff --git a/cpp/tests/test_sde_sirs.cpp b/cpp/tests/test_sde_sirs.cpp index 4e92ed27c3..1982c5fad4 100644 --- a/cpp/tests/test_sde_sirs.cpp +++ b/cpp/tests/test_sde_sirs.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Nils Wassmuth, Rene Schmieding, Martin J. Kuehn @@ -24,15 +24,15 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -const mio::ssirs::Model& ssirs_testing_model() +const mio::ssirs::Model& ssirs_testing_model() { - static mio::ssirs::Model model; + static mio::ssirs::Model model; model.populations.array().setConstant(1); - model.parameters.set(4); + model.parameters.set>(4); { - model.parameters.set(2); - model.parameters.set(1); - model.parameters.get().get_baseline()(0, 0) = 3; + model.parameters.set>(2); + model.parameters.set>(1); + model.parameters.get>().get_baseline()(0, 0) = 3; } return model; } @@ -73,7 +73,7 @@ TEST(TestSdeSirs, Simulation) .Times(testing::Exactly(3)) // one call for each flow .WillRepeatedly(testing::Return(.0)); // "disable" noise term, as it only adds sqrts of flows - auto sim = mio::StochasticSimulation(ssirs_testing_model(), 0.0, 1.0); + auto sim = mio::StochasticSimulation>(ssirs_testing_model(), 0.0, 1.0); sim.advance(1); EXPECT_EQ(sim.get_result().get_num_time_points(), 2); // stores initial value and single step @@ -85,11 +85,11 @@ TEST(TestSdeSirs, Simulation) TEST(TestSdeSirs, check_constraints_parameters) { // check parameters.check_constraints - mio::ssirs::Model::ParameterSet parameters; - parameters.set(6); - parameters.set(6); - parameters.set(0.04); - parameters.get().get_baseline()(0, 0) = 10; + mio::ssirs::Model::ParameterSet parameters; + parameters.set>(6); + parameters.set>(6); + parameters.set>(0.04); + parameters.get>().get_baseline()(0, 0) = 10; // model.check_constraints() combines the functions from population and parameters. // We only want to test the functions for the parameters defined in parameters.h @@ -97,15 +97,15 @@ TEST(TestSdeSirs, check_constraints_parameters) mio::set_log_level(mio::LogLevel::off); - parameters.set(0); + parameters.set>(0); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(6); - parameters.set(0); + parameters.set>(6); + parameters.set>(0); EXPECT_EQ(parameters.check_constraints(), 1); - parameters.set(6); - parameters.set(10.); + parameters.set>(6); + parameters.set>(10.); EXPECT_EQ(parameters.check_constraints(), 1); mio::set_log_level(mio::LogLevel::warn); } @@ -114,26 +114,26 @@ TEST(TestSdeSirs, apply_constraints_parameters) { // check parameters.apply_constraints const double tol_times = 1e-1; - mio::ssirs::Model::ParameterSet parameters; - parameters.set(6); - parameters.set(6); - parameters.set(0.04); - parameters.get().get_baseline()(0, 0) = 10; + mio::ssirs::Model::ParameterSet parameters; + parameters.set>(6); + parameters.set>(6); + parameters.set>(0.04); + parameters.get>().get_baseline()(0, 0) = 10; EXPECT_EQ(parameters.apply_constraints(), 0); mio::set_log_level(mio::LogLevel::off); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(-2.5); + parameters.set>(-2.5); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_EQ(parameters.get(), tol_times); + EXPECT_EQ(parameters.get>(), tol_times); - parameters.set(10.); + parameters.set>(10.); EXPECT_EQ(parameters.apply_constraints(), 1); - EXPECT_NEAR(parameters.get(), 0.0, 1e-14); + EXPECT_NEAR(parameters.get>(), 0.0, 1e-14); mio::set_log_level(mio::LogLevel::warn); } diff --git a/cpp/tests/test_smm_model.cpp b/cpp/tests/test_smm_model.cpp index 39fe57451e..30ba70e0b5 100644 --- a/cpp/tests/test_smm_model.cpp +++ b/cpp/tests/test_smm_model.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2024 German Aerospace Center (DLR-SC) * * Authors: Julia Bicker @@ -53,12 +53,12 @@ TEST(TestSMM, evaluateAdoptionRate) // with N(from, region) the population in Region "region" having infection state "from" //Second-order adoption rates are given by: // rate.factor * N(rate.from, rate.region)/total_pop * sum (over all rate.influences) influence.factor * N(influence.status, rate.region) - using Model = mio::smm::Model<1, InfectionState>; + using Model = mio::smm::Model; Model model; //Set adoption rates - std::vector> adoption_rates; + std::vector> adoption_rates; //Second-order adoption adoption_rates.push_back({InfectionState::S, InfectionState::E, @@ -84,7 +84,7 @@ TEST(TestSMM, evaluateTransitionRate) { //Same test as 'evaluateAdoptionRate' only for spatial transition rates. //Transition rates are given by: rate.factor * N(rate.status, rate.from) - using Model = mio::smm::Model<2, InfectionState>; + using Model = mio::smm::Model; Model model; //Initialize model populations @@ -102,7 +102,7 @@ TEST(TestSMM, evaluateTransitionRate) model.populations[{mio::regions::Region(1), InfectionState::R}] = 0; model.populations[{mio::regions::Region(1), InfectionState::D}] = 0; //Set transition rates - std::vector> transition_rates; + std::vector> transition_rates; transition_rates.push_back({InfectionState::S, mio::regions::Region(0), mio::regions::Region(1), 0.01}); transition_rates.push_back({InfectionState::E, mio::regions::Region(1), mio::regions::Region(0), 0.1}); @@ -114,7 +114,7 @@ TEST(TestSMMSimulation, advance) { //Test whether Gillespie algorithm calculates events in the correct order using testing::Return; - using Model = mio::smm::Model<2, InfectionState>; + using Model = mio::smm::Model; Model model; //Initialize model populations @@ -132,8 +132,8 @@ TEST(TestSMMSimulation, advance) model.populations[{mio::regions::Region(1), InfectionState::R}] = 1; model.populations[{mio::regions::Region(1), InfectionState::D}] = 0; //Set adoption and transition rates - std::vector> adoption_rates; - std::vector> transition_rates; + std::vector> adoption_rates; + std::vector> transition_rates; //Second-order adoption adoption_rates.push_back({InfectionState::S, @@ -151,8 +151,8 @@ TEST(TestSMMSimulation, advance) //Spatial transition transition_rates.push_back({InfectionState::R, mio::regions::Region(1), mio::regions::Region(0), 0.01}); - model.parameters.get>() = adoption_rates; - model.parameters.get>() = transition_rates; + model.parameters.get>() = adoption_rates; + model.parameters.get>() = transition_rates; //Mock exponential distribution to control the normalized waiting times that are drawn ScopedMockDistribution>>> @@ -195,13 +195,13 @@ TEST(TestSMMSimulation, stopsAtTmax) { //Test whether simulation stops at tmax and whether system state at tmax is saved if there are no adoptions/transitions using testing::Return; - using Model = mio::smm::Model<2, InfectionState>; + using Model = mio::smm::Model; Model model; //Set adoption and spatial transition rates - std::vector> adoption_rates; - std::vector> transition_rates; + std::vector> adoption_rates; + std::vector> transition_rates; adoption_rates.push_back({InfectionState::S, InfectionState::E, @@ -211,8 +211,8 @@ TEST(TestSMMSimulation, stopsAtTmax) transition_rates.push_back({InfectionState::R, mio::regions::Region(1), mio::regions::Region(0), 0.01}); - model.parameters.get>() = adoption_rates; - model.parameters.get>() = transition_rates; + model.parameters.get>() = adoption_rates; + model.parameters.get>() = transition_rates; //As populations are not set they have value 0 i.e. no events will happen //Simulate for 30 days @@ -228,14 +228,14 @@ TEST(TestSMMSimulation, convergence) //Test whether the mean number of transitions in one unit-time step corresponds to the expected number of transitions // given by rate * pop up to some tolerance using testing::Return; - using Model = mio::smm::Model<2, InfectionState>; + using Model = mio::smm::Model; double pop = 1000; size_t num_runs = 100; double rate = 0.2; //Only set one spatial transition rate - std::vector> transition_rates( + std::vector> transition_rates( 1, {InfectionState::S, mio::regions::Region(0), mio::regions::Region(1), rate}); double expected_num_trans = rate * pop; @@ -252,13 +252,13 @@ TEST(TestSMMSimulation, convergence) model.populations[{mio::regions::Region(0), InfectionState::R}] = 0; model.populations[{mio::regions::Region(0), InfectionState::D}] = 0; - model.populations[{mio::regions::Region(1), InfectionState::S}] = 0; - model.populations[{mio::regions::Region(1), InfectionState::E}] = 0; - model.populations[{mio::regions::Region(1), InfectionState::C}] = 0; - model.populations[{mio::regions::Region(1), InfectionState::I}] = 0; - model.populations[{mio::regions::Region(1), InfectionState::R}] = 0; - model.populations[{mio::regions::Region(1), InfectionState::D}] = 0; - model.parameters.get>() = transition_rates; + model.populations[{mio::regions::Region(1), InfectionState::S}] = 0; + model.populations[{mio::regions::Region(1), InfectionState::E}] = 0; + model.populations[{mio::regions::Region(1), InfectionState::C}] = 0; + model.populations[{mio::regions::Region(1), InfectionState::I}] = 0; + model.populations[{mio::regions::Region(1), InfectionState::R}] = 0; + model.populations[{mio::regions::Region(1), InfectionState::D}] = 0; + model.parameters.get>() = transition_rates; auto sim = mio::smm::Simulation(model, 0.0, 1.0); sim.advance(1.); diff --git a/cpp/tests/test_smoother.cpp b/cpp/tests/test_smoother.cpp index b733092924..91c18e779e 100644 --- a/cpp/tests/test_smoother.cpp +++ b/cpp/tests/test_smoother.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Martin J. Kuehn diff --git a/cpp/tests/test_state_age_function.cpp b/cpp/tests/test_state_age_function.cpp index 8dc07d4c62..3204e6a70b 100644 --- a/cpp/tests/test_state_age_function.cpp +++ b/cpp/tests/test_state_age_function.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Anna Wendler, Lena Ploetzke @@ -85,11 +85,11 @@ TEST(TestStateAgeFunction, testSpecialMember) // Test constructors of the other derived classes. // Copy and move (assignment) are defined in base class StateAgeFunction and are equal for all derived classes. - mio::ExponentialSurvivalFunction exponential(1.5, 0.5); + mio::ExponentialSurvivalFunction exponential(1.5, 0.5); EXPECT_EQ(exponential.get_distribution_parameter(), 1.5); EXPECT_EQ(exponential.get_location(), 0.5); - mio::SmootherCosine smoothcos(2.0, 1.3); + mio::SmootherCosine smoothcos(2.0, 1.3); EXPECT_EQ(smoothcos.get_distribution_parameter(), 2.0); EXPECT_EQ(smoothcos.get_location(), 1.3); @@ -98,7 +98,7 @@ TEST(TestStateAgeFunction, testSpecialMember) EXPECT_EQ(logn.get_location(), 3.3); EXPECT_EQ(logn.get_scale(), 0.5); - mio::ConstantFunction constfunc(1.4); + mio::ConstantFunction constfunc(1.4); EXPECT_EQ(constfunc.get_distribution_parameter(), 1.4); mio::ErlangDensity erl(2, 0.7); @@ -145,7 +145,7 @@ TEST(TestStateAgeFunction, testGetSupportMax) // Test get_support_max for all derived classes as this method can be overridden. // Check that the maximum support is correct after setting the parameters of a StateAgeFunction. - mio::ExponentialSurvivalFunction exponential(1.0, 1.0); + mio::ExponentialSurvivalFunction exponential(1.0, 1.0); EXPECT_NEAR(exponential.get_support_max(dt), 24.5, 1e-14); exponential.set_distribution_parameter(2.0); EXPECT_NEAR(exponential.get_support_max(dt), 13.0, 1e-14); @@ -154,7 +154,7 @@ TEST(TestStateAgeFunction, testGetSupportMax) exponential.set_scale(300.0); EXPECT_NEAR(exponential.get_support_max(dt), 13.0, 1e-14); - mio::SmootherCosine smoothcos(1.0, 1.); + mio::SmootherCosine smoothcos(1.0, 1.); EXPECT_NEAR(smoothcos.get_support_max(dt), 2.0, 1e-14); smoothcos.set_distribution_parameter(2.0); EXPECT_NEAR(smoothcos.get_support_max(dt), 3.0, 1e-14); @@ -186,7 +186,7 @@ TEST(TestStateAgeFunction, testGetSupportMax) // Support_max would be infinity here but is set to -2.0 in the get_support_max() method. mio::set_log_level(mio::LogLevel::off); - mio::ConstantFunction constfunc(1.0); + mio::ConstantFunction constfunc(1.0); EXPECT_NEAR(constfunc.get_support_max(dt), -2.0, 1e-14); constfunc.set_distribution_parameter(2.0); EXPECT_NEAR(constfunc.get_support_max(dt), -2.0, 1e-14); @@ -212,7 +212,7 @@ TEST(TestStateAgeFunction, testSAFWrapperSpecialMember) { // Same as testSpecialMember for Wrapper. ScalarType dt = 0.5; mio::GammaSurvivalFunction gamma(1., 2., 3.); - mio::StateAgeFunctionWrapper wrapper(gamma); + mio::StateAgeFunctionWrapper wrapper(gamma); // constructor EXPECT_EQ(wrapper.get_distribution_parameter(), 1.); @@ -222,7 +222,7 @@ TEST(TestStateAgeFunction, testSAFWrapperSpecialMember) EXPECT_NEAR(wrapper.get_mean(), 5., 1e-14); // copy - mio::StateAgeFunctionWrapper wrapper2(wrapper); + mio::StateAgeFunctionWrapper wrapper2(wrapper); EXPECT_EQ(wrapper.get_state_age_function_type(), wrapper2.get_state_age_function_type()); EXPECT_EQ(wrapper.get_distribution_parameter(), wrapper2.get_distribution_parameter()); EXPECT_EQ(wrapper.get_location(), wrapper2.get_location()); @@ -236,7 +236,7 @@ TEST(TestStateAgeFunction, testSAFWrapperSpecialMember) wrapper.set_distribution_parameter(1.0); // move - mio::StateAgeFunctionWrapper wrapper3(std::move(wrapper2)); + mio::StateAgeFunctionWrapper wrapper3(std::move(wrapper2)); EXPECT_EQ(wrapper.get_state_age_function_type(), wrapper3.get_state_age_function_type()); EXPECT_EQ(wrapper.get_distribution_parameter(), wrapper3.get_distribution_parameter()); EXPECT_EQ(wrapper.get_location(), wrapper3.get_location()); @@ -245,7 +245,7 @@ TEST(TestStateAgeFunction, testSAFWrapperSpecialMember) EXPECT_EQ(wrapper.get_mean(dt), wrapper3.get_mean(dt)); // copy assignment - mio::StateAgeFunctionWrapper wrapper4 = wrapper3; + mio::StateAgeFunctionWrapper wrapper4 = wrapper3; EXPECT_EQ(wrapper.get_state_age_function_type(), wrapper4.get_state_age_function_type()); EXPECT_EQ(wrapper.get_distribution_parameter(), wrapper4.get_distribution_parameter()); EXPECT_EQ(wrapper.get_location(), wrapper4.get_location()); @@ -259,7 +259,7 @@ TEST(TestStateAgeFunction, testSAFWrapperSpecialMember) wrapper.set_scale(3.0); // move assignment - mio::StateAgeFunctionWrapper wrapper5 = std::move(wrapper4); + mio::StateAgeFunctionWrapper wrapper5 = std::move(wrapper4); EXPECT_EQ(wrapper.get_state_age_function_type(), wrapper5.get_state_age_function_type()); EXPECT_EQ(wrapper.get_distribution_parameter(), wrapper5.get_distribution_parameter()); EXPECT_EQ(wrapper.get_location(), wrapper5.get_location()); @@ -269,22 +269,22 @@ TEST(TestStateAgeFunction, testSAFWrapperSpecialMember) // Also test the constructor of StateAgeFunctionWrapper initialized with other StateAgeFunction%s. // This way we can be sure that clone_impl works for all derived classes of StateAgeFunction. - mio::ExponentialSurvivalFunction exponential(1., 1.5); - mio::StateAgeFunctionWrapper wrapper_exp(exponential); + mio::ExponentialSurvivalFunction exponential(1., 1.5); + mio::StateAgeFunctionWrapper wrapper_exp(exponential); EXPECT_EQ(wrapper_exp.get_distribution_parameter(), 1.0); EXPECT_EQ(wrapper_exp.get_location(), 1.5); EXPECT_NEAR(wrapper_exp.get_support_max(dt), 25, 1e-14); EXPECT_NEAR(wrapper_exp.get_mean(dt), 2.5, 1e-14); - mio::SmootherCosine smoothcos(2., 1.3); - mio::StateAgeFunctionWrapper wrapper_smoothcos(smoothcos); + mio::SmootherCosine smoothcos(2., 1.3); + mio::StateAgeFunctionWrapper wrapper_smoothcos(smoothcos); EXPECT_EQ(wrapper_smoothcos.get_distribution_parameter(), 2.); EXPECT_EQ(wrapper_smoothcos.get_location(), 1.3); EXPECT_NEAR(wrapper_smoothcos.get_support_max(dt), 2.0 + 1.3, 1e-14); EXPECT_NEAR(wrapper_smoothcos.get_mean(), 2.3, 1e-14); mio::LognormSurvivalFunction logn(0.1, 1, 0.5); - mio::StateAgeFunctionWrapper wrapper_logn(logn); + mio::StateAgeFunctionWrapper wrapper_logn(logn); EXPECT_EQ(wrapper_logn.get_distribution_parameter(), 0.1); EXPECT_EQ(wrapper_logn.get_location(), 1.0); EXPECT_EQ(wrapper_logn.get_scale(), 0.5); @@ -292,8 +292,8 @@ TEST(TestStateAgeFunction, testSAFWrapperSpecialMember) // Mean value computed with python scipy-stats. EXPECT_NEAR(wrapper_logn.get_mean(0.1), 1.502506, 0.1); - mio::ConstantFunction constfunc(1.0); - mio::StateAgeFunctionWrapper wrapper_const(constfunc); + mio::ConstantFunction constfunc(1.0); + mio::StateAgeFunctionWrapper wrapper_const(constfunc); EXPECT_EQ(wrapper_const.get_distribution_parameter(), 1.); // Deactivate temporarily log output as an error is expected here. mio::set_log_level(mio::LogLevel::off); @@ -321,7 +321,7 @@ TEST(TestStateAgeFunction, testSAFWrapperSettersAndGetters) ScalarType dt = 0.5; mio::GammaSurvivalFunction gamma(1., 2., 3.); - mio::StateAgeFunctionWrapper wrapper(gamma); + mio::StateAgeFunctionWrapper wrapper(gamma); EXPECT_EQ(wrapper.get_distribution_parameter(), 1.); EXPECT_EQ(wrapper.get_location(), 2.); @@ -357,12 +357,12 @@ TEST(TestStateAgeFunction, testComparisonOperator) EXPECT_FALSE(gamma == logn); // Check that it also holds when a StateAgeFunctionWrapper is set with the respective functions - mio::StateAgeFunctionWrapper wrapper(gamma); - mio::StateAgeFunctionWrapper wrapper2(gamma2); - mio::StateAgeFunctionWrapper wrapper3(gamma3); - mio::StateAgeFunctionWrapper wrapper4(gamma4); - mio::StateAgeFunctionWrapper wrapper5(gamma5); - mio::StateAgeFunctionWrapper wrapper6(logn); + mio::StateAgeFunctionWrapper wrapper(gamma); + mio::StateAgeFunctionWrapper wrapper2(gamma2); + mio::StateAgeFunctionWrapper wrapper3(gamma3); + mio::StateAgeFunctionWrapper wrapper4(gamma4); + mio::StateAgeFunctionWrapper wrapper5(gamma5); + mio::StateAgeFunctionWrapper wrapper6(logn); EXPECT_TRUE(wrapper == wrapper2); EXPECT_FALSE(wrapper == wrapper3); @@ -409,7 +409,7 @@ TEST(TestStateAgeFunction, testGamma) // appropriate choice of parameters. for (ScalarType r : rate) { mio::GammaSurvivalFunction gamma(1, 0, 1. / r); - mio::ExponentialSurvivalFunction exp(r); + mio::ExponentialSurvivalFunction exp(r); for (ScalarType tau : times) { EXPECT_NEAR(gamma.eval(tau), exp.eval(tau), 1e-10); } diff --git a/cpp/tests/test_stl_util.cpp b/cpp/tests/test_stl_util.cpp index c81508041c..3de196389e 100644 --- a/cpp/tests/test_stl_util.cpp +++ b/cpp/tests/test_stl_util.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/test_temporal_hybrid_model.cpp b/cpp/tests/test_temporal_hybrid_model.cpp index e7948aa0ff..0ae31660ec 100644 --- a/cpp/tests/test_temporal_hybrid_model.cpp +++ b/cpp/tests/test_temporal_hybrid_model.cpp @@ -151,7 +151,7 @@ TEST(TestTemporalHybrid, test_advance) TEST(TestTemporalHybrid, test_conversion_dabm_smm) { using Model1 = mio::dabm::Model>; - using Model2 = mio::smm::Model<1, mio::osecir::InfectionState>; + using Model2 = mio::smm::Model; //Initialize agents for dabm SingleWell::Agent a1{Eigen::Vector2d{-0.5, 0}, @@ -163,7 +163,7 @@ TEST(TestTemporalHybrid, test_conversion_dabm_smm) Model1 model1({a1, a2, a3}, {}); Model2 model2; - model2.parameters.get>().push_back( + model2.parameters.get>().push_back( {mio::osecir::InfectionState::Susceptible, mio::osecir::InfectionState::Exposed, mio::regions::Region(0), diff --git a/cpp/tests/test_time_series.cpp b/cpp/tests/test_time_series.cpp index c51573097f..88563c0fcd 100644 --- a/cpp/tests/test_time_series.cpp +++ b/cpp/tests/test_time_series.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/test_transform_iterator.cpp b/cpp/tests/test_transform_iterator.cpp index fcef079617..3e32b9fa2d 100644 --- a/cpp/tests/test_transform_iterator.cpp +++ b/cpp/tests/test_transform_iterator.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele @@ -32,8 +32,7 @@ TEST(TestTransformIterator, produce_lvalue) int i; }; auto v = std::vector{{1}, {2}}; - auto get_i = [](auto&& foo) -> auto& - { + auto get_i = [](auto&& foo) -> auto& { return foo.get_i(); }; auto b = mio::make_transform_iterator(v.begin(), get_i); diff --git a/cpp/tests/test_type_safe.cpp b/cpp/tests/test_type_safe.cpp index 979ce27bbd..1170c62f09 100644 --- a/cpp/tests/test_type_safe.cpp +++ b/cpp/tests/test_type_safe.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele diff --git a/cpp/tests/test_uncertain.cpp b/cpp/tests/test_uncertain.cpp index fefbeba7b4..2157fd11c0 100644 --- a/cpp/tests/test_uncertain.cpp +++ b/cpp/tests/test_uncertain.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin J. Kuehn @@ -119,10 +119,10 @@ TEST(TestUncertain, uncertain_matrix) { mio::log_thread_local_rng_seeds(mio::LogLevel::warn); - mio::ContactMatrix contact_matrix(Eigen::MatrixXd::NullaryExpr(2, 2, [](auto i, auto j) -> double { + mio::ContactMatrix contact_matrix(Eigen::MatrixXd::NullaryExpr(2, 2, [](auto i, auto j) -> double { return (i + 1) * (j + 1); })); - contact_matrix.add_damping(0.7, mio::SimulationTime(30.)); + contact_matrix.add_damping(0.7, mio::SimulationTime(30.)); mio::UncertainContactMatrix uncertain_mat{{contact_matrix}}; @@ -131,13 +131,13 @@ TEST(TestUncertain, uncertain_matrix) EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[0].get_coeffs()(1, 1), 0.7); uncertain_mat.get_dampings().emplace_back(mio::UncertainValue(0.5), mio::DampingLevel(0), - mio::DampingType(0), mio::SimulationTime(3.0), + mio::DampingType(0), mio::SimulationTime(3.0), std::vector(1, size_t(0)), Eigen::VectorXd::Constant(2, 1.0)); uncertain_mat.get_school_holiday_damping() = mio::DampingSampling( - mio::UncertainValue(1.), mio::DampingLevel(1), mio::DampingType(0), mio::SimulationTime(0.0), + mio::UncertainValue(1.), mio::DampingLevel(1), mio::DampingType(0), mio::SimulationTime(0.0), std::vector(1, size_t(0)), (Eigen::VectorXd(2) << 1.0, 0.0).finished()); - uncertain_mat.get_school_holidays().assign({{mio::SimulationTime(5.0), mio::SimulationTime(17.0)}}); + uncertain_mat.get_school_holidays().assign({{mio::SimulationTime(5.0), mio::SimulationTime(17.0)}}); uncertain_mat.draw_sample(true); EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings().size(), 4); @@ -147,13 +147,13 @@ TEST(TestUncertain, uncertain_matrix) EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[0].get_level(), mio::DampingLevel(0)); EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[0].get_coeffs()(0, 0), 0.5); - EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[0].get_time(), mio::SimulationTime(3.0)); + EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[0].get_time(), mio::SimulationTime(3.0)); EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[1].get_level(), mio::DampingLevel(1)); EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[1].get_coeffs()(0, 0), 1.0); - EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[1].get_time(), mio::SimulationTime(5.0)); + EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[1].get_time(), mio::SimulationTime(5.0)); EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[2].get_level(), mio::DampingLevel(1)); EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[2].get_coeffs()(0, 0), 0.0); - EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[2].get_time(), mio::SimulationTime(17.0)); + EXPECT_EQ(uncertain_mat.get_cont_freq_mat()[0].get_dampings()[2].get_time(), mio::SimulationTime(17.0)); } diff --git a/cpp/tests/test_uncertain_ad_compatibility.cpp b/cpp/tests/test_uncertain_ad_compatibility.cpp new file mode 100644 index 0000000000..7a82b564bf --- /dev/null +++ b/cpp/tests/test_uncertain_ad_compatibility.cpp @@ -0,0 +1,273 @@ +/* +* Copyright (C) 2020-2025 MEmilio +* +* Authors: Daniel Abele, Martin J. Kuehn +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "memilio/utils/memory.h" +#include "memilio/utils/uncertain_value.h" +#include "memilio/epidemiology/uncertain_matrix.h" +#include "memilio/utils/parameter_distributions.h" +#include +#include "memilio/ad/ad.h" +#include "models/ode_secirvvs/model.h" +#include +#include + +#include + +using FP = ad::gt1s::type; + +TEST(TestUncertainADCompatibility, assign_constructor) +{ + mio::UncertainValue a(2.0); + mio::UncertainValue b(a); + EXPECT_EQ(a, 2.0); + EXPECT_EQ(b, 2.0); +} + +TEST(TestUncertainADCompatibility, copy_constructor) +{ + mio::UncertainValue a = 2.0; + mio::UncertainValue b = a; + EXPECT_EQ(a, 2.0); + EXPECT_EQ(b, 2.0); +} + +TEST(TestUncertainADCompatibility, addition) +{ + mio::UncertainValue a = 2.0; + mio::UncertainValue b = 3.0; + mio::UncertainValue result = 1.0 + a + b + 4.0; + EXPECT_EQ(result, 10.0); +} + +TEST(TestUncertainADCompatibility, subtraction) +{ + mio::UncertainValue a = 2.0; + mio::UncertainValue b = 3.0; + mio::UncertainValue result = -1.0 - a - b - 4.0; + EXPECT_EQ(result, -10.0); +} + +TEST(TestUncertainADCompatibility, multiplication) +{ + mio::UncertainValue a = 2.0; + mio::UncertainValue b = 3.0; + mio::UncertainValue result = a * b + 1.0 * a + b * 2.0 + 2.0; + EXPECT_EQ(result, 16.0); +} + +// Division operations +TEST(TestUncertainADCompatibility, division) +{ + mio::UncertainValue a = 6.0; + mio::UncertainValue b = 3.0; + mio::UncertainValue result = (a / b) + (3.0 / b) + (a / 2.0); + EXPECT_EQ(result, 6.0); +} + +// Compound assignment operators +TEST(TestUncertainADCompatibility, compound_assignment) +{ + FP base = 10.0; + mio::UncertainValue uv = 5.0; + + base += uv; + EXPECT_EQ(base, 15.0); + + base -= uv; + EXPECT_EQ(base, 10.0); + + base *= uv; + EXPECT_EQ(base, 50.0); + + base /= uv; + EXPECT_EQ(base, 10.0); +} + +// Comparison operators +TEST(TestUncertainADCompatibility, comparisons) +{ + mio::UncertainValue a = 5.0; + mio::UncertainValue b = 3.0; + + // UncertainValue vs UncertainValue + EXPECT_TRUE(a > b); + EXPECT_TRUE(b < a); + EXPECT_TRUE(a >= b); + EXPECT_TRUE(b <= a); + EXPECT_TRUE(a == a); + EXPECT_TRUE(a != b); + + // UncertainValue vs scalar + EXPECT_TRUE(a > 4.0); + EXPECT_TRUE(a < 6.0); + EXPECT_TRUE(a >= 5.0); + EXPECT_TRUE(a <= 5.0); + EXPECT_TRUE(a == 5.0); + EXPECT_TRUE(a != 4.0); + + // scalar vs UncertainValue + EXPECT_TRUE(4.0 < a); + EXPECT_TRUE(6.0 > a); + EXPECT_TRUE(5.0 <= a); + EXPECT_TRUE(5.0 >= a); + EXPECT_TRUE(5.0 == a); + EXPECT_TRUE(4.0 != a); +} + +TEST(TestUncertainADCompatibility, create_model) +{ + FP tmax = 56.0; + + constexpr size_t num_age_groups = 6; + mio::osecirvvs::Model model(num_age_groups); + auto& params = model.parameters; + + params.template get>() = 100; + params.template get>() = 0.0143; + params.template get>() = 0.2; + params.template get>() = 7; + + using std::floor; + size_t tmax_days = static_cast(floor(tmax)); + params.template get>().resize(mio::SimulationDay(tmax_days + 1)); + params.template get>().resize(mio::SimulationDay(tmax_days + 1)); + + size_t daily_vaccinations = 10; + for (size_t age_group = 0; age_group < num_age_groups; age_group++) { + for (size_t current_day = 0; current_day <= tmax_days; current_day++) { + mio::Index index{mio::AgeGroup(age_group), + mio::SimulationDay(current_day)}; + FP num_vaccinations = static_cast(current_day * daily_vaccinations); + params.template get>()[index] = num_vaccinations; + params.template get>()[index] = num_vaccinations; + } + } + + auto set_all_groups = [&](auto Tag, FP value) { + auto& age_group_params = params.template get(); + for (size_t age_group = 0; age_group < num_age_groups; age_group++) { + age_group_params[mio::AgeGroup(age_group)] = value; + } + }; + + // — times (all groups same value) + set_all_groups(mio::osecirvvs::TimeExposed{}, 3.33); + set_all_groups(mio::osecirvvs::TimeInfectedNoSymptoms{}, 1.87); + set_all_groups(mio::osecirvvs::TimeInfectedSymptoms{}, 7); + set_all_groups(mio::osecirvvs::TimeInfectedSevere{}, 6); + set_all_groups(mio::osecirvvs::TimeInfectedCritical{}, 7); + // — probabilities (all groups same value) + set_all_groups(mio::osecirvvs::TransmissionProbabilityOnContact{}, 0.15); + set_all_groups(mio::osecirvvs::RelativeTransmissionNoSymptoms{}, 0.5); + set_all_groups(mio::osecirvvs::RiskOfInfectionFromSymptomatic{}, 0.0); + set_all_groups(mio::osecirvvs::MaxRiskOfInfectionFromSymptomatic{}, 0.4); + set_all_groups(mio::osecirvvs::RecoveredPerInfectedNoSymptoms{}, 0.2); + set_all_groups(mio::osecirvvs::SeverePerInfectedSymptoms{}, 0.1); + set_all_groups(mio::osecirvvs::CriticalPerSevere{}, 0.1); + set_all_groups(mio::osecirvvs::DeathsPerCritical{}, 0.1); + // — immunity reductions (all groups same value) + set_all_groups(mio::osecirvvs::ReducExposedPartialImmunity{}, 0.8); + set_all_groups(mio::osecirvvs::ReducExposedImprovedImmunity{}, 0.331); + set_all_groups(mio::osecirvvs::ReducInfectedSymptomsPartialImmunity{}, 0.65); + set_all_groups(mio::osecirvvs::ReducInfectedSymptomsImprovedImmunity{}, 0.243); + set_all_groups(mio::osecirvvs::ReducInfectedSevereCriticalDeadPartialImmunity{}, 0.1); + set_all_groups(mio::osecirvvs::ReducInfectedSevereCriticalDeadImprovedImmunity{}, 0.091); + set_all_groups(mio::osecirvvs::ReducTimeInfectedMild{}, 0.9); + + // --------------------------- // + // Define the contact matrices // + int num_contact_locations = 4; + Eigen::Matrix baseline_home; + Eigen::Matrix baseline_school_pf_eig; + Eigen::Matrix baseline_work; + Eigen::Matrix baseline_other; + + baseline_home << 0.4413, 0.4504, 1.2383, 0.8033, 0.0494, 0.0017, 0.0485, 0.7616, 0.6532, 1.1614, 0.0256, 0.0013, + 0.1800, 0.1795, 0.8806, 0.6413, 0.0429, 0.0032, 0.0495, 0.2639, 0.5189, 0.8277, 0.0679, 0.0014, 0.0087, 0.0394, + 0.1417, 0.3834, 0.7064, 0.0447, 0.0292, 0.0648, 0.1248, 0.4179, 0.3497, 0.1544; + + baseline_school_pf_eig << 2.9964, 0.2501, 0.9132, 0.2509, 0.0000, 0.0000, 0.2210, 1.9155, 0.2574, 0.2863, 0.0070, + 0.0000, 0.0324, 0.3598, 1.2613, 0.1854, 0.0041, 0.0000, 0.1043, 0.4794, 1.1886, 0.1836, 0.0052, 0.0022, 0.0000, + 0.1150, 0.0000, 0.0000, 0.0168, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000; + + baseline_other << 0.5170, 0.3997, 0.7957, 0.9958, 0.3239, 0.0428, 0.0632, 0.9121, 0.3254, 0.4731, 0.2355, 0.0148, + 0.0336, 0.1604, 1.7529, 0.8622, 0.1440, 0.0077, 0.0204, 0.1444, 0.5738, 1.2127, 0.3433, 0.0178, 0.0371, 0.0393, + 0.4171, 0.9666, 0.7495, 0.0257, 0.0791, 0.0800, 0.3480, 0.5588, 0.2769, 0.0180; + + baseline_work << 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0127, 1.7570, 1.6050, 0.0133, 0.0000, 0.0000, 0.0020, 1.0311, 2.3166, 0.0098, 0.0000, 0.0000, 0.0002, + 0.0194, 0.0325, 0.0003, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000; + + Eigen::Matrix minimum_home; + Eigen::Matrix minimum_school_pf_eig; + Eigen::Matrix minimum_work; + Eigen::Matrix minimum_other; + + minimum_home.setZero(); + minimum_school_pf_eig.setZero(); + minimum_work.setZero(); + minimum_other.setZero(); + + mio::ContactMatrixGroup contact_matrices(num_contact_locations, num_age_groups); + + contact_matrices[0].get_baseline() = baseline_home; + contact_matrices[1].get_baseline() = baseline_school_pf_eig; + contact_matrices[2].get_baseline() = baseline_work; + contact_matrices[3].get_baseline() = baseline_other; + + contact_matrices[0].get_minimum() = minimum_home; + contact_matrices[1].get_minimum() = minimum_school_pf_eig; + contact_matrices[2].get_minimum() = minimum_work; + contact_matrices[3].get_minimum() = minimum_other; + + params.template get>() = + mio::UncertainContactMatrix(std::move(contact_matrices)); + + // ----------------------------- // + // Define the initial population // + size_t num_infection_states = static_cast(mio::osecirvvs::InfectionState::Count); + Eigen::Matrix population(num_age_groups, num_infection_states); + population << 2753030.497430, 0.000000, 11.708820, 0.000000, 2.212815, 7.578866, 0.000000, 4.084619, 0.000000, + 0.000000, 0.000000, 22.853384, 0.000000, 2.146616, 0.000000, 0.000000, 0.000000, 0.150378, 0.000000, 0.001865, + 0.150441, 0.000000, 0.005732, 1051258.609035, 64.000000, 0.000000, 0.000000, 3326384.779416, 0.000000, 5.015423, + 0.000000, 3.287046, 2.945401, 0.000000, 5.907620, 0.000000, 0.000000, 0.000000, 5.168372, 0.000000, 1.831628, + 0.000000, 0.000000, 0.000000, 0.023957, 0.000000, 0.001460, 0.138450, 0.000000, 0.017723, 4506996.883505, + 45.000000, 0.000000, 0.000000, 7474316.734280, 0.000000, 15.185091, 0.000000, 9.963930, 10.119921, 0.000000, + 19.347768, 0.000000, 0.000000, 0.000000, 27.662806, 0.000000, 9.908623, 0.000000, 0.000000, 0.000000, 0.839570, + 0.000000, 0.044404, 0.349355, 0.000000, 0.046282, 11247005.297970, 510.000000, 0.000000, 0.000000, + 13079641.709818, 0.000000, 28.087697, 0.000000, 14.523549, 23.028794, 0.000000, 36.771572, 0.000000, 0.000000, + 0.000000, 74.950401, 0.000000, 21.335313, 0.000000, 0.000000, 0.000000, 7.139552, 0.000000, 0.287452, 2.329300, + 0.000000, 0.231930, 15095393.104624, 8936.000000, 0.000000, 0.000000, 12370660.206757, 0.000000, 66.165784, + 0.000000, 13.006396, 51.098574, 0.000000, 30.752489, 0.000000, 0.000000, 0.000000, 162.557513, 0.000000, + 16.728201, 0.000000, 0.000000, 0.000000, 38.893189, 0.000000, 0.564814, 13.248394, 0.000000, 0.494789, + 5239420.683100, 56248.571429, 0.000000, 0.000000, 5614932.848062, 0.000000, 61.140294, 0.000000, 9.149288, + 46.298860, 0.000000, 21.109925, 0.000000, 0.000000, 0.000000, 137.630764, 0.000000, 11.369236, 0.000000, + 0.000000, 0.000000, 59.433799, 0.000000, 0.673454, 24.245028, 0.000000, 0.742577, 1695860.958713, 121825.428571, + 0.000000, 0.000000; + + for (Eigen::Index column = 0; column < population.cols(); column++) { + for (Eigen::Index row = 0; row < population.rows(); row++) { + mio::Index index{mio::AgeGroup(row), + mio::osecirvvs::InfectionState(column)}; + model.populations[index] = population(row, column); + } + } + + model.apply_constraints(); +} diff --git a/cpp/tests/test_utils.cpp b/cpp/tests/test_utils.cpp index 5004c54e34..cbffefdfff 100644 --- a/cpp/tests/test_utils.cpp +++ b/cpp/tests/test_utils.cpp @@ -1,7 +1,7 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * -* Authors: Rene Schmieding +* Authors: Rene Schmieding * * Contact: Martin J. Kuehn * diff --git a/cpp/tests/testmain.cpp b/cpp/tests/testmain.cpp index 671021a433..d2c17044a9 100644 --- a/cpp/tests/testmain.cpp +++ b/cpp/tests/testmain.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Daniel Abele, Martin Siggel diff --git a/cpp/tests/utils.h b/cpp/tests/utils.h index e0664714a3..31750a956b 100644 --- a/cpp/tests/utils.h +++ b/cpp/tests/utils.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2020-2025 MEmilio * * Authors: Henrik Zunker diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/contact_matrix.h b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/contact_matrix.h index 440d5273b0..917a606b7d 100755 --- a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/contact_matrix.h +++ b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/contact_matrix.h @@ -72,7 +72,7 @@ void bind_damping_expression_members(DampingExpressionClass& damping_expression_ return std::vector(self.get_dampings().begin(), self.get_dampings().end()); }) .def("get_matrix_at", [](const DampingExpression& self, double t) { - return self.get_matrix_at(t); + return self.get_matrix_at(mio::SimulationTime(t)); }); bind_shape_property(damping_expression_class); } @@ -119,7 +119,7 @@ void bind_damping_expression_group_members(DampingExpressionGroupClass& cl) self[i] = m; }) .def("get_matrix_at", [](const DampingExpressionGroup& self, double t) { - return self.get_matrix_at(t); + return self.get_matrix_at(mio::SimulationTime(t)); }); } diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping.h b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping.h index 5283f36982..693ad88cd3 100755 --- a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping.h +++ b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping.h @@ -45,7 +45,7 @@ void bind_damping_members(DampingClass& damping_class) damping_class .def(pybind11::init([](const Eigen::Ref& c, double t, int level, int type) { - return Damping(c, mio::DampingLevel(level), mio::DampingType(type), mio::SimulationTime(t)); + return Damping(c, mio::DampingLevel(level), mio::DampingType(type), mio::SimulationTime(t)); }), pybind11::arg("coeffs"), pybind11::arg("t"), pybind11::arg("level") = 0, pybind11::arg("type") = 0) .def_property( @@ -60,7 +60,7 @@ void bind_damping_members(DampingClass& damping_class) return double(self.get_time()); }, [](Damping& self, double v) { - self.get_time() = mio::SimulationTime(v); + self.get_time() = mio::SimulationTime(v); }, pybind11::return_value_policy::reference_internal) .def_property( @@ -105,7 +105,7 @@ void bind_dampings_members(DampingsClass& dampings_class) self.add(d); }) .def("get_matrix_at", [](const Dampings& self, double t) { - return self.get_matrix_at(t); + return self.get_matrix_at(mio::SimulationTime(t)); }); } diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping_sampling.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping_sampling.cpp index bb7dbd0ed9..74ff582cc9 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping_sampling.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/damping_sampling.cpp @@ -36,7 +36,7 @@ void bind_damping_sampling(py::module_& m, std::string const& name) .def(py::init([](const mio::UncertainValue& value, int level, int type, double time, const std::vector& matrices, const Eigen::Ref& groups) { return mio::DampingSampling(value, mio::DampingLevel(level), mio::DampingType(type), - mio::SimulationTime(time), matrices, groups); + mio::SimulationTime(time), matrices, groups); }), py::arg("value"), py::arg("level"), py::arg("type"), py::arg("time"), py::arg("matrix_indices"), py::arg("group_weights")) @@ -64,7 +64,7 @@ void bind_damping_sampling(py::module_& m, std::string const& name) return double(self.get_time()); }, [](mio::DampingSampling& self, double t) { - self.set_time(mio::SimulationTime(t)); + self.set_time(mio::SimulationTime(t)); }) .def_property("matrix_indices", &mio::DampingSampling::get_matrix_indices, &mio::DampingSampling::set_matrix_indices) diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/dynamic_npis.h b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/dynamic_npis.h index a7f21a7b27..140cab5eef 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/dynamic_npis.h +++ b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/dynamic_npis.h @@ -44,7 +44,7 @@ void bind_dynamicNPI_members(pybind11::module_& m, std::string const& name) return static_cast(self.get_interval()); }, [](mio::DynamicNPIs& self, double v) { - self.set_interval(mio::SimulationTime(v)); + self.set_interval(mio::SimulationTime(v)); }) .def_property( "duration", @@ -52,7 +52,7 @@ void bind_dynamicNPI_members(pybind11::module_& m, std::string const& name) return static_cast(self.get_duration()); }, [](mio::DynamicNPIs& self, double v) { - self.set_duration(mio::SimulationTime(v)); + self.set_duration(mio::SimulationTime(v)); }) .def_property( "base_value", diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/uncertain_matrix.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/uncertain_matrix.cpp index c6dcacc548..68eb2e5cc3 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/uncertain_matrix.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/epidemiology/uncertain_matrix.cpp @@ -35,10 +35,10 @@ void bind_uncertain_contact_matrix(py::module_& m, std::string const& name) { bind_class, EnablePickling::Required>(m, name.c_str()) .def(py::init<>()) - .def(py::init()) + .def(py::init&>()) .def_property( "cont_freq_mat", py::overload_cast<>(&mio::UncertainContactMatrix::get_cont_freq_mat), - [](mio::UncertainContactMatrix& self, const mio::ContactMatrixGroup& c) { + [](mio::UncertainContactMatrix& self, const mio::ContactMatrixGroup& c) { self.get_cont_freq_mat() = c; }, py::return_value_policy::reference_internal) @@ -61,7 +61,7 @@ void bind_uncertain_contact_matrix(py::module_& m, std::string const& name) [](mio::UncertainContactMatrix& self, const std::vector>& v) { self.get_school_holidays().resize(v.size()); std::transform(v.begin(), v.end(), self.get_school_holidays().begin(), [](auto& p) { - return std::make_pair(mio::SimulationTime(p.first), mio::SimulationTime(p.second)); + return std::make_pair(mio::SimulationTime(p.first), mio::SimulationTime(p.second)); }); }) .def_property("school_holiday_damping", diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/mobility/graph_simulation.h b/pycode/memilio-simulation/memilio/simulation/bindings/mobility/graph_simulation.h index ba32cc9daa..c551c4be68 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/mobility/graph_simulation.h +++ b/pycode/memilio-simulation/memilio/simulation/bindings/mobility/graph_simulation.h @@ -35,10 +35,10 @@ namespace pymio template void bind_GraphSimulation(pybind11::module_& m, std::string const& name) { - using GS = mio::GraphSimulation; + using GS = mio::GraphSimulation; bind_class(m, name.c_str()) .def(pybind11::init([](Graph& graph, double t0, double dt) { - return std::make_unique(mio::make_mobility_sim(t0, dt, std::move(graph))); + return std::make_unique(mio::make_mobility_sim(t0, dt, std::move(graph))); }), pybind11::arg("graph"), pybind11::arg("t0") = 0.0, pybind11::arg("dt") = 1.0) .def_property_readonly( diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.cpp index 4d28f16107..d006473742 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.cpp @@ -31,10 +31,10 @@ void bind_mobility_parameters(py::module_& m, std::string const& name) { bind_class, EnablePickling::IfAvailable>(m, name.c_str()) .def(py::init(), py::arg("coeffs")) - .def(py::init(), py::arg("coeffs")) + .def(py::init&>(), py::arg("coeffs")) .def_property( "coefficients", py::overload_cast<>(&mio::MobilityParameters::get_coefficients), - [](mio::MobilityParameters& self, const mio::MobilityCoefficientGroup& v) { + [](mio::MobilityParameters& self, const mio::MobilityCoefficientGroup& v) { self.get_coefficients() = v; }, py::return_value_policy::reference_internal); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.h b/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.h index 9a5e5c51df..361bfea983 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.h +++ b/pycode/memilio-simulation/memilio/simulation/bindings/mobility/metapopulation_mobility_instant.h @@ -32,7 +32,7 @@ namespace pymio template void bind_MobilityGraph(pybind11::module_& m, std::string const& name) { - using G = mio::Graph, mio::MobilityEdge>; + using G = mio::Graph, mio::MobilityEdge>; bind_class(m, name.c_str()) .def(pybind11::init<>()) .def( @@ -105,14 +105,14 @@ void bind_ModelNode(pybind11::module_& m, std::string const& name) template void bind_SimulationNode(pybind11::module_& m, std::string const& name) { - bind_class>, EnablePickling::IfAvailable>(m, name.c_str()) + bind_class>, EnablePickling::IfAvailable>(m, name.c_str()) .def_property_readonly("id", - [](const mio::Node>& self) { + [](const mio::Node>& self) { return self.id; }) .def_property_readonly( "property", - [](const mio::Node>& self) -> auto& { + [](const mio::Node>& self) -> auto& { return self.property.get_simulation(); }, pybind11::return_value_policy::reference_internal); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/osecir.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/osecir.cpp index 50ea9b65b8..cd8d6fcbe6 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/osecir.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/osecir.cpp @@ -57,8 +57,8 @@ namespace //select only the first node of the graph of each run, used for parameterstudy with single nodes template -std::vector -filter_graph_results(std::vector, mio::MobilityEdge>>&& graph_results) +std::vector filter_graph_results( + std::vector, mio::MobilityEdge>>&& graph_results) { std::vector results; results.reserve(graph_results.size()); @@ -74,29 +74,34 @@ filter_graph_results(std::vector, mio::Mobil template void bind_ParameterStudy(py::module_& m, std::string const& name) { - pymio::bind_class, pymio::EnablePickling::Never>(m, name.c_str()) + pymio::bind_class, pymio::EnablePickling::Never>(m, name.c_str()) .def(py::init(), py::arg("model"), py::arg("t0"), py::arg("tmax"), py::arg("num_runs")) .def(py::init>&, double, double, double, size_t>(), py::arg("model_graph"), py::arg("t0"), py::arg("tmax"), py::arg("dt"), py::arg("num_runs")) - .def_property("num_runs", &mio::ParameterStudy::get_num_runs, - &mio::ParameterStudy::set_num_runs) - .def_property("tmax", &mio::ParameterStudy::get_tmax, &mio::ParameterStudy::set_tmax) - .def_property("t0", &mio::ParameterStudy::get_t0, &mio::ParameterStudy::set_t0) - .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model), + .def_property("num_runs", &mio::ParameterStudy::get_num_runs, + &mio::ParameterStudy::set_num_runs) + .def_property("tmax", &mio::ParameterStudy::get_tmax, + &mio::ParameterStudy::set_tmax) + .def_property("t0", &mio::ParameterStudy::get_t0, + &mio::ParameterStudy::set_t0) + .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model), py::return_value_policy::reference_internal) - .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model, py::const_), - py::return_value_policy::reference_internal) - .def_property_readonly("model_graph", py::overload_cast<>(&mio::ParameterStudy::get_model_graph), + .def_property_readonly("model", + py::overload_cast<>(&mio::ParameterStudy::get_model, py::const_), py::return_value_policy::reference_internal) .def_property_readonly("model_graph", - py::overload_cast<>(&mio::ParameterStudy::get_model_graph, py::const_), + py::overload_cast<>(&mio::ParameterStudy::get_model_graph), py::return_value_policy::reference_internal) + .def_property_readonly( + "model_graph", py::overload_cast<>(&mio::ParameterStudy::get_model_graph, py::const_), + py::return_value_policy::reference_internal) .def( "run", - [](mio::ParameterStudy& self, - std::function, mio::MobilityEdge>, size_t)> + [](mio::ParameterStudy& self, + std::function, mio::MobilityEdge>, + size_t)> handle_result) { self.run( [](auto&& g) { @@ -113,14 +118,14 @@ void bind_ParameterStudy(py::module_& m, std::string const& name) }, py::arg("handle_result_func")) .def("run", - [](mio::ParameterStudy& self) { //default argument doesn't seem to work with functions + [](mio::ParameterStudy& self) { //default argument doesn't seem to work with functions return self.run([](auto&& g) { return draw_sample(g); }); }) .def( "run_single", - [](mio::ParameterStudy& self, std::function handle_result) { + [](mio::ParameterStudy& self, std::function handle_result) { self.run( [](auto&& g) { return draw_sample(g); @@ -131,7 +136,7 @@ void bind_ParameterStudy(py::module_& m, std::string const& name) }); }, py::arg("handle_result_func")) - .def("run_single", [](mio::ParameterStudy& self) { + .def("run_single", [](mio::ParameterStudy& self) { return filter_graph_results(self.run([](auto&& g) { return draw_sample(g); })); @@ -147,7 +152,8 @@ enum class ContactLocation Count, }; -using MobilityGraph = mio::Graph>, mio::MobilityEdge>; +using MobilityGraph = + mio::Graph>, mio::MobilityEdge>; } // namespace @@ -179,8 +185,8 @@ PYBIND11_MODULE(_simulation_osecir, m) m.def("interpolate_ensemble_results", &mio::interpolate_ensemble_results>); - m.def("ensemble_mean", &mio::ensemble_mean); - m.def("ensemble_percentile", &mio::ensemble_percentile); + m.def("ensemble_mean", &mio::ensemble_mean); + m.def("ensemble_percentile", &mio::ensemble_percentile); pymio::iterable_enum(m, "InfectionState") .value("Susceptible", mio::osecir::InfectionState::Susceptible) @@ -212,7 +218,7 @@ PYBIND11_MODULE(_simulation_osecir, m) m, "Model") .def(py::init(), py::arg("num_agegroups")); - pymio::bind_Simulation>(m, "Simulation"); + pymio::bind_Simulation>(m, "Simulation"); pymio::bind_Flow_Simulation< mio::osecir::Simulation>>>(m, "FlowSimulation"); @@ -224,16 +230,16 @@ PYBIND11_MODULE(_simulation_osecir, m) py::arg("model"), py::arg("integrator") = py::none()); pymio::bind_ModelNode>(m, "ModelNode"); - pymio::bind_SimulationNode>(m, "SimulationNode"); + pymio::bind_SimulationNode>(m, "SimulationNode"); pymio::bind_ModelGraph>(m, "ModelGraph"); - pymio::bind_MobilityGraph>(m, "MobilityGraph"); + pymio::bind_MobilityGraph>(m, "MobilityGraph"); pymio::bind_GraphSimulation(m, "MobilitySimulation"); //normally, std::vector is bound to any python iterable, but this doesn't work for move-only elements //Bound the vector as a custom type that serves as output of ParameterStudy::run and input to //interpolate_ensemble_results py::bind_vector>(m, "EnsembleGraphResults"); - bind_ParameterStudy>(m, "ParameterStudy"); + bind_ParameterStudy>(m, "ParameterStudy"); m.def("set_params_distributions_normal", &mio::osecir::set_params_distributions_normal, py::arg("model"), py::arg("t0"), py::arg("tmax"), py::arg("dev_rel")); @@ -254,6 +260,7 @@ PYBIND11_MODULE(_simulation_osecir, m) const std::vector& scaling_factor_inf, double scaling_factor_icu, double tnt_capacity_factor, int num_days = 0, bool export_time_series = false) { auto result = mio::set_nodes< + double, // FP mio::osecir::TestAndTraceCapacity, mio::osecir::ContactPatterns, mio::osecir::Model, mio::MobilityParameters, mio::osecir::Parameters, decltype(mio::osecir::read_input_data_county>), decltype(mio::get_node_ids)>( @@ -279,11 +286,12 @@ PYBIND11_MODULE(_simulation_osecir, m) mio::osecir::InfectionState::InfectedNoSymptoms, mio::osecir::InfectionState::InfectedSymptoms, mio::osecir::InfectionState::Recovered}; auto weights = std::vector{0., 0., 1.0, 1.0, 0.33, 0., 0.}; - auto result = mio::set_edges, mio::MobilityParameters, - mio::MobilityCoefficientGroup, mio::osecir::InfectionState, - decltype(mio::read_mobility_plain)>(mobility_data_file, params_graph, - mobile_comp, contact_locations_size, - mio::read_mobility_plain, weights); + auto result = mio::set_edges, mio::MobilityParameters, + mio::MobilityCoefficientGroup, mio::osecir::InfectionState, + decltype(mio::read_mobility_plain)>(mobility_data_file, params_graph, + mobile_comp, contact_locations_size, + mio::read_mobility_plain, weights); return pymio::check_and_throw(result); }, py::return_value_policy::move); @@ -306,8 +314,8 @@ PYBIND11_MODULE(_simulation_osecir, m) py::return_value_policy::move); #endif // MEMILIO_HAS_JSONCPP - m.def("interpolate_simulation_result", - py::overload_cast(&mio::interpolate_simulation_result>)); + m.def("interpolate_simulation_result", py::overload_cast( + &mio::interpolate_simulation_result>)); m.def("interpolate_ensemble_results", &mio::interpolate_ensemble_results); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/osecirvvs.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/osecirvvs.cpp index 53a0a449f6..c7919b4237 100755 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/osecirvvs.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/osecirvvs.cpp @@ -51,8 +51,8 @@ namespace { //select only the first node of the graph of each run, used for parameterstudy with single nodes template -std::vector -filter_graph_results(std::vector, mio::MobilityEdge>>&& graph_results) +std::vector filter_graph_results( + std::vector, mio::MobilityEdge>>&& graph_results) { std::vector results; results.reserve(graph_results.size()); @@ -68,29 +68,34 @@ filter_graph_results(std::vector, mio::Mobil template void bind_ParameterStudy(py::module_& m, std::string const& name) { - pymio::bind_class, pymio::EnablePickling::Never>(m, name.c_str()) + pymio::bind_class, pymio::EnablePickling::Never>(m, name.c_str()) .def(py::init(), py::arg("model"), py::arg("t0"), py::arg("tmax"), py::arg("num_runs")) .def(py::init>&, double, double, double, size_t>(), py::arg("model_graph"), py::arg("t0"), py::arg("tmax"), py::arg("dt"), py::arg("num_runs")) - .def_property("num_runs", &mio::ParameterStudy::get_num_runs, - &mio::ParameterStudy::set_num_runs) - .def_property("tmax", &mio::ParameterStudy::get_tmax, &mio::ParameterStudy::set_tmax) - .def_property("t0", &mio::ParameterStudy::get_t0, &mio::ParameterStudy::set_t0) - .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model), + .def_property("num_runs", &mio::ParameterStudy::get_num_runs, + &mio::ParameterStudy::set_num_runs) + .def_property("tmax", &mio::ParameterStudy::get_tmax, + &mio::ParameterStudy::set_tmax) + .def_property("t0", &mio::ParameterStudy::get_t0, + &mio::ParameterStudy::set_t0) + .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model), py::return_value_policy::reference_internal) - .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model, py::const_), - py::return_value_policy::reference_internal) - .def_property_readonly("model_graph", py::overload_cast<>(&mio::ParameterStudy::get_model_graph), + .def_property_readonly("model", + py::overload_cast<>(&mio::ParameterStudy::get_model, py::const_), py::return_value_policy::reference_internal) .def_property_readonly("model_graph", - py::overload_cast<>(&mio::ParameterStudy::get_model_graph, py::const_), + py::overload_cast<>(&mio::ParameterStudy::get_model_graph), py::return_value_policy::reference_internal) + .def_property_readonly( + "model_graph", py::overload_cast<>(&mio::ParameterStudy::get_model_graph, py::const_), + py::return_value_policy::reference_internal) .def( "run", - [](mio::ParameterStudy& self, - std::function, mio::MobilityEdge>, size_t)> + [](mio::ParameterStudy& self, + std::function, mio::MobilityEdge>, + size_t)> handle_result, bool variant_high) { self.run( @@ -109,7 +114,7 @@ void bind_ParameterStudy(py::module_& m, std::string const& name) py::arg("handle_result_func"), py::arg("variant_high")) .def( "run", - [](mio::ParameterStudy& self, + [](mio::ParameterStudy& self, bool variant_high) { //default argument doesn't seem to work with functions return self.run([variant_high](auto&& g) { return draw_sample(g, variant_high); @@ -118,7 +123,7 @@ void bind_ParameterStudy(py::module_& m, std::string const& name) py::arg("variant_high")) .def( "run_single", - [](mio::ParameterStudy& self, std::function handle_result, + [](mio::ParameterStudy& self, std::function handle_result, bool variant_high) { self.run( [variant_high](auto&& g) { @@ -132,7 +137,7 @@ void bind_ParameterStudy(py::module_& m, std::string const& name) py::arg("handle_result_func"), py::arg("variant_high")) .def( "run_single", - [](mio::ParameterStudy& self, bool variant_high) { + [](mio::ParameterStudy& self, bool variant_high) { return filter_graph_results(self.run([variant_high](auto&& g) { return draw_sample(g, variant_high); })); @@ -149,7 +154,8 @@ enum class ContactLocation Count, }; -using MobilityGraph = mio::Graph>, mio::MobilityEdge>; +using MobilityGraph = + mio::Graph>, mio::MobilityEdge>; } // namespace @@ -262,7 +268,7 @@ PYBIND11_MODULE(_simulation_osecirvvs, m) mio::osecirvvs::Parameters>>(m, "Model") .def(py::init(), py::arg("num_agegroups")); - pymio::bind_Simulation>(m, "Simulation"); + pymio::bind_Simulation>(m, "Simulation"); pymio::bind_Flow_Simulation< mio::osecirvvs::Simulation>>>( m, "FlowSimulation"); @@ -275,16 +281,16 @@ PYBIND11_MODULE(_simulation_osecirvvs, m) py::arg("model"), py::arg("integrator") = py::none()); pymio::bind_ModelNode>(m, "ModelNode"); - pymio::bind_SimulationNode>(m, "SimulationNode"); + pymio::bind_SimulationNode>(m, "SimulationNode"); pymio::bind_ModelGraph>(m, "ModelGraph"); - pymio::bind_MobilityGraph>(m, "MobilityGraph"); + pymio::bind_MobilityGraph>(m, "MobilityGraph"); pymio::bind_GraphSimulation(m, "MobilitySimulation"); //normally, std::vector is bound to any python iterable, but this doesn't work for move-only elements //Bound the vector as a custom type that serves as output of ParameterStudy::run and input to //interpolate_ensemble_results py::bind_vector>(m, "EnsembleGraphResults"); - bind_ParameterStudy>(m, "ParameterStudy"); + bind_ParameterStudy>(m, "ParameterStudy"); m.def( "draw_sample", @@ -302,7 +308,8 @@ PYBIND11_MODULE(_simulation_osecirvvs, m) const std::vector& scaling_factor_inf, double scaling_factor_icu, double tnt_capacity_factor, int num_days = 0, bool export_time_series = false) { auto result = - mio::set_nodes, mio::osecirvvs::ContactPatterns, + mio::set_nodes, mio::osecirvvs::ContactPatterns, mio::osecirvvs::Model, mio::MobilityParameters, mio::osecirvvs::Parameters, decltype(mio::osecirvvs::read_input_data_county>), @@ -332,9 +339,10 @@ PYBIND11_MODULE(_simulation_osecirvvs, m) mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunity, mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunity}; auto weights = std::vector{0., 0., 1.0, 1.0, 0.33, 0., 0.}; - auto result = mio::set_edges, - mio::MobilityParameters, mio::MobilityCoefficientGroup, - mio::osecirvvs::InfectionState, decltype(mio::read_mobility_plain)>( + auto result = mio::set_edges, + mio::MobilityParameters, mio::MobilityCoefficientGroup, + mio::osecirvvs::InfectionState, decltype(mio::read_mobility_plain)>( mobility_data_file, params_graph, mobile_comp, contact_locations_size, mio::read_mobility_plain, weights); return pymio::check_and_throw(result); @@ -360,7 +368,8 @@ PYBIND11_MODULE(_simulation_osecirvvs, m) #endif // MEMILIO_HAS_JSONCPP m.def("interpolate_simulation_result", - py::overload_cast(&mio::interpolate_simulation_result>)); + py::overload_cast( + &mio::interpolate_simulation_result>)); m.def("interpolate_ensemble_results", &mio::interpolate_ensemble_results); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/pybind_util.h b/pycode/memilio-simulation/memilio/simulation/bindings/pybind_util.h index a30dafd059..5e0ef8876d 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/pybind_util.h +++ b/pycode/memilio-simulation/memilio/simulation/bindings/pybind_util.h @@ -191,8 +191,8 @@ std::string pretty_name() * is a value of Type T, where T is the type of the argument. */ template ::value || - std::is_same::value), + class = std::enable_if_t<(std::is_same>::value || + std::is_same>::value), void>> void bind_shape_constructor(C& cl, ArgTuples... arg_tuples) { diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/simulation.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/simulation.cpp index e8c771b829..a98012dce8 100755 --- a/pycode/memilio-simulation/memilio/simulation/bindings/simulation.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/simulation.cpp @@ -67,10 +67,10 @@ PYBIND11_MODULE(_simulation, m) pymio::bind_date(m, "Date"); - auto damping_class = pymio::bind_class(m, "Damping"); + auto damping_class = pymio::bind_class, pymio::EnablePickling::Required>(m, "Damping"); pymio::bind_damping_members(damping_class); - auto dampings_class = pymio::bind_class(m, "Dampings"); + auto dampings_class = pymio::bind_class, pymio::EnablePickling::Required>(m, "Dampings"); pymio::bind_dampings_members(dampings_class); pymio::bind_time_series(m, "TimeSeries"); @@ -78,32 +78,32 @@ PYBIND11_MODULE(_simulation, m) pymio::bind_Integrator_Core(m); auto contact_matrix_class = - pymio::bind_class(m, "ContactMatrix"); + pymio::bind_class, pymio::EnablePickling::Required>(m, "ContactMatrix"); pymio::bind_damping_expression_members(contact_matrix_class); - contact_matrix_class.def_property_readonly("num_groups", &mio::ContactMatrix::get_num_groups); + contact_matrix_class.def_property_readonly("num_groups", &mio::ContactMatrix::get_num_groups); auto contact_matrix_group_class = - pymio::bind_class(m, "ContactMatrixGroup"); + pymio::bind_class, pymio::EnablePickling::Required>(m, "ContactMatrixGroup"); pymio::bind_damping_expression_group_members(contact_matrix_group_class); - contact_matrix_group_class.def_property_readonly("num_groups", &mio::ContactMatrixGroup::get_num_groups); + contact_matrix_group_class.def_property_readonly("num_groups", &mio::ContactMatrixGroup::get_num_groups); pymio::bind_damping_sampling(m, "DampingSampling"); pymio::bind_uncertain_contact_matrix(m, "UncertainContactMatrix"); auto mobility_damping_class = - pymio::bind_class(m, "MobilityDamping"); + pymio::bind_class, pymio::EnablePickling::Required>(m, "MobilityDamping"); pymio::bind_damping_members(mobility_damping_class); auto mobility_dampings_class = - pymio::bind_class(m, "MobilityDampings"); + pymio::bind_class, pymio::EnablePickling::Required>(m, "MobilityDampings"); pymio::bind_dampings_members(mobility_dampings_class); auto mobility_coeffs_class = - pymio::bind_class(m, "MobilityCoefficients"); + pymio::bind_class, pymio::EnablePickling::Required>(m, "MobilityCoefficients"); pymio::bind_damping_expression_members(mobility_coeffs_class); - auto mobility_coeff_group_class = pymio::bind_class( + auto mobility_coeff_group_class = pymio::bind_class, pymio::EnablePickling::Required>( m, "MobilityCoefficientGroup"); pymio::bind_damping_expression_group_members(mobility_coeff_group_class); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/utils/custom_index_array.h b/pycode/memilio-simulation/memilio/simulation/bindings/utils/custom_index_array.h index 9c47e4a0cb..01ed4c1c7d 100755 --- a/pycode/memilio-simulation/memilio/simulation/bindings/utils/custom_index_array.h +++ b/pycode/memilio-simulation/memilio/simulation/bindings/utils/custom_index_array.h @@ -38,10 +38,12 @@ namespace pymio { template -struct has_indices_attribute : std::false_type {}; +struct has_indices_attribute : std::false_type { +}; template -struct has_indices_attribute().size().indices)>> : std::true_type {}; +struct has_indices_attribute().size().indices)>> : std::true_type { +}; // Recursively bind the members of custom index array // that require a single Tag as a template argument. @@ -64,7 +66,7 @@ void bind_single_tag_template_members(pybind11::module_& m, pybind11::class_& c.def(("size_" + tname).c_str(), &C::template size); // Only add if the Index is multidimensional, e.g. only when Index::indices exists, because it is used in resize - if constexpr (has_indices_attribute::value){ + if constexpr (has_indices_attribute::value) { c.def(("resize_" + tname).c_str(), &C::template resize); }