From 2d4aec10e0b6a80dddb6e05772671c523d19dd22 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:29:07 +0200 Subject: [PATCH 01/49] add model with Populations with Regions --- cpp/CMakeLists.txt | 1 + cpp/examples/CMakeLists.txt | 4 + cpp/examples/ode_sir_mobility.cpp | 63 +++++++ cpp/models/ode_sir_mobility/CMakeLists.txt | 12 ++ cpp/models/ode_sir_mobility/README.md | 21 +++ cpp/models/ode_sir_mobility/infection_state.h | 25 +++ cpp/models/ode_sir_mobility/model.cpp | 10 ++ cpp/models/ode_sir_mobility/model.h | 52 ++++++ cpp/models/ode_sir_mobility/parameters.h | 162 ++++++++++++++++++ cpp/models/ode_sir_mobility/regions.h | 26 +++ 10 files changed, 376 insertions(+) create mode 100644 cpp/examples/ode_sir_mobility.cpp create mode 100644 cpp/models/ode_sir_mobility/CMakeLists.txt create mode 100644 cpp/models/ode_sir_mobility/README.md create mode 100644 cpp/models/ode_sir_mobility/infection_state.h create mode 100644 cpp/models/ode_sir_mobility/model.cpp create mode 100644 cpp/models/ode_sir_mobility/model.h create mode 100644 cpp/models/ode_sir_mobility/parameters.h create mode 100644 cpp/models/ode_sir_mobility/regions.h diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e7a3306c24..fec9d5b711 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -124,6 +124,7 @@ if(MEMILIO_BUILD_MODELS) add_subdirectory(models/ide_seir) add_subdirectory(models/ode_seir) add_subdirectory(models/ode_sir) + add_subdirectory(models/ode_sir_mobility) add_subdirectory(models/sde_sir) add_subdirectory(models/sde_sirs) endif() diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index e982766dc3..4b3250b7b3 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -26,6 +26,10 @@ add_executable(sde_sir_example sde_sir.cpp) target_link_libraries(sde_sir_example PRIVATE memilio sde_sir) target_compile_options(sde_sir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(ode_sir_mobility_example ode_sir_mobility.cpp) +target_link_libraries(ode_sir_mobility_example PRIVATE memilio ode_sir_mobility) +target_compile_options(ode_sir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(sde_sirs_example sde_sirs.cpp) target_link_libraries(sde_sirs_example PRIVATE memilio sde_sirs) target_compile_options(sde_sirs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/examples/ode_sir_mobility.cpp b/cpp/examples/ode_sir_mobility.cpp new file mode 100644 index 0000000000..e7510298cb --- /dev/null +++ b/cpp/examples/ode_sir_mobility.cpp @@ -0,0 +1,63 @@ + +#include "memilio/compartments/simulation.h" +#include "memilio/math/euler.h" +#include "memilio/utils/logging.h" +#include "memilio/epidemiology/age_group.h" +#include "ode_sir_mobility/infection_state.h" +#include "ode_sir_mobility/model.h" +#include "ode_sir_mobility/parameters.h" +#include "ode_sir_mobility/regions.h" + +int main() +{ + mio::set_log_level(mio::LogLevel::debug); + + double t0 = 0.; + double tmax = 50.; + double dt = 0.1; + + double total_population = 1061000; + + mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); + + mio::osirmobility::Model model(5); + + model.populations[{mio::Index(mio::osirmobility::InfectionState::Infected, mio::osirmobility::Region(1))}] = 1000; + model.populations[{mio::Index(mio::osirmobility::InfectionState::Recovered, mio::osirmobility::Region(1))}] = 1000; + model.populations[{mio::Index(mio::osirmobility::InfectionState::Susceptible, mio::osirmobility::Region(1))}] = + total_population - + model.populations[{mio::Index(mio::osirmobility::InfectionState::Infected, mio::osirmobility::Region(1))}] - + model.populations[{mio::Index(mio::osirmobility::InfectionState::Recovered, mio::osirmobility::Region(1))}]; + model.parameters.set(2); + model.parameters.set(0.04); + model.parameters.get().get_baseline()(0, 0) = 2.7; + model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + + auto integrator = std::make_shared(); + + model.check_constraints(); + + auto sir = simulate(t0, tmax, dt, model, integrator); + + bool print_to_terminal = true; + + if (print_to_terminal) { + std::vector vars = {"S", "I", "R"}; + printf("\n # t"); + for (size_t k = 0; k < (size_t)mio::osirmobility::InfectionState::Count; k++) { + printf(" %s", vars[k].c_str()); + } + + auto num_points = static_cast(sir.get_num_time_points()); + for (size_t i = 0; i < num_points; i++) { + printf("\n%.14f ", sir.get_time(i)); + Eigen::VectorXd res_j = sir.get_value(i); + for (size_t j = 0; j < (size_t)mio::osirmobility::InfectionState::Count; j++) { + printf(" %.14f", res_j[j]); + } + } + + Eigen::VectorXd res_j = sir.get_last_value(); + printf("\nnumber total: %f \n", res_j[0] + res_j[1] + res_j[2]); + } +} diff --git a/cpp/models/ode_sir_mobility/CMakeLists.txt b/cpp/models/ode_sir_mobility/CMakeLists.txt new file mode 100644 index 0000000000..55605f5c94 --- /dev/null +++ b/cpp/models/ode_sir_mobility/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(ode_sir_mobility + infection_state.h + model.h + model.cpp + parameters.h +) +target_link_libraries(ode_sir_mobility PUBLIC memilio) +target_include_directories(ode_sir_mobility PUBLIC + $ + $ +) +target_compile_options(ode_sir_mobility PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/models/ode_sir_mobility/README.md b/cpp/models/ode_sir_mobility/README.md new file mode 100644 index 0000000000..77f5698546 --- /dev/null +++ b/cpp/models/ode_sir_mobility/README.md @@ -0,0 +1,21 @@ + +# ODE SIR compartment model + +This model is a very simple ODE model with only three compartments and few parameters, mostly for demonstration of the MEmilio framework: +- Susceptible, may become infected at any time +- Infected, will be recovered after some time +- Recovered, recovered from infectious process (dead or recovered) + +We assume simulations over short periods of time, so that the population size can be considered constant and birth as well as (natural) mortality rates can be ignored. + +Below is an overview of the model architecture and its compartments. + +![SIR_model](https://github.com/SciCompMod/memilio/assets/69154294/01c9a2ae-2f5c-4bad-b7f0-34de651f2c73) +| Mathematical variable | C++ variable name | Description | +|---------------------------- | --------------- | -------------------------------------------------------------------------------------------------- | +| $\phi$ | `ContactPatterns` | Daily contact rate / Number of daily contacts. | +| $\rho$ | `TransmissionProbabilityOnContact` | Transmission risk for people located in the Susceptible compartment. | +| $N$ | `populations.get_total()` | Total population. | +| $T_{I}$ | `TimeInfected` | Time in days an individual stays in the Infected compartment. | + +An example can be found in [examples/ode_sir.cpp](../../examples/ode_sir.cpp) diff --git a/cpp/models/ode_sir_mobility/infection_state.h b/cpp/models/ode_sir_mobility/infection_state.h new file mode 100644 index 0000000000..dc98471c73 --- /dev/null +++ b/cpp/models/ode_sir_mobility/infection_state.h @@ -0,0 +1,25 @@ + +#ifndef ODESIRMOBILITY_INFECTIONSTATE_H +#define ODESIRMOBILITY_INFECTIONSTATE_H + +namespace mio +{ +namespace osirmobility +{ + +/** + * @brief The InfectionState enum describes the possible + * categories for the infectious state of persons + */ +enum class InfectionState +{ + Susceptible, + Infected, + Recovered, + Count +}; + +} // namespace osir +} // namespace mio + +#endif // ODESIR_INFECTIONSTATE_H diff --git a/cpp/models/ode_sir_mobility/model.cpp b/cpp/models/ode_sir_mobility/model.cpp new file mode 100644 index 0000000000..0f8bf7b573 --- /dev/null +++ b/cpp/models/ode_sir_mobility/model.cpp @@ -0,0 +1,10 @@ + +#include "ode_sir_mobility/model.h" + +namespace mio +{ +namespace osirmobility +{ + +} // namespace osir +} // namespace mio diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h new file mode 100644 index 0000000000..8ba2678299 --- /dev/null +++ b/cpp/models/ode_sir_mobility/model.h @@ -0,0 +1,52 @@ + +#ifndef ODESIRMOBILITY_MODEL_H +#define ODESIRMOBILITY_MODEL_H + +#include "memilio/compartments/compartmentalmodel.h" +#include "memilio/epidemiology/populations.h" +#include "memilio/epidemiology/contact_matrix.h" +#include "memilio/mobility/graph.h" +#include "ode_sir_mobility/infection_state.h" +#include "ode_sir_mobility/parameters.h" +#include "ode_sir_mobility/regions.h" + +namespace mio +{ +namespace osirmobility +{ + +/******************** + * define the model * + ********************/ + +class Model : public CompartmentalModel, Parameters> +{ + using Base = CompartmentalModel, Parameters>; + +public: + Model(int num_regions) + : Base(Populations({InfectionState::Count, Region(num_regions)}, 0.), ParameterSet()) + { + } + + void get_derivatives(Eigen::Ref pop, Eigen::Ref y, double t, + Eigen::Ref dydt) const override + { + auto& params = this->parameters; + double coeffStoI = params.get().get_matrix_at(t)(0, 0) * + params.get() / populations.get_total(); + + dydt[(size_t)InfectionState::Susceptible] = + -coeffStoI * y[(size_t)InfectionState::Susceptible] * pop[(size_t)InfectionState::Infected]; + dydt[(size_t)InfectionState::Infected] = + coeffStoI * y[(size_t)InfectionState::Susceptible] * pop[(size_t)InfectionState::Infected] - + (1.0 / params.get()) * y[(size_t)InfectionState::Infected]; + dydt[(size_t)InfectionState::Recovered] = + (1.0 / params.get()) * y[(size_t)InfectionState::Infected]; + } +}; + +} // namespace osir +} // namespace mio + +#endif // ODESIR_MODEL_H diff --git a/cpp/models/ode_sir_mobility/parameters.h b/cpp/models/ode_sir_mobility/parameters.h new file mode 100644 index 0000000000..8a5d6b23d1 --- /dev/null +++ b/cpp/models/ode_sir_mobility/parameters.h @@ -0,0 +1,162 @@ + +#ifndef SIRMOBILITY_PARAMETERS_H +#define SIRMOBILITY_PARAMETERS_H + +#include "memilio/utils/uncertain_value.h" +#include "memilio/epidemiology/contact_matrix.h" +#include "memilio/utils/parameter_set.h" + +#include + +namespace mio +{ +namespace osirmobility +{ + +/******************************************* + * Define Parameters of the SIR model * + *******************************************/ + +/** + * @brief probability of getting infected from a contact + */ +struct TransmissionProbabilityOnContact { + using Type = UncertainValue; + static Type get_default() + { + return Type(1.0); + } + static std::string name() + { + return "TransmissionProbabilityOnContact"; + } +}; + +/** + * @brief the infectious time in day unit + */ +struct TimeInfected { + using Type = UncertainValue; + static Type get_default() + { + return Type(6.0); + } + static std::string name() + { + return "TimeInfected"; + } +}; + +/** + * @brief the contact patterns within the society are modelled using a ContactMatrix + */ +struct ContactPatterns { + using Type = ContactMatrix; + static Type get_default() + { + return Type{1}; + } + static std::string name() + { + return "ContactPatterns"; + } +}; + +using ParametersBase = ParameterSet; + +/** + * @brief Parameters of SIR model. + */ +class Parameters : public ParametersBase +{ +public: + Parameters() + : 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]. + * + * Attention: This function should be used with care. It is necessary for some test problems to run through quickly, + * but in a manual execution of an example, check_constraints() may be preferred. Note that the apply_constraints() + * function can and will not set Parameters to meaningful values in an epidemiological or virological context, + * as all models are designed to be transferable to multiple diseases. Consequently, only acceptable + * (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. + */ + bool apply_constraints() + { + double tol_times = 1e-1; + + int corrected = false; + if (this->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; + } + if (this->get() < 0.0 || + this->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; + } + return corrected; + } + + /** + * @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. + */ + bool check_constraints() const + { + double tol_times = 1e-1; + + if (this->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); + return true; + } + if (this->get() < 0.0 || + this->get() > 1.0) { + log_error( + "Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or greater {:.4f}", + this->get(), 0.0, 1.0); + return true; + } + return false; + } + +private: + Parameters(ParametersBase&& base) + : ParametersBase(std::move(base)) + { + } + +public: + /** + * deserialize an object of this class. + * @see mio::deserialize + */ + template + static IOResult deserialize(IOContext& io) + { + BOOST_OUTCOME_TRY(base, ParametersBase::deserialize(io)); + return success(Parameters(std::move(base))); + } +}; + +} // namespace osir +} // namespace mio + +#endif // SIR_PARAMETERS_H diff --git a/cpp/models/ode_sir_mobility/regions.h b/cpp/models/ode_sir_mobility/regions.h new file mode 100644 index 0000000000..d3c8b528a7 --- /dev/null +++ b/cpp/models/ode_sir_mobility/regions.h @@ -0,0 +1,26 @@ + +#ifndef ODESIRMOBILITY_REGIONS_H +#define ODESIRMOBILITY_REGIONS_H + +#include "memilio/utils/index.h" + +namespace mio +{ +namespace osirmobility +{ + +/** + * @brief The AgeGroup struct is used as a dynamically + * sized tag for all age dependent categories + */ +struct Region : public Index { + Region(size_t val) + : Index(val) + { + } +}; + +} // namespace osirmobility +} // namespace mio + +#endif From 941625639680870f5dbf19db2c8140a0165b9fc8 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:16:53 +0200 Subject: [PATCH 02/49] working SIR model without infections during commuting --- cpp/examples/CMakeLists.txt | 4 ++ cpp/examples/ode_sir_mobility_simple.cpp | 83 ++++++++++++++++++++++++ cpp/models/ode_sir_mobility/model.h | 58 +++++++++++------ cpp/models/ode_sir_mobility/parameters.h | 56 +++++++++++++--- 4 files changed, 174 insertions(+), 27 deletions(-) create mode 100644 cpp/examples/ode_sir_mobility_simple.cpp diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 4b3250b7b3..5f6e753cf0 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -30,6 +30,10 @@ add_executable(ode_sir_mobility_example ode_sir_mobility.cpp) target_link_libraries(ode_sir_mobility_example PRIVATE memilio ode_sir_mobility) target_compile_options(ode_sir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(ode_sir_mobility_example_simple ode_sir_mobility_simple.cpp) +target_link_libraries(ode_sir_mobility_example_simple PRIVATE memilio ode_sir_mobility) +target_compile_options(ode_sir_mobility_example_simple PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(sde_sirs_example sde_sirs.cpp) target_link_libraries(sde_sirs_example PRIVATE memilio sde_sirs) target_compile_options(sde_sirs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/examples/ode_sir_mobility_simple.cpp b/cpp/examples/ode_sir_mobility_simple.cpp new file mode 100644 index 0000000000..69e77899d9 --- /dev/null +++ b/cpp/examples/ode_sir_mobility_simple.cpp @@ -0,0 +1,83 @@ + +#include "memilio/compartments/simulation.h" +#include "memilio/math/euler.h" +#include "memilio/utils/logging.h" +#include "memilio/utils/custom_index_array.h" +#include "memilio/io/mobility_io.h" +#include "ode_sir_mobility/infection_state.h" +#include "ode_sir_mobility/model.h" +#include "ode_sir_mobility/parameters.h" +#include "ode_sir_mobility/regions.h" +#include "ode_sir_mobility/contact_location.h" + +int main() +{ + mio::set_log_level(mio::LogLevel::debug); + + double t0 = 0.; + double tmax = 50.; + double dt = 1; + + size_t number_regions = 2; + size_t total_population_per_region = 10; + + mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); + + mio::osirmobility::Model model(number_regions); + + for (size_t i = 0; i < number_regions; i++) { + // model.populations[{mio::Index( + // mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Infected)}] = 1; + model.populations[{mio::Index( + mio::osirmobility::Region(1), mio::osirmobility::InfectionState::Infected)}] = 5; + model.populations[{mio::Index( + mio::osirmobility::Region(0), mio::osirmobility::InfectionState::Infected)}] = 0; + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Recovered)}] = 0; + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Susceptible)}] = + total_population_per_region - + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Infected)}] - + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Recovered)}]; + } + + model.parameters.set(2); + model.parameters.set(0.04); + model.parameters.get().get_baseline()(0, 0) = 1.; + // model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.get().push_back( + {mio::osirmobility::Region(0), mio::osirmobility::Region(1), 0.}); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); + + auto integrator = std::make_shared(); + + model.check_constraints(); + + auto sir = simulate(t0, tmax, dt, model, integrator); + + bool print_to_terminal = true; + + sir.print_table(); + + if (print_to_terminal) { + + 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::osirmobility::InfectionState::Count; k++) { + double dummy = 0; + + for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { + printf("\t %s[%d]: %.2f", vars[k].c_str(), (int)i, + sir.get_last_value()[k + (size_t)mio::osirmobility::InfectionState::Count * (int)i]); + dummy += sir.get_last_value()[k + (size_t)mio::osirmobility::InfectionState::Count * (int)i]; + } + + printf("\t %s_total: %.2f\n", vars[k].c_str(), dummy); + } + } +} diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h index 8ba2678299..5eb8ca0fdd 100644 --- a/cpp/models/ode_sir_mobility/model.h +++ b/cpp/models/ode_sir_mobility/model.h @@ -2,10 +2,8 @@ #ifndef ODESIRMOBILITY_MODEL_H #define ODESIRMOBILITY_MODEL_H -#include "memilio/compartments/compartmentalmodel.h" +#include "memilio/compartments/flow_model.h" #include "memilio/epidemiology/populations.h" -#include "memilio/epidemiology/contact_matrix.h" -#include "memilio/mobility/graph.h" #include "ode_sir_mobility/infection_state.h" #include "ode_sir_mobility/parameters.h" #include "ode_sir_mobility/regions.h" @@ -19,34 +17,56 @@ namespace osirmobility * define the model * ********************/ -class Model : public CompartmentalModel, Parameters> +using Flows = TypeList, + Flow>; + +class Model : public FlowModel, Parameters, Flows> { - using Base = CompartmentalModel, Parameters>; + + using Base = FlowModel, Parameters, Flows>; public: Model(int num_regions) - : Base(Populations({InfectionState::Count, Region(num_regions)}, 0.), ParameterSet()) + : Base(Populations({Region(num_regions), InfectionState::Count}, 0.), ParameterSet(num_regions)) { } + // Einmal über den Vektor und später nochmal über die Regions - void get_derivatives(Eigen::Ref pop, Eigen::Ref y, double t, - Eigen::Ref dydt) const override + void get_flows(Eigen::Ref pop, Eigen::Ref y, double t, + Eigen::Ref flows) const override { auto& params = this->parameters; + auto& population = this->populations; + double coeffStoI = params.get().get_matrix_at(t)(0, 0) * - params.get() / populations.get_total(); - - dydt[(size_t)InfectionState::Susceptible] = - -coeffStoI * y[(size_t)InfectionState::Susceptible] * pop[(size_t)InfectionState::Infected]; - dydt[(size_t)InfectionState::Infected] = - coeffStoI * y[(size_t)InfectionState::Susceptible] * pop[(size_t)InfectionState::Infected] - - (1.0 / params.get()) * y[(size_t)InfectionState::Infected]; - dydt[(size_t)InfectionState::Recovered] = - (1.0 / params.get()) * y[(size_t)InfectionState::Infected]; + params.get() / population.get_total(); + + Region n_regions = params.get_num_regions(); + + for (auto edge : params.get()) { + auto start_region = get<0>(edge); + auto end_region = get<1>(edge); + auto strength = get(edge); + // s_n += h_mn/P_m * i_m + flows[get_flat_flow_index(start_region)] += + strength * pop[population.get_flat_index({end_region, InfectionState::Infected})]; + // s_m += h_mn/P_m * i_n + flows[get_flat_flow_index(end_region)] += + strength * pop[population.get_flat_index({start_region, InfectionState::Infected})]; + } + + for (auto i = Region(0); i < n_regions; i++) { + flows[get_flat_flow_index({i})] += + pop[population.get_flat_index({i, InfectionState::Infected})]; + flows[get_flat_flow_index({i})] *= + coeffStoI * y[population.get_flat_index({i, InfectionState::Susceptible})]; + flows[get_flat_flow_index({i})] = + (1.0 / params.get()) * y[population.get_flat_index({i, InfectionState::Infected})]; + } } }; -} // namespace osir +} // namespace osirmobility } // namespace mio -#endif // ODESIR_MODEL_H +#endif // ODESIRMOBILITY_MODEL_H diff --git a/cpp/models/ode_sir_mobility/parameters.h b/cpp/models/ode_sir_mobility/parameters.h index 8a5d6b23d1..a429bf1f26 100644 --- a/cpp/models/ode_sir_mobility/parameters.h +++ b/cpp/models/ode_sir_mobility/parameters.h @@ -5,6 +5,9 @@ #include "memilio/utils/uncertain_value.h" #include "memilio/epidemiology/contact_matrix.h" #include "memilio/utils/parameter_set.h" +#include "memilio/utils/custom_index_array.h" +#include +#include "regions.h" #include @@ -62,7 +65,35 @@ struct ContactPatterns { } }; -using ParametersBase = ParameterSet; +/** + * @brief The mean number of Persons migrating from one Region to another during a Time interval + */ +struct CommutingRatio { + using Type = std::vector>; + static Type get_default() + { + return Type({{Region(0), Region(0), 0.}}); + } + static std::string name() + { + return "CommutingRatio"; + } +}; + +struct ImpactCommuters { + using Type = UncertainValue; + static Type get_default() + { + return Type(1.0); + } + static std::string name() + { + return "ImpactCommuters"; + } +}; + +using ParametersBase = + ParameterSet; /** * @brief Parameters of SIR model. @@ -70,11 +101,17 @@ using ParametersBase = ParameterSet Date: Fri, 26 Apr 2024 14:39:52 +0200 Subject: [PATCH 03/49] add infections during commuting and age groups --- cpp/examples/ode_sir_mobility_simple.cpp | 139 +++++++++++++++++++---- cpp/models/ode_sir_mobility/model.h | 74 ++++++++---- cpp/models/ode_sir_mobility/parameters.h | 67 +++++++---- 3 files changed, 217 insertions(+), 63 deletions(-) diff --git a/cpp/examples/ode_sir_mobility_simple.cpp b/cpp/examples/ode_sir_mobility_simple.cpp index 69e77899d9..465a45ecfb 100644 --- a/cpp/examples/ode_sir_mobility_simple.cpp +++ b/cpp/examples/ode_sir_mobility_simple.cpp @@ -9,6 +9,99 @@ #include "ode_sir_mobility/parameters.h" #include "ode_sir_mobility/regions.h" #include "ode_sir_mobility/contact_location.h" +#include "memilio/io/io.h" + +mio::IOResult>>> read_path_mobility(const std::string& filename) +{ + BOOST_OUTCOME_TRY(num_lines, mio::count_lines(filename)); + + if (num_lines == 0) { + std::vector>> arr(0, std::vector>(0)); + return mio::success(arr); + } + + std::fstream file; + file.open(filename, std::ios::in); + if (!file.is_open()) { + return failure(mio::StatusCode::FileNotFound, filename); + } + + std::vector>> arr(std::sqrt(num_lines), + std::vector>(std::sqrt(num_lines))); + + try { + std::string tp; + while (getline(file, tp)) { + auto line = mio::split(tp, ' '); + int indx_x = std::stoi(line[0]); + int indx_y = std::stoi(line[1]); + if (indx_x != indx_y) { + auto path = std::accumulate(line.begin() + 2, line.end(), std::string("")); + + // string -> vector of integers + std::vector path_vec; + + // Remove the square brackets and \r + path = path.substr(1, path.size() - 3); + std::stringstream ss(path); + std::string token; + + // get numbers and save them in path_vec + while (std::getline(ss, token, ',')) { + path_vec.push_back(std::stoi(token)); + } + + // Sorted by end location + for (int number : path_vec) { + if (number != indx_x && number != indx_y) { + arr[indx_x][indx_y].push_back(number); + } + } + } + } + } + catch (std::runtime_error& ex) { + return failure(mio::StatusCode::InvalidFileFormat, filename + ": " + ex.what()); + } + + return mio::success(arr); +} + +mio::IOResult run(const std::string& filename, mio::osirmobility::Model& model) +{ + BOOST_OUTCOME_TRY(mobility_paths, read_path_mobility(filename)); + size_t n_regions = (size_t)model.parameters.get_num_regions(); + for (size_t i = 0; i < n_regions; i++) { + for (size_t j = 0; j < n_regions; j++) { + if (j == i) { + continue; + } + std::sort(mobility_paths[i][j].begin(), mobility_paths[i][j].end()); + std::vector intersection_int; + std::vector intersection_region(intersection_int.size(), + mio::osirmobility::Region(0)); + for (size_t k = 0; k < n_regions; k++) { + if (k == i || k == j) { + continue; + } + std::sort(mobility_paths[k][j].begin(), mobility_paths[k][j].end()); + std::set_intersection(mobility_paths[i][j].begin(), mobility_paths[i][j].end(), + mobility_paths[k][j].begin(), mobility_paths[k][j].end(), + std::back_inserter(intersection_int)); + + if (intersection_int.begin() != intersection_int.end()) { + intersection_region.push_back(mio::osirmobility::Region(k)); + intersection_int.pop_back(); + } + } + if (intersection_region.begin() != intersection_region.end()) { + model.parameters.get()[mio::Index( + mio::osirmobility::Region(i), mio::osirmobility::Region(j))] = intersection_region; + } + } + } + return mio::success(); +} int main() { @@ -18,39 +111,47 @@ int main() double tmax = 50.; double dt = 1; - size_t number_regions = 2; + size_t number_regions = 4; + size_t number_age_groups = 1; size_t total_population_per_region = 10; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::osirmobility::Model model(number_regions); + const std::string& filename = ""; + + mio::osirmobility::Model model(number_regions, number_age_groups); for (size_t i = 0; i < number_regions; i++) { - // model.populations[{mio::Index( - // mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Infected)}] = 1; - model.populations[{mio::Index( - mio::osirmobility::Region(1), mio::osirmobility::InfectionState::Infected)}] = 5; - model.populations[{mio::Index( - mio::osirmobility::Region(0), mio::osirmobility::InfectionState::Infected)}] = 0; - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Recovered)}] = 0; - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Susceptible)}] = + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] = 1; + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}] = 0; + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Susceptible)}] = total_population_per_region - - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Infected)}] - - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::osirmobility::InfectionState::Recovered)}]; + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] - + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}]; } model.parameters.set(2); model.parameters.set(0.04); + model.parameters.set(1.); model.parameters.get().get_baseline()(0, 0) = 1.; - // model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); - model.parameters.get().push_back( - {mio::osirmobility::Region(0), mio::osirmobility::Region(1), 0.}); + model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(2), 0.8}); + model.parameters.get().push_back( + {mio::osirmobility::Region(2), mio::osirmobility::Region(0), 0.5}); + model.parameters.get().push_back( + {mio::osirmobility::Region(0), mio::osirmobility::Region(3), 1.0}); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(3), 0.8}); + + auto preprocess = run(filename, model); auto integrator = std::make_shared(); diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h index 5eb8ca0fdd..a121f2ae0a 100644 --- a/cpp/models/ode_sir_mobility/model.h +++ b/cpp/models/ode_sir_mobility/model.h @@ -7,6 +7,7 @@ #include "ode_sir_mobility/infection_state.h" #include "ode_sir_mobility/parameters.h" #include "ode_sir_mobility/regions.h" +#include "memilio/epidemiology/age_group.h" namespace mio { @@ -20,14 +21,15 @@ namespace osirmobility using Flows = TypeList, Flow>; -class Model : public FlowModel, Parameters, Flows> +class Model : public FlowModel, Parameters, Flows> { - using Base = FlowModel, Parameters, Flows>; + using Base = FlowModel, Parameters, Flows>; public: - Model(int num_regions) - : Base(Populations({Region(num_regions), InfectionState::Count}, 0.), ParameterSet(num_regions)) + Model(int num_regions, int num_agegroups) + : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}, 0.), + ParameterSet(num_regions, num_agegroups)) { } // Einmal über den Vektor und später nochmal über die Regions @@ -41,27 +43,53 @@ class Model : public FlowModel().get_matrix_at(t)(0, 0) * params.get() / population.get_total(); - Region n_regions = params.get_num_regions(); + Region n_regions = params.get_num_regions(); + AgeGroup n_agegroups = params.get_num_agegroups(); + for (auto age = AgeGroup(0); age < n_agegroups; age++) { + for (auto edge : params.get()) { + auto start_region = get<0>(edge); + auto end_region = get<1>(edge); + auto strength = get(edge); + if (start_region == end_region) { + continue; + } + // s_n += h_mn/P_m * i_m + flows[get_flat_flow_index( + {start_region, age})] += + strength * pop[population.get_flat_index({end_region, age, InfectionState::Infected})]; + // s_m += h_mn/P_m * i_n + flows[get_flat_flow_index({end_region, age})] += + strength * pop[population.get_flat_index({start_region, age, InfectionState::Infected})]; - for (auto edge : params.get()) { - auto start_region = get<0>(edge); - auto end_region = get<1>(edge); - auto strength = get(edge); - // s_n += h_mn/P_m * i_m - flows[get_flat_flow_index(start_region)] += - strength * pop[population.get_flat_index({end_region, InfectionState::Infected})]; - // s_m += h_mn/P_m * i_n - flows[get_flat_flow_index(end_region)] += - strength * pop[population.get_flat_index({start_region, InfectionState::Infected})]; - } + // s_n += gamma * h_nm/P_n * sum(h_km/P_k * p_nm,k * i_k) + for (auto edge_commuter : params.get()) { + auto start_region_commuter = get<0>(edge_commuter); + auto end_region_commuter = get<1>(edge_commuter); + auto strength_commuter = get(edge_commuter); + if (end_region_commuter != end_region || start_region_commuter == start_region || + ((std::find(params.get()[{start_region, end_region}].begin(), + params.get()[{start_region, end_region}].end(), + start_region_commuter)) == + params.get()[{start_region, end_region}].end())) { + continue; + } + auto test = params.get()[{start_region, end_region}]; + flows[get_flat_flow_index( + {start_region, age})] += + params.get() * strength * strength_commuter * + pop[population.get_flat_index({start_region_commuter, age, InfectionState::Infected})]; + } + } - for (auto i = Region(0); i < n_regions; i++) { - flows[get_flat_flow_index({i})] += - pop[population.get_flat_index({i, InfectionState::Infected})]; - flows[get_flat_flow_index({i})] *= - coeffStoI * y[population.get_flat_index({i, InfectionState::Susceptible})]; - flows[get_flat_flow_index({i})] = - (1.0 / params.get()) * y[population.get_flat_index({i, InfectionState::Infected})]; + for (auto i = Region(0); i < n_regions; i++) { + flows[get_flat_flow_index({i, age})] += + pop[population.get_flat_index({i, age, InfectionState::Infected})]; + flows[get_flat_flow_index({i, age})] *= + coeffStoI * y[population.get_flat_index({i, age, InfectionState::Susceptible})]; + flows[get_flat_flow_index({i, age})] = + (1.0 / params.get()) * + y[population.get_flat_index({i, age, InfectionState::Infected})]; + } } } }; diff --git a/cpp/models/ode_sir_mobility/parameters.h b/cpp/models/ode_sir_mobility/parameters.h index a429bf1f26..90e95dad3a 100644 --- a/cpp/models/ode_sir_mobility/parameters.h +++ b/cpp/models/ode_sir_mobility/parameters.h @@ -4,10 +4,10 @@ #include "memilio/utils/uncertain_value.h" #include "memilio/epidemiology/contact_matrix.h" +#include "memilio/epidemiology/age_group.h" #include "memilio/utils/parameter_set.h" #include "memilio/utils/custom_index_array.h" -#include -#include "regions.h" +#include "ode_sir_mobility/regions.h" #include @@ -16,16 +16,16 @@ namespace mio namespace osirmobility { -/******************************************* - * Define Parameters of the SIR model * - *******************************************/ +/**************************************************** + * Define Parameters of the SIR model with mobility * + ****************************************************/ /** - * @brief probability of getting infected from a contact - */ + * @brief Probability of getting infected from a contact. + */ struct TransmissionProbabilityOnContact { using Type = UncertainValue; - static Type get_default() + static Type get_default(Region) { return Type(1.0); } @@ -36,11 +36,11 @@ struct TransmissionProbabilityOnContact { }; /** - * @brief the infectious time in day unit + * @brief The infectious time in day unit. */ struct TimeInfected { using Type = UncertainValue; - static Type get_default() + static Type get_default(Region) { return Type(6.0); } @@ -51,11 +51,11 @@ struct TimeInfected { }; /** - * @brief the contact patterns within the society are modelled using a ContactMatrix + * @brief The contact patterns within the society are modelled using a ContactMatrix. */ struct ContactPatterns { using Type = ContactMatrix; - static Type get_default() + static Type get_default(Region) { return Type{1}; } @@ -66,11 +66,11 @@ struct ContactPatterns { }; /** - * @brief The mean number of Persons migrating from one Region to another during a Time interval - */ + * @brief The mean number of people migrating from one Region to another during a TimeStep. + */ struct CommutingRatio { using Type = std::vector>; - static Type get_default() + static Type get_default(Region) { return Type({{Region(0), Region(0), 0.}}); } @@ -80,11 +80,14 @@ struct CommutingRatio { } }; +/** + * @brief The ratio that regulates the infections during commuting. +*/ struct ImpactCommuters { using Type = UncertainValue; - static Type get_default() + static Type get_default(Region) { - return Type(1.0); + return Type(0.); } static std::string name() { @@ -92,8 +95,23 @@ struct ImpactCommuters { } }; -using ParametersBase = - ParameterSet; +/** + * @brief The Region%s that a person crosses when travelling from one Region to another. +*/ +struct PathIntersections { + using Type = CustomIndexArray, Region, Region>; + static Type get_default(Region size) + { + return Type({size, size}); + } + static std::string name() + { + return "PathIntersections"; + } +}; + +using ParametersBase = ParameterSet; /** * @brief Parameters of SIR model. @@ -101,9 +119,10 @@ using ParametersBase = class Parameters : public ParametersBase { public: - Parameters(Region num_regions) - : ParametersBase() //TODO: Is this fine? + Parameters(Region num_regions, AgeGroup num_agegroups) + : ParametersBase(num_regions) , m_num_regions{num_regions} + , m_num_agegroups(num_agegroups) { } @@ -112,6 +131,11 @@ class Parameters : public ParametersBase return m_num_regions; } + AgeGroup get_num_agegroups() const + { + return m_num_agegroups; + } + /** * @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]. @@ -194,6 +218,7 @@ class Parameters : public ParametersBase private: Region m_num_regions; + AgeGroup m_num_agegroups; }; } // namespace osirmobility From 2b883b6b358d5afe8e9b5360e1bcbf1cba1b8c6c Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:16:11 +0200 Subject: [PATCH 04/49] read in mobility data for new model --- cpp/examples/ode_sir_mobility.cpp | 63 ------------------------ cpp/examples/ode_sir_mobility_simple.cpp | 36 ++++++++++++-- 2 files changed, 33 insertions(+), 66 deletions(-) delete mode 100644 cpp/examples/ode_sir_mobility.cpp diff --git a/cpp/examples/ode_sir_mobility.cpp b/cpp/examples/ode_sir_mobility.cpp deleted file mode 100644 index e7510298cb..0000000000 --- a/cpp/examples/ode_sir_mobility.cpp +++ /dev/null @@ -1,63 +0,0 @@ - -#include "memilio/compartments/simulation.h" -#include "memilio/math/euler.h" -#include "memilio/utils/logging.h" -#include "memilio/epidemiology/age_group.h" -#include "ode_sir_mobility/infection_state.h" -#include "ode_sir_mobility/model.h" -#include "ode_sir_mobility/parameters.h" -#include "ode_sir_mobility/regions.h" - -int main() -{ - mio::set_log_level(mio::LogLevel::debug); - - double t0 = 0.; - double tmax = 50.; - double dt = 0.1; - - double total_population = 1061000; - - mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - - mio::osirmobility::Model model(5); - - model.populations[{mio::Index(mio::osirmobility::InfectionState::Infected, mio::osirmobility::Region(1))}] = 1000; - model.populations[{mio::Index(mio::osirmobility::InfectionState::Recovered, mio::osirmobility::Region(1))}] = 1000; - model.populations[{mio::Index(mio::osirmobility::InfectionState::Susceptible, mio::osirmobility::Region(1))}] = - total_population - - model.populations[{mio::Index(mio::osirmobility::InfectionState::Infected, mio::osirmobility::Region(1))}] - - model.populations[{mio::Index(mio::osirmobility::InfectionState::Recovered, mio::osirmobility::Region(1))}]; - model.parameters.set(2); - model.parameters.set(0.04); - model.parameters.get().get_baseline()(0, 0) = 2.7; - model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); - - auto integrator = std::make_shared(); - - model.check_constraints(); - - auto sir = simulate(t0, tmax, dt, model, integrator); - - bool print_to_terminal = true; - - if (print_to_terminal) { - std::vector vars = {"S", "I", "R"}; - printf("\n # t"); - for (size_t k = 0; k < (size_t)mio::osirmobility::InfectionState::Count; k++) { - printf(" %s", vars[k].c_str()); - } - - auto num_points = static_cast(sir.get_num_time_points()); - for (size_t i = 0; i < num_points; i++) { - printf("\n%.14f ", sir.get_time(i)); - Eigen::VectorXd res_j = sir.get_value(i); - for (size_t j = 0; j < (size_t)mio::osirmobility::InfectionState::Count; j++) { - printf(" %.14f", res_j[j]); - } - } - - Eigen::VectorXd res_j = sir.get_last_value(); - printf("\nnumber total: %f \n", res_j[0] + res_j[1] + res_j[2]); - } -} diff --git a/cpp/examples/ode_sir_mobility_simple.cpp b/cpp/examples/ode_sir_mobility_simple.cpp index 465a45ecfb..dbae487f2c 100644 --- a/cpp/examples/ode_sir_mobility_simple.cpp +++ b/cpp/examples/ode_sir_mobility_simple.cpp @@ -67,7 +67,7 @@ mio::IOResult>>> read_path_mobility(con return mio::success(arr); } -mio::IOResult run(const std::string& filename, mio::osirmobility::Model& model) +mio::IOResult preprocess(const std::string& filename, mio::osirmobility::Model& model) { BOOST_OUTCOME_TRY(mobility_paths, read_path_mobility(filename)); size_t n_regions = (size_t)model.parameters.get_num_regions(); @@ -103,6 +103,35 @@ mio::IOResult run(const std::string& filename, mio::osirmobility::Model& m return mio::success(); } +mio::IOResult set_mobility_weights(const std::string& mobility_data, const std::string& trip_chains, + mio::osirmobility::Model& model, size_t number_regions) +{ + BOOST_OUTCOME_TRY(preprocess(trip_chains, model)); + // mobility between nodes + BOOST_OUTCOME_TRY(mobility_data_commuter, + mio::read_mobility_plain(mobility_data + "mobility" + "commuter_migration_scaled.txt")); + if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || + mobility_data_commuter.cols() != Eigen::Index(number_regions)) { + return mio::failure(mio::StatusCode::InvalidValue, + "Mobility matrices do not have the correct size. You may need to run " + "transformMobilitydata.py from pycode memilio epidata package."); + } + + for (auto age = mio::AgeGroup(0); age < model.parameters.get_num_agegroups(); age++) { + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + for (size_t county_idx_j = 0; county_idx_j < number_regions; ++county_idx_j) { + //commuters + auto population_i = model.populations.get_group_total(mio::osirmobility::Region(county_idx_i)); + auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / population_i; + model.parameters.get().push_back( + {mio::osirmobility::Region(county_idx_i), mio::osirmobility::Region(county_idx_j), + commuter_coeff_ij}); + } + } + } + return mio::success(); +} + int main() { mio::set_log_level(mio::LogLevel::debug); @@ -117,7 +146,8 @@ int main() mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - const std::string& filename = ""; + const std::string& mobility_data = ""; + const std::string& trip_chain_data = ""; mio::osirmobility::Model model(number_regions, number_age_groups); @@ -151,7 +181,7 @@ int main() model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(3), 0.8}); - auto preprocess = run(filename, model); + auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); auto integrator = std::make_shared(); From d49a048931b6fe343fcc699337f6f112879f798c Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:19:09 +0200 Subject: [PATCH 05/49] only add edges if weight is big enough --- cpp/examples/ode_sir_mobility_simple.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp/examples/ode_sir_mobility_simple.cpp b/cpp/examples/ode_sir_mobility_simple.cpp index dbae487f2c..af24a94db2 100644 --- a/cpp/examples/ode_sir_mobility_simple.cpp +++ b/cpp/examples/ode_sir_mobility_simple.cpp @@ -8,7 +8,6 @@ #include "ode_sir_mobility/model.h" #include "ode_sir_mobility/parameters.h" #include "ode_sir_mobility/regions.h" -#include "ode_sir_mobility/contact_location.h" #include "memilio/io/io.h" mio::IOResult>>> read_path_mobility(const std::string& filename) @@ -123,9 +122,11 @@ mio::IOResult set_mobility_weights(const std::string& mobility_data, const //commuters auto population_i = model.populations.get_group_total(mio::osirmobility::Region(county_idx_i)); auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / population_i; - model.parameters.get().push_back( - {mio::osirmobility::Region(county_idx_i), mio::osirmobility::Region(county_idx_j), - commuter_coeff_ij}); + if (commuter_coeff_ij > 4e-5) { + model.parameters.get().push_back( + {mio::osirmobility::Region(county_idx_i), mio::osirmobility::Region(county_idx_j), + commuter_coeff_ij}); + } } } } From 56c4dd4ce12e0d4a21ff9a9c5494db64fb7df992 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Wed, 8 May 2024 12:06:40 +0200 Subject: [PATCH 06/49] correct age resolution --- cpp/examples/CMakeLists.txt | 4 - ...bility_simple.cpp => ode_sir_mobility.cpp} | 0 cpp/models/ode_sir_mobility/model.h | 85 ++++++++++--------- 3 files changed, 46 insertions(+), 43 deletions(-) rename cpp/examples/{ode_sir_mobility_simple.cpp => ode_sir_mobility.cpp} (100%) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 5f6e753cf0..4b3250b7b3 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -30,10 +30,6 @@ add_executable(ode_sir_mobility_example ode_sir_mobility.cpp) target_link_libraries(ode_sir_mobility_example PRIVATE memilio ode_sir_mobility) target_compile_options(ode_sir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -add_executable(ode_sir_mobility_example_simple ode_sir_mobility_simple.cpp) -target_link_libraries(ode_sir_mobility_example_simple PRIVATE memilio ode_sir_mobility) -target_compile_options(ode_sir_mobility_example_simple PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - add_executable(sde_sirs_example sde_sirs.cpp) target_link_libraries(sde_sirs_example PRIVATE memilio sde_sirs) target_compile_options(sde_sirs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/examples/ode_sir_mobility_simple.cpp b/cpp/examples/ode_sir_mobility.cpp similarity index 100% rename from cpp/examples/ode_sir_mobility_simple.cpp rename to cpp/examples/ode_sir_mobility.cpp diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h index a121f2ae0a..3936b6180d 100644 --- a/cpp/models/ode_sir_mobility/model.h +++ b/cpp/models/ode_sir_mobility/model.h @@ -40,55 +40,62 @@ class Model : public FlowModelparameters; auto& population = this->populations; - double coeffStoI = params.get().get_matrix_at(t)(0, 0) * - params.get() / population.get_total(); - Region n_regions = params.get_num_regions(); AgeGroup n_agegroups = params.get_num_agegroups(); - for (auto age = AgeGroup(0); age < n_agegroups; age++) { - for (auto edge : params.get()) { - auto start_region = get<0>(edge); - auto end_region = get<1>(edge); - auto strength = get(edge); - if (start_region == end_region) { - continue; - } - // s_n += h_mn/P_m * i_m - flows[get_flat_flow_index( - {start_region, age})] += - strength * pop[population.get_flat_index({end_region, age, InfectionState::Infected})]; - // s_m += h_mn/P_m * i_n - flows[get_flat_flow_index({end_region, age})] += - strength * pop[population.get_flat_index({start_region, age, InfectionState::Infected})]; - // s_n += gamma * h_nm/P_n * sum(h_km/P_k * p_nm,k * i_k) - for (auto edge_commuter : params.get()) { - auto start_region_commuter = get<0>(edge_commuter); - auto end_region_commuter = get<1>(edge_commuter); - auto strength_commuter = get(edge_commuter); - if (end_region_commuter != end_region || start_region_commuter == start_region || - ((std::find(params.get()[{start_region, end_region}].begin(), - params.get()[{start_region, end_region}].end(), - start_region_commuter)) == - params.get()[{start_region, end_region}].end())) { + for (auto age_i = AgeGroup(0); age_i < n_agegroups; age_i++) { + for (auto age_j = AgeGroup(0); age_j < n_agegroups; age_j++) { + double coeffStoI = + params.get().get_matrix_at(t)(static_cast((size_t)age_i), + static_cast((size_t)age_j)) * + params.get() / population.get_group_total(age_j); + for (auto edge : params.get()) { + auto start_region = get<0>(edge); + auto end_region = get<1>(edge); + auto strength = get(edge); + if (start_region == end_region) { continue; } - auto test = params.get()[{start_region, end_region}]; + // s_n += h_mn/P_m * i_m + flows[get_flat_flow_index( + {start_region, age_i})] += + strength * pop[population.get_flat_index({end_region, age_j, InfectionState::Infected})]; + // s_m += h_mn/P_m * i_n + flows[get_flat_flow_index( + {end_region, age_i})] += + strength * pop[population.get_flat_index({start_region, age_j, InfectionState::Infected})]; + + // s_n += gamma * h_nm/P_n * sum(h_km/P_k * p_nm,k * i_k) + for (auto edge_commuter : params.get()) { + auto start_region_commuter = get<0>(edge_commuter); + auto end_region_commuter = get<1>(edge_commuter); + auto strength_commuter = get(edge_commuter); + if (end_region_commuter != end_region || start_region_commuter == start_region || + ((std::find(params.get()[{start_region, end_region}].begin(), + params.get()[{start_region, end_region}].end(), + start_region_commuter)) == + params.get()[{start_region, end_region}].end())) { + continue; + } + flows[get_flat_flow_index( + {start_region, age_i})] += + params.get() * strength * strength_commuter * + pop[population.get_flat_index({start_region_commuter, age_j, InfectionState::Infected})]; + } + } + for (auto region = Region(0); region < n_regions; region++) { + flows[get_flat_flow_index( + {region, age_i})] += pop[population.get_flat_index({region, age_j, InfectionState::Infected})]; flows[get_flat_flow_index( - {start_region, age})] += - params.get() * strength * strength_commuter * - pop[population.get_flat_index({start_region_commuter, age, InfectionState::Infected})]; + {region, age_i})] *= + coeffStoI * y[population.get_flat_index({region, age_j, InfectionState::Susceptible})]; } } - for (auto i = Region(0); i < n_regions; i++) { - flows[get_flat_flow_index({i, age})] += - pop[population.get_flat_index({i, age, InfectionState::Infected})]; - flows[get_flat_flow_index({i, age})] *= - coeffStoI * y[population.get_flat_index({i, age, InfectionState::Susceptible})]; - flows[get_flat_flow_index({i, age})] = + for (auto region = Region(0); region < n_regions; region++) { + flows[get_flat_flow_index({region, age_i})] = (1.0 / params.get()) * - y[population.get_flat_index({i, age, InfectionState::Infected})]; + y[population.get_flat_index({region, age_i, InfectionState::Infected})]; } } } From 0efa8db01eeed34f07f411d8373b935bb68057c0 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:59:08 +0200 Subject: [PATCH 07/49] add tests --- cpp/examples/ode_sir_mobility.cpp | 56 +++-- cpp/models/ode_sir_mobility/model.h | 2 +- cpp/models/ode_sir_mobility/parameters.h | 40 ++++ cpp/tests/CMakeLists.txt | 1 + cpp/tests/test_odesirmobility.cpp | 248 +++++++++++++++++++++++ 5 files changed, 328 insertions(+), 19 deletions(-) create mode 100644 cpp/tests/test_odesirmobility.cpp diff --git a/cpp/examples/ode_sir_mobility.cpp b/cpp/examples/ode_sir_mobility.cpp index af24a94db2..afaea9860e 100644 --- a/cpp/examples/ode_sir_mobility.cpp +++ b/cpp/examples/ode_sir_mobility.cpp @@ -94,8 +94,8 @@ mio::IOResult preprocess(const std::string& filename, mio::osirmobility::M } } if (intersection_region.begin() != intersection_region.end()) { - model.parameters.get()[mio::Index( - mio::osirmobility::Region(i), mio::osirmobility::Region(j))] = intersection_region; + model.parameters.get()[{ + mio::osirmobility::Region(i), mio::osirmobility::Region(j)}] = intersection_region; } } } @@ -143,7 +143,7 @@ int main() size_t number_regions = 4; size_t number_age_groups = 1; - size_t total_population_per_region = 10; + size_t total_population_per_region = 5000; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); @@ -154,7 +154,7 @@ int main() for (size_t i = 0; i < number_regions; i++) { model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] = 1; + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] = 50; model.populations[{mio::Index( mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}] = 0; model.populations[{mio::Index( @@ -171,16 +171,34 @@ int main() model.parameters.set(1.); model.parameters.get().get_baseline()(0, 0) = 1.; model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.get().push_back( - {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.2}); model.parameters.get().push_back( - {mio::osirmobility::Region(1), mio::osirmobility::Region(2), 0.8}); + {mio::osirmobility::Region(1), mio::osirmobility::Region(2), 0.6}); model.parameters.get().push_back( {mio::osirmobility::Region(2), mio::osirmobility::Region(0), 0.5}); model.parameters.get().push_back( {mio::osirmobility::Region(0), mio::osirmobility::Region(3), 1.0}); model.parameters.get().push_back( - {mio::osirmobility::Region(1), mio::osirmobility::Region(3), 0.8}); + {mio::osirmobility::Region(1), mio::osirmobility::Region(3), 0.2}); + + model.parameters.get()[{mio::osirmobility::Region(0), + mio::osirmobility::Region(1)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(0), + mio::osirmobility::Region(3)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(1), + mio::osirmobility::Region(0)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(1), + mio::osirmobility::Region(2)}] = {0}; + model.parameters.get()[{mio::osirmobility::Region(1), + mio::osirmobility::Region(3)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(2), + mio::osirmobility::Region(1)}] = {0}; + model.parameters.get()[{mio::osirmobility::Region(3), + mio::osirmobility::Region(0)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(3), + mio::osirmobility::Region(1)}] = {2}; auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); @@ -197,19 +215,21 @@ int main() if (print_to_terminal) { 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::osirmobility::InfectionState::Count; k++) { - double dummy = 0; - - for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { - printf("\t %s[%d]: %.2f", vars[k].c_str(), (int)i, - sir.get_last_value()[k + (size_t)mio::osirmobility::InfectionState::Count * (int)i]); - dummy += sir.get_last_value()[k + (size_t)mio::osirmobility::InfectionState::Count * (int)i]; + printf("\n # t"); + for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { + for (size_t k = 0; k < (size_t)mio::osirmobility::InfectionState::Count; k++) { + printf(" %s_%d", vars[k].c_str(), (int)i); } + } - printf("\t %s_total: %.2f\n", vars[k].c_str(), dummy); + auto num_points = static_cast(sir.get_num_time_points()); + for (size_t i = 0; i < num_points; i++) { + printf("\n%.14f ", sir.get_time(i)); + for (size_t k = 0; k < (size_t)model.parameters.get_num_regions(); k++) { + for (size_t j = 0; j < (size_t)mio::osirmobility::InfectionState::Count; j++) { + printf(" %.14f", sir.get_value(i)[j + (size_t)mio::osirmobility::InfectionState::Count * (int)k]); + } + } } } } diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h index 3936b6180d..dc1b019a79 100644 --- a/cpp/models/ode_sir_mobility/model.h +++ b/cpp/models/ode_sir_mobility/model.h @@ -27,7 +27,7 @@ class Model : public FlowModel, Parameters, Flows>; public: - Model(int num_regions, int num_agegroups) + Model(int num_regions, int num_agegroups = 1) : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}, 0.), ParameterSet(num_regions, num_agegroups)) { diff --git a/cpp/models/ode_sir_mobility/parameters.h b/cpp/models/ode_sir_mobility/parameters.h index 90e95dad3a..ba66109d98 100644 --- a/cpp/models/ode_sir_mobility/parameters.h +++ b/cpp/models/ode_sir_mobility/parameters.h @@ -169,6 +169,28 @@ class Parameters : public ParametersBase this->get() = 0.0; corrected = true; } + if (this->get() < 0.0 || this->get() > 1.0) { + log_warning("Constraint check: Parameter ImpactCommuters changed from {:.4f} to {:.4f}.", + this->get(), 0.0); + this->get() = 0.0; + corrected = true; + } + for (auto& i : this->get()) { + if (std::get(i) < 0.0 || std::get(i) > 1.0) { + log_warning("Constraint check: Parameter CommutingRatio changed from {:.4f} to {:.4f}.", + std::get(i), 0.0); + std::get(i) = 0.0; + corrected = true; + } + if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) >= m_num_regions || + std::get<1>(i) >= m_num_regions) { + log_warning( + "Constraint check: Removed entry of Parameter CommutingRatio because of non-existing Regions."); + auto it = std::find(this->get().begin(), this->get().end(), i); + this->get().erase(it); + corrected = true; + } + } return corrected; } @@ -195,6 +217,24 @@ class Parameters : public ParametersBase this->get(), 0.0, 1.0); return true; } + if (this->get() < 0.0 || this->get() > 1.0) { + log_error("Constraint check: Parameter ImpactCommuters {:.4f} smaller {:.4f} or greater {:.4f}", + this->get(), 0.0, 1.0); + return true; + } + for (auto i : this->get()) { + if (std::get(i) < 0.0 || std::get(i) > 1.0) { + log_error("Constraint check: Parameter CommutingRatio entry {:.4f} smaller {:.4f} or greater {:.4f}", + std::get(i), 0.0, 1.0); + return true; + } + if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) > m_num_regions || + std::get<1>(i) > m_num_regions) { + log_error("Constraint check: Parameter CommutingRatio has an entry with start or end Region that does " + "not appear in the model."); + return true; + } + } return false; } diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 9aae63e081..672ca234de 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -9,6 +9,7 @@ set(TESTSOURCES test_populations.cpp test_odeseir.cpp test_odesir.cpp + test_odesirmobility.cpp test_numericalIntegration.cpp test_smoother.cpp test_damping.cpp diff --git a/cpp/tests/test_odesirmobility.cpp b/cpp/tests/test_odesirmobility.cpp new file mode 100644 index 0000000000..13173ec405 --- /dev/null +++ b/cpp/tests/test_odesirmobility.cpp @@ -0,0 +1,248 @@ + +#include "load_test_data.h" +#include "memilio/config.h" +#include "memilio/utils/time_series.h" +#include "ode_sir_mobility/model.h" +#include "ode_sir_mobility/infection_state.h" +#include "ode_sir_mobility/parameters.h" +#include "memilio/math/euler.h" +#include "memilio/compartments/simulation.h" +#include +#include +#include + +TEST(TestOdeSirMobility, simulateDefault) +{ + double t0 = 0; + double tmax = 1; + double dt = 0.1; + + size_t num_regions = 4; + + mio::osirmobility::Model model(num_regions); + mio::TimeSeries result = simulate(t0, tmax, dt, model); + + EXPECT_NEAR(result.get_last_time(), tmax, 1e-10); +} + +TEST(TestOdeSirMobility, compareWithPreviousRun) +{ + // initialization + double t0 = 0.; + double tmax = 3.; + double dt = 0.1; + + size_t number_regions = 4; + size_t number_age_groups = 1; + size_t total_population_per_region = 5000; + + mio::osirmobility::Model model(number_regions, number_age_groups); + + for (size_t i = 0; i < number_regions; i++) { + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] = 50; + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}] = 0; + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Susceptible)}] = + total_population_per_region - + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] - + model.populations[{mio::Index( + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}]; + } + model.parameters.set(1.0); + model.parameters.set(2); + + model.parameters.get().get_baseline()(0, 0) = 2.7; + model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + + model.parameters.set(1.); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.2}); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(2), 0.6}); + model.parameters.get().push_back( + {mio::osirmobility::Region(2), mio::osirmobility::Region(0), 0.5}); + model.parameters.get().push_back( + {mio::osirmobility::Region(0), mio::osirmobility::Region(3), 1.0}); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(3), 0.2}); + + model.parameters.get()[{mio::osirmobility::Region(0), + mio::osirmobility::Region(1)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(0), + mio::osirmobility::Region(3)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(1), + mio::osirmobility::Region(0)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(1), + mio::osirmobility::Region(2)}] = {0}; + model.parameters.get()[{mio::osirmobility::Region(1), + mio::osirmobility::Region(3)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(2), + mio::osirmobility::Region(1)}] = {0}; + model.parameters.get()[{mio::osirmobility::Region(3), + mio::osirmobility::Region(0)}] = {2}; + model.parameters.get()[{mio::osirmobility::Region(3), + mio::osirmobility::Region(1)}] = {2}; + + std::vector> refData = load_test_data_csv("ode-sir-mobility-compare.csv"); + auto integrator = std::make_shared(); + auto result = mio::simulate(t0, tmax, dt, model, integrator); + + ASSERT_EQ(refData.size(), static_cast(result.get_num_time_points())); + + for (Eigen::Index irow = 0; irow < result.get_num_time_points(); ++irow) { + double t = refData[static_cast(irow)][0]; + auto rel_tol = 1e-6; + + //test result diverges at damping because of changes, not worth fixing at the moment + if (t > 11.0 && t < 13.0) { + //strong divergence around damping + rel_tol = 0.5; + } + else if (t > 13.0) { + //minor divergence after damping + rel_tol = 1e-2; + } + mio::unused(rel_tol); + + ASSERT_NEAR(t, result.get_times()[irow], 1e-12) << "at row " << irow; + + for (size_t icol = 0; icol < 12; ++icol) { + double ref = refData[static_cast(irow)][icol + 1]; + double actual = result[irow][icol]; + + double tol = rel_tol * ref; + ASSERT_NEAR(ref, actual, tol) << "at row " << irow; + } + } +} + +TEST(TestOdeSirMobility, checkPopulationConservation) +{ + // initialization + double t0 = 0.; + double tmax = 50.; + double dt = 0.1002004008016032; + + double population_per_region = 1061000; + mio::osirmobility::Region num_regions = 4; + + mio::osirmobility::Model model((size_t)num_regions); + + for (auto region = mio::osirmobility::Region(0); region < num_regions; ++region) { + model.populations[{region, mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected}] = 1000; + model.populations[{region, mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered}] = 1000; + model.populations[{region, mio::AgeGroup(0), mio::osirmobility::InfectionState::Susceptible}] = + population_per_region - + model.populations[{region, mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected}] - + model.populations[{region, mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered}]; + } + model.parameters.set(2); + model.parameters.set(0.04); + model.parameters.set(1.); + model.parameters.get().get_baseline()(0, 0) = 1.; + model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(2), 0.8}); + model.parameters.get().push_back( + {mio::osirmobility::Region(2), mio::osirmobility::Region(0), 0.5}); + model.parameters.get().push_back( + {mio::osirmobility::Region(0), mio::osirmobility::Region(3), 1.0}); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(3), 0.8}); + + auto result = mio::simulate(t0, tmax, dt, model); + double num_persons = 0.0; + for (auto i = 0; i < result.get_last_value().size(); ++i) { + num_persons += result.get_last_value()[i]; + } + EXPECT_NEAR(num_persons, population_per_region * (size_t)num_regions, 1e-8); +} + +TEST(TestOdeSirMobility, check_constraints_parameters) +{ + mio::osirmobility::Region num_regions = 2; + + mio::osirmobility::Model model((size_t)num_regions); + model.parameters.set(6); + model.parameters.set(0.04); + model.parameters.set(1.); + model.parameters.get().get_baseline()(0, 0) = 10.; + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); + + // model.check_constraints() combines the functions from population and parameters. + // We only want to test the functions for the parameters defined in parameters.h + ASSERT_EQ(model.parameters.check_constraints(), 0); + + mio::set_log_level(mio::LogLevel::off); + + model.parameters.set(0); + ASSERT_EQ(model.parameters.check_constraints(), 1); + + model.parameters.set(6); + model.parameters.set(10.); + ASSERT_EQ(model.parameters.check_constraints(), 1); + + model.parameters.set(0.04); + model.parameters.set(10.); + ASSERT_EQ(model.parameters.check_constraints(), 1); + + model.parameters.set(1.); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 10.5}); + ASSERT_EQ(model.parameters.check_constraints(), 1); + + model.parameters.get().pop_back(); + model.parameters.get().push_back( + {mio::osirmobility::Region(2), mio::osirmobility::Region(0), 0.5}); + mio::set_log_level(mio::LogLevel::warn); +} + +TEST(TestOdeSirMobility, apply_constraints_parameters) +{ + const double tol_times = 1e-1; + mio::osirmobility::Region num_regions = 2; + + mio::osirmobility::Model model((size_t)num_regions); + model.parameters.set(6); + model.parameters.set(0.04); + model.parameters.set(1.); + model.parameters.get().get_baseline()(0, 0) = 10.; + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); + + EXPECT_EQ(model.parameters.apply_constraints(), 0); + + mio::set_log_level(mio::LogLevel::off); + + model.parameters.set(-2.5); + EXPECT_EQ(model.parameters.apply_constraints(), 1); + EXPECT_EQ(model.parameters.get(), tol_times); + + model.parameters.set(10.); + EXPECT_EQ(model.parameters.apply_constraints(), 1); + EXPECT_NEAR(model.parameters.get(), 0.0, 1e-14); + + model.parameters.set(0.04); + model.parameters.set(10.); + EXPECT_EQ(model.parameters.apply_constraints(), 1); + EXPECT_NEAR(model.parameters.get(), 0.0, 1e-14); + + model.parameters.set(1.); + model.parameters.get().push_back( + {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 10.5}); + EXPECT_EQ(model.parameters.apply_constraints(), 1); + EXPECT_NEAR(std::get(model.parameters.get()[2]), 0.0, 1e-14); + + model.parameters.get().pop_back(); + model.parameters.get().push_back( + {mio::osirmobility::Region(2), mio::osirmobility::Region(0), 0.5}); + EXPECT_EQ(model.parameters.apply_constraints(), 1); + // EXPECT_EQ(model.parameters.get().size(), 2); // 1 by default + 1 added + mio::set_log_level(mio::LogLevel::warn); +} \ No newline at end of file From 05e3717a41cc6cc555f94f93cb9bdc559fa3b6b0 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:42:24 +0200 Subject: [PATCH 08/49] add seir version of mobility model --- cpp/CMakeLists.txt | 1 + cpp/examples/CMakeLists.txt | 4 + cpp/examples/ode_seir_mobility.cpp | 238 ++++++++++++++ cpp/models/ode_seir_mobility/CMakeLists.txt | 13 + .../ode_seir_mobility/infection_state.h | 26 ++ cpp/models/ode_seir_mobility/model.cpp | 10 + cpp/models/ode_seir_mobility/model.h | 112 +++++++ cpp/models/ode_seir_mobility/parameters.h | 297 ++++++++++++++++++ cpp/models/ode_seir_mobility/regions.h | 26 ++ 9 files changed, 727 insertions(+) create mode 100644 cpp/examples/ode_seir_mobility.cpp create mode 100644 cpp/models/ode_seir_mobility/CMakeLists.txt create mode 100644 cpp/models/ode_seir_mobility/infection_state.h create mode 100644 cpp/models/ode_seir_mobility/model.cpp create mode 100644 cpp/models/ode_seir_mobility/model.h create mode 100644 cpp/models/ode_seir_mobility/parameters.h create mode 100644 cpp/models/ode_seir_mobility/regions.h diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index fec9d5b711..fd501cdd42 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -125,6 +125,7 @@ if(MEMILIO_BUILD_MODELS) add_subdirectory(models/ode_seir) add_subdirectory(models/ode_sir) add_subdirectory(models/ode_sir_mobility) + add_subdirectory(models/ode_seir_mobility) add_subdirectory(models/sde_sir) add_subdirectory(models/sde_sirs) endif() diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 4b3250b7b3..1e61f1cbce 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -30,6 +30,10 @@ add_executable(ode_sir_mobility_example ode_sir_mobility.cpp) target_link_libraries(ode_sir_mobility_example PRIVATE memilio ode_sir_mobility) target_compile_options(ode_sir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(ode_seir_mobility_example ode_seir_mobility.cpp) +target_link_libraries(ode_seir_mobility_example PRIVATE memilio ode_seir_mobility) +target_compile_options(ode_seir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(sde_sirs_example sde_sirs.cpp) target_link_libraries(sde_sirs_example PRIVATE memilio sde_sirs) target_compile_options(sde_sirs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp new file mode 100644 index 0000000000..75e5ce1141 --- /dev/null +++ b/cpp/examples/ode_seir_mobility.cpp @@ -0,0 +1,238 @@ + +#include "memilio/compartments/simulation.h" +#include "memilio/math/euler.h" +#include "memilio/utils/logging.h" +#include "memilio/utils/custom_index_array.h" +#include "memilio/io/mobility_io.h" +#include "models/ode_seir_mobility/infection_state.h" +#include "models/ode_seir_mobility/model.h" +#include "models/ode_seir_mobility/parameters.h" +#include "models/ode_seir_mobility/regions.h" +#include "memilio/io/io.h" + +mio::IOResult>>> read_path_mobility(const std::string& filename) +{ + BOOST_OUTCOME_TRY(num_lines, mio::count_lines(filename)); + + if (num_lines == 0) { + std::vector>> arr(0, std::vector>(0)); + return mio::success(arr); + } + + std::fstream file; + file.open(filename, std::ios::in); + if (!file.is_open()) { + return failure(mio::StatusCode::FileNotFound, filename); + } + + std::vector>> arr(std::sqrt(num_lines), + std::vector>(std::sqrt(num_lines))); + + try { + std::string tp; + while (getline(file, tp)) { + auto line = mio::split(tp, ' '); + int indx_x = std::stoi(line[0]); + int indx_y = std::stoi(line[1]); + if (indx_x != indx_y) { + auto path = std::accumulate(line.begin() + 2, line.end(), std::string("")); + + // string -> vector of integers + std::vector path_vec; + + // Remove the square brackets and \r + path = path.substr(1, path.size() - 3); + std::stringstream ss(path); + std::string token; + + // get numbers and save them in path_vec + while (std::getline(ss, token, ',')) { + path_vec.push_back(std::stoi(token)); + } + + // Sorted by end location + for (int number : path_vec) { + if (number != indx_x && number != indx_y) { + arr[indx_x][indx_y].push_back(number); + } + } + } + } + } + catch (std::runtime_error& ex) { + return failure(mio::StatusCode::InvalidFileFormat, filename + ": " + ex.what()); + } + + return mio::success(arr); +} + +mio::IOResult preprocess(const std::string& filename, mio::oseirmobility::Model& model) +{ + BOOST_OUTCOME_TRY(mobility_paths, read_path_mobility(filename)); + size_t n_regions = (size_t)model.parameters.get_num_regions(); + for (size_t i = 0; i < n_regions; i++) { + for (size_t j = 0; j < n_regions; j++) { + if (j == i) { + continue; + } + std::sort(mobility_paths[i][j].begin(), mobility_paths[i][j].end()); + std::vector intersection_int; + std::vector intersection_region(intersection_int.size(), + mio::oseirmobility::Region(0)); + for (size_t k = 0; k < n_regions; k++) { + if (k == i || k == j) { + continue; + } + std::sort(mobility_paths[k][j].begin(), mobility_paths[k][j].end()); + std::set_intersection(mobility_paths[i][j].begin(), mobility_paths[i][j].end(), + mobility_paths[k][j].begin(), mobility_paths[k][j].end(), + std::back_inserter(intersection_int)); + + if (intersection_int.begin() != intersection_int.end()) { + intersection_region.push_back(mio::oseirmobility::Region(k)); + intersection_int.pop_back(); + } + } + if (intersection_region.begin() != intersection_region.end()) { + model.parameters.get()[mio::Index( + mio::oseirmobility::Region(i), mio::oseirmobility::Region(j))] = intersection_region; + } + } + } + return mio::success(); +} + +mio::IOResult set_mobility_weights(const std::string& mobility_data, const std::string& trip_chains, + mio::oseirmobility::Model& model, size_t number_regions) +{ + BOOST_OUTCOME_TRY(preprocess(trip_chains, model)); + // mobility between nodes + BOOST_OUTCOME_TRY(mobility_data_commuter, + mio::read_mobility_plain(mobility_data + "mobility" + "commuter_migration_scaled.txt")); + if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || + mobility_data_commuter.cols() != Eigen::Index(number_regions)) { + return mio::failure(mio::StatusCode::InvalidValue, + "Mobility matrices do not have the correct size. You may need to run " + "transformMobilitydata.py from pycode memilio epidata package."); + } + + for (auto age = mio::AgeGroup(0); age < model.parameters.get_num_agegroups(); age++) { + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + for (size_t county_idx_j = 0; county_idx_j < number_regions; ++county_idx_j) { + //commuters + auto population_i = model.populations.get_group_total(mio::oseirmobility::Region(county_idx_i)); + auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / population_i; + if (commuter_coeff_ij > 4e-5) { + model.parameters.get().push_back( + {mio::oseirmobility::Region(county_idx_i), mio::oseirmobility::Region(county_idx_j), + commuter_coeff_ij}); + } + } + } + } + return mio::success(); +} + +int main() +{ + mio::set_log_level(mio::LogLevel::debug); + + double t0 = 0.; + double tmax = 50.; + double dt = 1; + + size_t number_regions = 4; + size_t number_age_groups = 1; + size_t total_population_per_region = 10; + + mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); + + const std::string& mobility_data = ""; + const std::string& trip_chain_data = ""; + + mio::oseirmobility::Model model(number_regions, number_age_groups); + + for (size_t i = 0; i < number_regions; i++) { + model.populations[{mio::Index( + mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Infected)}] = 1; + model.populations[{mio::Index( + mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Recovered)}] = 0; + model.populations[{mio::Index( + mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Susceptible)}] = + total_population_per_region - + model + .populations[{mio::Index( + mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Infected)}] - + model + .populations[{mio::Index( + mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Recovered)}]; + } + + model.parameters.set(1); + model.parameters.set(2); + model.parameters.set(0.04); + model.parameters.set(1.); + model.parameters.get().get_baseline()(0, 0) = 1.; + model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + + model.parameters.get().push_back( + {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.2}); + model.parameters.get().push_back( + {mio::oseirmobility::Region(1), mio::oseirmobility::Region(2), 0.6}); + model.parameters.get().push_back( + {mio::oseirmobility::Region(2), mio::oseirmobility::Region(0), 0.5}); + model.parameters.get().push_back( + {mio::oseirmobility::Region(0), mio::oseirmobility::Region(3), 1.0}); + model.parameters.get().push_back( + {mio::oseirmobility::Region(1), mio::oseirmobility::Region(3), 0.2}); + + model.parameters.get()[{mio::oseirmobility::Region(0), + mio::oseirmobility::Region(1)}] = {2}; + model.parameters.get()[{mio::oseirmobility::Region(0), + mio::oseirmobility::Region(3)}] = {2}; + model.parameters.get()[{mio::oseirmobility::Region(1), + mio::oseirmobility::Region(0)}] = {2}; + model.parameters.get()[{mio::oseirmobility::Region(1), + mio::oseirmobility::Region(2)}] = {0}; + model.parameters.get()[{mio::oseirmobility::Region(1), + mio::oseirmobility::Region(3)}] = {2}; + model.parameters.get()[{mio::oseirmobility::Region(2), + mio::oseirmobility::Region(1)}] = {0}; + model.parameters.get()[{mio::oseirmobility::Region(3), + mio::oseirmobility::Region(0)}] = {2}; + model.parameters.get()[{mio::oseirmobility::Region(3), + mio::oseirmobility::Region(1)}] = {2}; + + // auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); + + auto integrator = std::make_shared(); + + model.check_constraints(); + + auto sir = simulate(t0, tmax, dt, model, integrator); + + bool print_to_terminal = true; + + sir.print_table(); + + if (print_to_terminal) { + + std::vector vars = {"S", "E", "I", "R"}; + printf("\n # t"); + for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { + for (size_t k = 0; k < (size_t)mio::oseirmobility::InfectionState::Count; k++) { + printf(" %s_%d", vars[k].c_str(), (int)i); + } + } + + auto num_points = static_cast(sir.get_num_time_points()); + for (size_t i = 0; i < num_points; i++) { + printf("\n%.14f ", sir.get_time(i)); + for (size_t k = 0; k < (size_t)model.parameters.get_num_regions(); k++) { + for (size_t j = 0; j < (size_t)mio::oseirmobility::InfectionState::Count; j++) { + printf(" %.14f", sir.get_value(i)[j + (size_t)mio::oseirmobility::InfectionState::Count * (int)k]); + } + } + } + } +} diff --git a/cpp/models/ode_seir_mobility/CMakeLists.txt b/cpp/models/ode_seir_mobility/CMakeLists.txt new file mode 100644 index 0000000000..ecf6e3112b --- /dev/null +++ b/cpp/models/ode_seir_mobility/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(ode_seir_mobility + infection_state.h + model.h + model.cpp + parameters.h + regions.h +) +target_link_libraries(ode_seir_mobility PUBLIC memilio) +target_include_directories(ode_seir_mobility PUBLIC + $ + $ +) +target_compile_options(ode_seir_mobility PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/models/ode_seir_mobility/infection_state.h b/cpp/models/ode_seir_mobility/infection_state.h new file mode 100644 index 0000000000..f4207708b3 --- /dev/null +++ b/cpp/models/ode_seir_mobility/infection_state.h @@ -0,0 +1,26 @@ + +#ifndef ODESEIRMOBILITY_INFECTIONSTATE_H +#define ODESEIRMOBILITY_INFECTIONSTATE_H + +namespace mio +{ +namespace oseirmobility +{ + +/** + * @brief The InfectionState enum describes the possible + * categories for the infectious state of persons + */ +enum class InfectionState +{ + Susceptible, + Exposed, + Infected, + Recovered, + Count +}; + +} // namespace oseirmobility +} // namespace mio + +#endif // ODESEIR_INFECTIONSTATE_H diff --git a/cpp/models/ode_seir_mobility/model.cpp b/cpp/models/ode_seir_mobility/model.cpp new file mode 100644 index 0000000000..75494e52d6 --- /dev/null +++ b/cpp/models/ode_seir_mobility/model.cpp @@ -0,0 +1,10 @@ + +#include "ode_seir_mobility/model.h" + +namespace mio +{ +namespace oseirmobility +{ + +} // namespace oseirmobility +} // namespace mio diff --git a/cpp/models/ode_seir_mobility/model.h b/cpp/models/ode_seir_mobility/model.h new file mode 100644 index 0000000000..647a01dac1 --- /dev/null +++ b/cpp/models/ode_seir_mobility/model.h @@ -0,0 +1,112 @@ + +#ifndef ODESEIRMOBILITY_MODEL_H +#define ODESEIRMOBILITY_MODEL_H + +#include "memilio/compartments/flow_model.h" +#include "memilio/epidemiology/populations.h" +#include "models/ode_seir_mobility/infection_state.h" +#include "models/ode_seir_mobility/parameters.h" +#include "models/ode_seir_mobility/regions.h" +#include "memilio/epidemiology/age_group.h" + +namespace mio +{ +namespace oseirmobility +{ + +/******************** + * define the model * + ********************/ + +using Flows = TypeList, + Flow, + Flow>; + +class Model : public FlowModel, Parameters, Flows> +{ + + using Base = FlowModel, Parameters, Flows>; + +public: + Model(int num_regions, int num_agegroups = 1) + : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}, 0.), + ParameterSet(num_regions, num_agegroups)) + { + } + // Einmal über den Vektor und später nochmal über die Regions + + void get_flows(Eigen::Ref pop, Eigen::Ref y, double t, + Eigen::Ref flows) const override + { + auto& params = this->parameters; + auto& population = this->populations; + + Region n_regions = params.get_num_regions(); + AgeGroup n_agegroups = params.get_num_agegroups(); + + for (auto age_i = AgeGroup(0); age_i < n_agegroups; age_i++) { + for (auto age_j = AgeGroup(0); age_j < n_agegroups; age_j++) { + double coeffStoI = + params.get().get_matrix_at(t)(static_cast((size_t)age_i), + static_cast((size_t)age_j)) * + params.get() / population.get_group_total(age_j); + for (auto edge : params.get()) { + auto start_region = get<0>(edge); + auto end_region = get<1>(edge); + auto strength = get(edge); + if (start_region == end_region) { + continue; + } + // s_n += h_mn/P_m * i_m + flows[get_flat_flow_index( + {start_region, age_i})] += + strength * pop[population.get_flat_index({end_region, age_j, InfectionState::Infected})]; + // s_m += h_mn/P_m * i_n + flows[get_flat_flow_index( + {end_region, age_i})] += + strength * pop[population.get_flat_index({start_region, age_j, InfectionState::Infected})]; + + // s_n += gamma * h_nm/P_n * sum(h_km/P_k * p_nm,k * i_k) + for (auto edge_commuter : params.get()) { + auto start_region_commuter = get<0>(edge_commuter); + auto end_region_commuter = get<1>(edge_commuter); + auto strength_commuter = get(edge_commuter); + if (end_region_commuter != end_region || start_region_commuter == start_region || + ((std::find(params.get()[{start_region, end_region}].begin(), + params.get()[{start_region, end_region}].end(), + start_region_commuter)) == + params.get()[{start_region, end_region}].end())) { + continue; + } + flows[get_flat_flow_index( + {start_region, age_i})] += + params.get() * strength * strength_commuter * + pop[population.get_flat_index({start_region_commuter, age_j, InfectionState::Infected})]; + } + } + for (auto region = Region(0); region < n_regions; region++) { + flows[get_flat_flow_index({region, age_i})] += + pop[population.get_flat_index({region, age_j, InfectionState::Infected})]; + flows[get_flat_flow_index({region, age_i})] *= + coeffStoI * y[population.get_flat_index({region, age_j, InfectionState::Susceptible})]; + } + } + + for (auto region = Region(0); region < n_regions; region++) { + flows[get_flat_flow_index({region, age_i})] = + (1.0 / params.get()) * + y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; + flows[get_flat_flow_index({region, age_i})] = + (1.0 / params.get()) * + y[population.get_flat_index({region, age_i, InfectionState::Infected})]; + // flows[get_flat_flow_index({region, age_i})] = 0.; + // flows[get_flat_flow_index({region, age_i})] = 0.; + } + } + } +}; + +} // namespace oseirmobility +} // namespace mio + +#endif // ODESEIRMOBILITY_MODEL_H diff --git a/cpp/models/ode_seir_mobility/parameters.h b/cpp/models/ode_seir_mobility/parameters.h new file mode 100644 index 0000000000..fa02511316 --- /dev/null +++ b/cpp/models/ode_seir_mobility/parameters.h @@ -0,0 +1,297 @@ + +#ifndef SEIRMOBILITY_PARAMETERS_H +#define SEIRMOBILITY_PARAMETERS_H + +#include "memilio/utils/uncertain_value.h" +#include "memilio/epidemiology/contact_matrix.h" +#include "memilio/epidemiology/age_group.h" +#include "memilio/utils/parameter_set.h" +#include "memilio/utils/custom_index_array.h" +#include "models/ode_seir_mobility/regions.h" + +#include + +namespace mio +{ +namespace oseirmobility +{ + +/**************************************************** + * Define Parameters of the SEIR model with mobility * + ****************************************************/ + +/** + * @brief Probability of getting infected from a contact. + */ +struct TransmissionProbabilityOnContact { + using Type = UncertainValue; + static Type get_default(Region) + { + return Type(1.0); + } + static std::string name() + { + return "TransmissionProbabilityOnContact"; + } +}; + +/** + * @brief the latent time in day unit + */ +struct TimeExposed { + using Type = UncertainValue; + static Type get_default() + { + return Type(5.2); + } + static std::string name() + { + return "TimeExposed"; + } +}; + +/** + * @brief The infectious time in day unit. + */ +struct TimeInfected { + using Type = UncertainValue; + static Type get_default(Region) + { + return Type(6.0); + } + static std::string name() + { + return "TimeInfected"; + } +}; + +/** + * @brief The contact patterns within the society are modelled using a ContactMatrix. + */ +struct ContactPatterns { + using Type = ContactMatrix; + static Type get_default(Region) + { + return Type{1}; + } + static std::string name() + { + return "ContactPatterns"; + } +}; + +/** + * @brief The mean number of people migrating from one Region to another during a TimeStep. + */ +struct CommutingRatio { + using Type = std::vector>; + static Type get_default(Region) + { + return Type({{Region(0), Region(0), 0.}}); + } + static std::string name() + { + return "CommutingRatio"; + } +}; + +/** + * @brief The ratio that regulates the infections during commuting. +*/ +struct ImpactCommuters { + using Type = UncertainValue; + static Type get_default(Region) + { + return Type(0.); + } + static std::string name() + { + return "ImpactCommuters"; + } +}; + +/** + * @brief The Region%s that a person crosses when travelling from one Region to another. +*/ +struct PathIntersections { + using Type = CustomIndexArray, Region, Region>; + static Type get_default(Region size) + { + return Type({size, size}); + } + static std::string name() + { + return "PathIntersections"; + } +}; + +using ParametersBase = ParameterSet; + +/** + * @brief Parameters of SEIR model. + */ +class Parameters : public ParametersBase +{ +public: + Parameters(Region num_regions, AgeGroup num_agegroups) + : ParametersBase(num_regions) + , m_num_regions{num_regions} + , m_num_agegroups(num_agegroups) + { + } + + Region get_num_regions() const + { + return m_num_regions; + } + + AgeGroup get_num_agegroups() const + { + return m_num_agegroups; + } + + /** + * @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]. + * + * Attention: This function should be used with care. It is necessary for some test problems to run through quickly, + * but in a manual execution of an example, check_constraints() may be preferred. Note that the apply_constraints() + * function can and will not set Parameters to meaningful values in an epidemiological or virological context, + * as all models are designed to be transferable to multiple diseases. Consequently, only acceptable + * (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. + */ + bool apply_constraints() + { + double tol_times = 1e-1; + + int corrected = false; + if (this->get() < tol_times) { + log_warning("Constraint check: Parameter TimeExposed 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; + } + if (this->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; + } + if (this->get() < 0.0 || + this->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; + } + if (this->get() < 0.0 || this->get() > 1.0) { + log_warning("Constraint check: Parameter ImpactCommuters changed from {:.4f} to {:.4f}.", + this->get(), 0.0); + this->get() = 0.0; + corrected = true; + } + for (auto& i : this->get()) { + if (std::get(i) < 0.0 || std::get(i) > 1.0) { + log_warning("Constraint check: Parameter CommutingRatio changed from {:.4f} to {:.4f}.", + std::get(i), 0.0); + std::get(i) = 0.0; + corrected = true; + } + if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) >= m_num_regions || + std::get<1>(i) >= m_num_regions) { + log_warning( + "Constraint check: Removed entry of Parameter CommutingRatio because of non-existing Regions."); + auto it = std::find(this->get().begin(), this->get().end(), i); + this->get().erase(it); + corrected = true; + } + } + return corrected; + } + + /** + * @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. + */ + bool check_constraints() const + { + double tol_times = 1e-1; + + if (this->get() < tol_times) { + log_error("Constraint check: Parameter TimeExposed {:.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); + return true; + } + if (this->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); + return true; + } + if (this->get() < 0.0 || + this->get() > 1.0) { + log_error( + "Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or greater {:.4f}", + this->get(), 0.0, 1.0); + return true; + } + if (this->get() < 0.0 || this->get() > 1.0) { + log_error("Constraint check: Parameter ImpactCommuters {:.4f} smaller {:.4f} or greater {:.4f}", + this->get(), 0.0, 1.0); + return true; + } + for (auto i : this->get()) { + if (std::get(i) < 0.0 || std::get(i) > 1.0) { + log_error("Constraint check: Parameter CommutingRatio entry {:.4f} smaller {:.4f} or greater {:.4f}", + std::get(i), 0.0, 1.0); + return true; + } + if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) > m_num_regions || + std::get<1>(i) > m_num_regions) { + log_error("Constraint check: Parameter CommutingRatio has an entry with start or end Region that does " + "not appear in the model."); + return true; + } + } + return false; + } + +private: + // Parameters(ParametersBase&& base) + // : ParametersBase(std::move(base)) //TODO: Adjust + // { + // } + +public: + /** + * deserialize an object of this class. + * @see mio::deserialize + */ + template + static IOResult deserialize(IOContext& io) + { + BOOST_OUTCOME_TRY(base, ParametersBase::deserialize(io)); + return success(Parameters(std::move(base))); + } + +private: + Region m_num_regions; + AgeGroup m_num_agegroups; +}; + +} // namespace oseirmobility +} // namespace mio + +#endif // SEIR_PARAMETERS_H diff --git a/cpp/models/ode_seir_mobility/regions.h b/cpp/models/ode_seir_mobility/regions.h new file mode 100644 index 0000000000..4a8352b11b --- /dev/null +++ b/cpp/models/ode_seir_mobility/regions.h @@ -0,0 +1,26 @@ + +#ifndef ODESEIRMOBILITY_REGIONS_H +#define ODESEIRMOBILITY_REGIONS_H + +#include "memilio/utils/index.h" + +namespace mio +{ +namespace oseirmobility +{ + +/** + * @brief The AgeGroup struct is used as a dynamically + * sized tag for all age dependent categories + */ +struct Region : public Index { + Region(size_t val) + : Index(val) + { + } +}; + +} // namespace oseirmobility +} // namespace mio + +#endif From f475e3ca52a3ed3f74f6bdce9301138c253e06b4 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:43:15 +0200 Subject: [PATCH 09/49] small adjustments sir version --- cpp/examples/ode_sir_mobility.cpp | 8 ++++---- cpp/models/ode_sir_mobility/CMakeLists.txt | 1 + cpp/models/ode_sir_mobility/model.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/examples/ode_sir_mobility.cpp b/cpp/examples/ode_sir_mobility.cpp index afaea9860e..e8e986de7e 100644 --- a/cpp/examples/ode_sir_mobility.cpp +++ b/cpp/examples/ode_sir_mobility.cpp @@ -141,9 +141,9 @@ int main() double tmax = 50.; double dt = 1; - size_t number_regions = 4; + size_t number_regions = 1; size_t number_age_groups = 1; - size_t total_population_per_region = 5000; + size_t total_population_per_region = 10; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); @@ -154,7 +154,7 @@ int main() for (size_t i = 0; i < number_regions; i++) { model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] = 50; + mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] = 1; model.populations[{mio::Index( mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}] = 0; model.populations[{mio::Index( @@ -200,7 +200,7 @@ int main() model.parameters.get()[{mio::osirmobility::Region(3), mio::osirmobility::Region(1)}] = {2}; - auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); + // auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); auto integrator = std::make_shared(); diff --git a/cpp/models/ode_sir_mobility/CMakeLists.txt b/cpp/models/ode_sir_mobility/CMakeLists.txt index 55605f5c94..3a2f54adeb 100644 --- a/cpp/models/ode_sir_mobility/CMakeLists.txt +++ b/cpp/models/ode_sir_mobility/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(ode_sir_mobility model.h model.cpp parameters.h + regions.h ) target_link_libraries(ode_sir_mobility PUBLIC memilio) target_include_directories(ode_sir_mobility PUBLIC diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h index dc1b019a79..3936b6180d 100644 --- a/cpp/models/ode_sir_mobility/model.h +++ b/cpp/models/ode_sir_mobility/model.h @@ -27,7 +27,7 @@ class Model : public FlowModel, Parameters, Flows>; public: - Model(int num_regions, int num_agegroups = 1) + Model(int num_regions, int num_agegroups) : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}, 0.), ParameterSet(num_regions, num_agegroups)) { From f19bcf625e39569aec9ae6cb8f43b7942b688315 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:57:51 +0200 Subject: [PATCH 10/49] fixes after merge --- cpp/examples/ode_seir_mobility.cpp | 72 ++++----- cpp/examples/ode_sir_mobility.cpp | 66 ++++---- cpp/models/ode_seir_mobility/model.h | 87 ++++++----- cpp/models/ode_seir_mobility/parameters.h | 178 ++++++++++++---------- cpp/models/ode_sir_mobility/model.h | 74 ++++----- cpp/models/ode_sir_mobility/parameters.h | 136 +++++++++-------- 6 files changed, 334 insertions(+), 279 deletions(-) diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp index 75e5ce1141..c6b9785639 100644 --- a/cpp/examples/ode_seir_mobility.cpp +++ b/cpp/examples/ode_seir_mobility.cpp @@ -12,7 +12,7 @@ mio::IOResult>>> read_path_mobility(const std::string& filename) { - BOOST_OUTCOME_TRY(num_lines, mio::count_lines(filename)); + BOOST_OUTCOME_TRY(auto&& num_lines, mio::count_lines(filename)); if (num_lines == 0) { std::vector>> arr(0, std::vector>(0)); @@ -66,9 +66,10 @@ mio::IOResult>>> read_path_mobility(con return mio::success(arr); } -mio::IOResult preprocess(const std::string& filename, mio::oseirmobility::Model& model) +template +mio::IOResult preprocess(const std::string& filename, mio::oseirmobility::Model& model) { - BOOST_OUTCOME_TRY(mobility_paths, read_path_mobility(filename)); + BOOST_OUTCOME_TRY(auto&& mobility_paths, read_path_mobility(filename)); size_t n_regions = (size_t)model.parameters.get_num_regions(); for (size_t i = 0; i < n_regions; i++) { for (size_t j = 0; j < n_regions; j++) { @@ -94,20 +95,21 @@ mio::IOResult preprocess(const std::string& filename, mio::oseirmobility:: } } if (intersection_region.begin() != intersection_region.end()) { - model.parameters.get()[mio::Index( - mio::oseirmobility::Region(i), mio::oseirmobility::Region(j))] = intersection_region; + model.parameters.template get()[{ + mio::oseirmobility::Region(i), mio::oseirmobility::Region(j)}] = intersection_region; } } } return mio::success(); } +template mio::IOResult set_mobility_weights(const std::string& mobility_data, const std::string& trip_chains, - mio::oseirmobility::Model& model, size_t number_regions) + mio::oseirmobility::Model& model, size_t number_regions) { BOOST_OUTCOME_TRY(preprocess(trip_chains, model)); // mobility between nodes - BOOST_OUTCOME_TRY(mobility_data_commuter, + BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, mio::read_mobility_plain(mobility_data + "mobility" + "commuter_migration_scaled.txt")); if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || mobility_data_commuter.cols() != Eigen::Index(number_regions)) { @@ -123,7 +125,7 @@ mio::IOResult set_mobility_weights(const std::string& mobility_data, const auto population_i = model.populations.get_group_total(mio::oseirmobility::Region(county_idx_i)); auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / population_i; if (commuter_coeff_ij > 4e-5) { - model.parameters.get().push_back( + model.parameters.template get().push_back( {mio::oseirmobility::Region(county_idx_i), mio::oseirmobility::Region(county_idx_j), commuter_coeff_ij}); } @@ -137,43 +139,43 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0.; - double tmax = 50.; - double dt = 1; + ScalarType t0 = 0.; + ScalarType tmax = 50.; + ScalarType dt = 1; - size_t number_regions = 4; - size_t number_age_groups = 1; - size_t total_population_per_region = 10; + ScalarType number_regions = 4; + ScalarType number_age_groups = 1; + ScalarType total_population_per_region = 10; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); const std::string& mobility_data = ""; const std::string& trip_chain_data = ""; - mio::oseirmobility::Model model(number_regions, number_age_groups); + mio::oseirmobility::Model model(number_regions, number_age_groups); for (size_t i = 0; i < number_regions; i++) { - model.populations[{mio::Index( - mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Infected)}] = 1; - model.populations[{mio::Index( - mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Recovered)}] = 0; - model.populations[{mio::Index( - mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Susceptible)}] = + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Infected}] = 1; + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Recovered}] = 0; + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Susceptible}] = total_population_per_region - - model - .populations[{mio::Index( - mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Infected)}] - - model - .populations[{mio::Index( - mio::oseirmobility::Region(i), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Recovered)}]; + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Infected}] - + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Recovered}]; } - model.parameters.set(1); - model.parameters.set(2); - model.parameters.set(0.04); - model.parameters.set(1.); - model.parameters.get().get_baseline()(0, 0) = 1.; - model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.set>(1); + model.parameters.set>(2); + model.parameters.set>(0.04); + model.parameters.set>(1.); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(1.0); + contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); model.parameters.get().push_back( {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.2}); @@ -205,7 +207,8 @@ int main() // auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); - auto integrator = std::make_shared(); + std::shared_ptr> integrator = + std::make_shared>(); model.check_constraints(); @@ -234,5 +237,6 @@ int main() } } } + printf("\n"); } } diff --git a/cpp/examples/ode_sir_mobility.cpp b/cpp/examples/ode_sir_mobility.cpp index e8e986de7e..46dca6739e 100644 --- a/cpp/examples/ode_sir_mobility.cpp +++ b/cpp/examples/ode_sir_mobility.cpp @@ -12,7 +12,7 @@ mio::IOResult>>> read_path_mobility(const std::string& filename) { - BOOST_OUTCOME_TRY(num_lines, mio::count_lines(filename)); + BOOST_OUTCOME_TRY(auto&& num_lines, mio::count_lines(filename)); if (num_lines == 0) { std::vector>> arr(0, std::vector>(0)); @@ -66,9 +66,10 @@ mio::IOResult>>> read_path_mobility(con return mio::success(arr); } -mio::IOResult preprocess(const std::string& filename, mio::osirmobility::Model& model) +template +mio::IOResult preprocess(const std::string& filename, mio::osirmobility::Model& model) { - BOOST_OUTCOME_TRY(mobility_paths, read_path_mobility(filename)); + BOOST_OUTCOME_TRY(auto&& mobility_paths, read_path_mobility(filename)); size_t n_regions = (size_t)model.parameters.get_num_regions(); for (size_t i = 0; i < n_regions; i++) { for (size_t j = 0; j < n_regions; j++) { @@ -94,7 +95,7 @@ mio::IOResult preprocess(const std::string& filename, mio::osirmobility::M } } if (intersection_region.begin() != intersection_region.end()) { - model.parameters.get()[{ + model.parameters.template get()[{ mio::osirmobility::Region(i), mio::osirmobility::Region(j)}] = intersection_region; } } @@ -102,12 +103,13 @@ mio::IOResult preprocess(const std::string& filename, mio::osirmobility::M return mio::success(); } +template mio::IOResult set_mobility_weights(const std::string& mobility_data, const std::string& trip_chains, - mio::osirmobility::Model& model, size_t number_regions) + mio::osirmobility::Model& model, size_t number_regions) { BOOST_OUTCOME_TRY(preprocess(trip_chains, model)); // mobility between nodes - BOOST_OUTCOME_TRY(mobility_data_commuter, + BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, mio::read_mobility_plain(mobility_data + "mobility" + "commuter_migration_scaled.txt")); if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || mobility_data_commuter.cols() != Eigen::Index(number_regions)) { @@ -123,7 +125,7 @@ mio::IOResult set_mobility_weights(const std::string& mobility_data, const auto population_i = model.populations.get_group_total(mio::osirmobility::Region(county_idx_i)); auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / population_i; if (commuter_coeff_ij > 4e-5) { - model.parameters.get().push_back( + model.parameters.template get().push_back( {mio::osirmobility::Region(county_idx_i), mio::osirmobility::Region(county_idx_j), commuter_coeff_ij}); } @@ -137,40 +139,42 @@ int main() { mio::set_log_level(mio::LogLevel::debug); - double t0 = 0.; - double tmax = 50.; - double dt = 1; + ScalarType t0 = 0.; + ScalarType tmax = 50.; + ScalarType dt = 1; - size_t number_regions = 1; - size_t number_age_groups = 1; - size_t total_population_per_region = 10; + ScalarType number_regions = 4; + ScalarType number_age_groups = 1; + ScalarType total_population_per_region = 10; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); const std::string& mobility_data = ""; const std::string& trip_chain_data = ""; - mio::osirmobility::Model model(number_regions, number_age_groups); + mio::osirmobility::Model model(number_regions, number_age_groups); for (size_t i = 0; i < number_regions; i++) { - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] = 1; - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}] = 0; - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Susceptible)}] = + model.populations[{mio::osirmobility::Region(i), mio::AgeGroup(0), + mio::osirmobility::InfectionState::Infected}] = 1; + model.populations[{mio::osirmobility::Region(i), mio::AgeGroup(0), + mio::osirmobility::InfectionState::Recovered}] = 0; + model.populations[{mio::osirmobility::Region(i), mio::AgeGroup(0), + mio::osirmobility::InfectionState::Susceptible}] = total_population_per_region - - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Infected)}] - - model.populations[{mio::Index( - mio::osirmobility::Region(i), mio::AgeGroup(0), mio::osirmobility::InfectionState::Recovered)}]; + model.populations[{mio::osirmobility::Region(i), mio::AgeGroup(0), + mio::osirmobility::InfectionState::Infected}] - + model.populations[{mio::osirmobility::Region(i), mio::AgeGroup(0), + mio::osirmobility::InfectionState::Recovered}]; } - model.parameters.set(2); - model.parameters.set(0.04); - model.parameters.set(1.); - model.parameters.get().get_baseline()(0, 0) = 1.; - model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.set>(2); + model.parameters.set>(0.04); + model.parameters.set>(1.); + mio::ContactMatrixGroup& contact_matrix = + model.parameters.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(1.0); + contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.2}); @@ -202,7 +206,8 @@ int main() // auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); - auto integrator = std::make_shared(); + std::shared_ptr> integrator = + std::make_shared>(); model.check_constraints(); @@ -231,5 +236,6 @@ int main() } } } + printf("\n"); } } diff --git a/cpp/models/ode_seir_mobility/model.h b/cpp/models/ode_seir_mobility/model.h index 647a01dac1..7308b874a2 100644 --- a/cpp/models/ode_seir_mobility/model.h +++ b/cpp/models/ode_seir_mobility/model.h @@ -22,35 +22,41 @@ using Flows = TypeList, Flow>; -class Model : public FlowModel, Parameters, Flows> +template +class Model : public FlowModel, + Parameters, Flows> { - using Base = FlowModel, Parameters, Flows>; + using Base = + FlowModel, Parameters, Flows>; public: - Model(int num_regions, int num_agegroups = 1) - : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}, 0.), - ParameterSet(num_regions, num_agegroups)) + using typename Base::ParameterSet; + using typename Base::Populations; + + Model(int num_regions, int num_agegroups) + : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}), + ParameterSet(Region(num_regions), AgeGroup(num_agegroups))) { } // Einmal über den Vektor und später nochmal über die Regions - void get_flows(Eigen::Ref pop, Eigen::Ref y, double t, - Eigen::Ref flows) const override + void get_flows(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> flows) const override { - auto& params = this->parameters; - auto& population = this->populations; - - Region n_regions = params.get_num_regions(); - AgeGroup n_agegroups = params.get_num_agegroups(); - - for (auto age_i = AgeGroup(0); age_i < n_agegroups; age_i++) { - for (auto age_j = AgeGroup(0); age_j < n_agegroups; age_j++) { - double coeffStoI = - params.get().get_matrix_at(t)(static_cast((size_t)age_i), - static_cast((size_t)age_j)) * - params.get() / population.get_group_total(age_j); - for (auto edge : params.get()) { + const auto& params = this->parameters; + const auto& population = this->populations; + + const Index n_age_groups = reduce_index>(params.get_num_agegroups()); + const Index n_regions = reduce_index>(params.get_num_regions()); + + for (auto age_i : make_index_range(n_age_groups)) { + for (auto age_j : make_index_range(n_age_groups)) { + double coeffStoI = params.template get>().get_cont_freq_mat().get_matrix_at(t)( + age_i.get(), age_j.get()) * + params.template get>()[age_i] / + population.get_group_total(age_j); + for (auto edge : params.template get()) { auto start_region = get<0>(edge); auto end_region = get<1>(edge); auto strength = get(edge); @@ -58,49 +64,48 @@ class Model : public FlowModel( + flows[Base::template get_flat_flow_index( {start_region, age_i})] += strength * pop[population.get_flat_index({end_region, age_j, InfectionState::Infected})]; // s_m += h_mn/P_m * i_n - flows[get_flat_flow_index( + flows[Base::template get_flat_flow_index( {end_region, age_i})] += strength * pop[population.get_flat_index({start_region, age_j, InfectionState::Infected})]; // s_n += gamma * h_nm/P_n * sum(h_km/P_k * p_nm,k * i_k) - for (auto edge_commuter : params.get()) { + for (auto edge_commuter : params.template get()) { auto start_region_commuter = get<0>(edge_commuter); auto end_region_commuter = get<1>(edge_commuter); auto strength_commuter = get(edge_commuter); if (end_region_commuter != end_region || start_region_commuter == start_region || - ((std::find(params.get()[{start_region, end_region}].begin(), - params.get()[{start_region, end_region}].end(), + ((std::find(params.template get()[{start_region, end_region}].begin(), + params.template get()[{start_region, end_region}].end(), start_region_commuter)) == - params.get()[{start_region, end_region}].end())) { + params.template get()[{start_region, end_region}].end())) { continue; } - flows[get_flat_flow_index( + flows[Base::template get_flat_flow_index( {start_region, age_i})] += - params.get() * strength * strength_commuter * + params.template get>() * strength * strength_commuter * pop[population.get_flat_index({start_region_commuter, age_j, InfectionState::Infected})]; } } - for (auto region = Region(0); region < n_regions; region++) { - flows[get_flat_flow_index({region, age_i})] += - pop[population.get_flat_index({region, age_j, InfectionState::Infected})]; - flows[get_flat_flow_index({region, age_i})] *= + for (auto region : make_index_range(n_regions)) { + flows[Base::template get_flat_flow_index( + {region, age_i})] += pop[population.get_flat_index({region, age_j, InfectionState::Infected})]; + flows[Base::template get_flat_flow_index( + {region, age_i})] *= coeffStoI * y[population.get_flat_index({region, age_j, InfectionState::Susceptible})]; } } - for (auto region = Region(0); region < n_regions; region++) { - flows[get_flat_flow_index({region, age_i})] = - (1.0 / params.get()) * - y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; - flows[get_flat_flow_index({region, age_i})] = - (1.0 / params.get()) * - y[population.get_flat_index({region, age_i, InfectionState::Infected})]; - // flows[get_flat_flow_index({region, age_i})] = 0.; - // flows[get_flat_flow_index({region, age_i})] = 0.; + for (auto region : make_index_range(n_regions)) { + flows[Base::template get_flat_flow_index( + {region, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; + flows[Base::template get_flat_flow_index( + {region, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region, age_i, InfectionState::Infected})]; } } } diff --git a/cpp/models/ode_seir_mobility/parameters.h b/cpp/models/ode_seir_mobility/parameters.h index fa02511316..c2a02f83e4 100644 --- a/cpp/models/ode_seir_mobility/parameters.h +++ b/cpp/models/ode_seir_mobility/parameters.h @@ -2,8 +2,8 @@ #ifndef SEIRMOBILITY_PARAMETERS_H #define SEIRMOBILITY_PARAMETERS_H +#include "memilio/epidemiology/uncertain_matrix.h" #include "memilio/utils/uncertain_value.h" -#include "memilio/epidemiology/contact_matrix.h" #include "memilio/epidemiology/age_group.h" #include "memilio/utils/parameter_set.h" #include "memilio/utils/custom_index_array.h" @@ -23,11 +23,12 @@ namespace oseirmobility /** * @brief Probability of getting infected from a contact. */ +template struct TransmissionProbabilityOnContact { - using Type = UncertainValue; - static Type get_default(Region) + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) { - return Type(1.0); + return Type(size, 1.0); } static std::string name() { @@ -36,13 +37,14 @@ struct TransmissionProbabilityOnContact { }; /** - * @brief the latent time in day unit - */ + * @brief the latent time in day unit + */ +template struct TimeExposed { - using Type = UncertainValue; - static Type get_default() + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) { - return Type(5.2); + return Type(size, 5.2); } static std::string name() { @@ -51,13 +53,14 @@ struct TimeExposed { }; /** - * @brief The infectious time in day unit. - */ + * @brief The infectious time in day unit. + */ +template struct TimeInfected { - using Type = UncertainValue; - static Type get_default(Region) + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) { - return Type(6.0); + return Type(size, 6.0); } static std::string name() { @@ -68,11 +71,12 @@ struct TimeInfected { /** * @brief The contact patterns within the society are modelled using a ContactMatrix. */ +template struct ContactPatterns { - using Type = ContactMatrix; - static Type get_default(Region) + using Type = UncertainContactMatrix; + static Type get_default(Region, AgeGroup size) { - return Type{1}; + return Type(1, static_cast((size_t)size)); } static std::string name() { @@ -85,7 +89,7 @@ struct ContactPatterns { */ struct CommutingRatio { using Type = std::vector>; - static Type get_default(Region) + static Type get_default(Region, AgeGroup) { return Type({{Region(0), Region(0), 0.}}); } @@ -98,9 +102,10 @@ struct CommutingRatio { /** * @brief The ratio that regulates the infections during commuting. */ +template struct ImpactCommuters { - using Type = UncertainValue; - static Type get_default(Region) + using Type = UncertainValue; + static Type get_default(Region, AgeGroup) { return Type(0.); } @@ -115,7 +120,7 @@ struct ImpactCommuters { */ struct PathIntersections { using Type = CustomIndexArray, Region, Region>; - static Type get_default(Region size) + static Type get_default(Region size, AgeGroup) { return Type({size, size}); } @@ -125,17 +130,19 @@ struct PathIntersections { } }; -using ParametersBase = ParameterSet; +template +using ParametersBase = ParameterSet, TimeExposed, TimeInfected, + ContactPatterns, CommutingRatio, ImpactCommuters, PathIntersections>; /** * @brief Parameters of SEIR model. */ -class Parameters : public ParametersBase +template +class Parameters : public ParametersBase { public: Parameters(Region num_regions, AgeGroup num_agegroups) - : ParametersBase(num_regions) + : ParametersBase(num_regions, num_agegroups) , m_num_regions{num_regions} , m_num_agegroups(num_agegroups) { @@ -169,36 +176,42 @@ class Parameters : public ParametersBase double tol_times = 1e-1; int corrected = false; - if (this->get() < tol_times) { - log_warning("Constraint check: Parameter TimeExposed 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; - } - if (this->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; - } - if (this->get() < 0.0 || - this->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; + + for (auto i = AgeGroup(0); i < AgeGroup(m_num_agegroups); i++) { + if (this->template get>()[i] < 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->template get>()[i], tol_times); + this->template get>()[i] = tol_times; + corrected = true; + } + 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->template get>()[i], tol_times); + this->template get>()[i] = tol_times; + corrected = true; + } + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { + log_warning( + "Constraint check: Parameter TransmissionProbabilityOnContact changed from {:0.4f} to {:d} ", + this->template get>()[i], 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 ImpactCommuters changed from {:.4f} to {:.4f}.", - this->get(), 0.0); - this->get() = 0.0; - corrected = true; + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } - for (auto& i : this->get()) { + for (auto& i : this->template get()) { if (std::get(i) < 0.0 || std::get(i) > 1.0) { log_warning("Constraint check: Parameter CommutingRatio changed from {:.4f} to {:.4f}.", std::get(i), 0.0); @@ -209,8 +222,9 @@ class Parameters : public ParametersBase std::get<1>(i) >= m_num_regions) { log_warning( "Constraint check: Removed entry of Parameter CommutingRatio because of non-existing Regions."); - auto it = std::find(this->get().begin(), this->get().end(), i); - this->get().erase(it); + auto it = std::find(this->template get().begin(), + this->template get().end(), i); + this->template get().erase(it); corrected = true; } } @@ -226,33 +240,37 @@ class Parameters : public ParametersBase { double tol_times = 1e-1; - if (this->get() < tol_times) { - log_error("Constraint check: Parameter TimeExposed {:.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); - return true; - } - if (this->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); - return true; - } - if (this->get() < 0.0 || - this->get() > 1.0) { - log_error( - "Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); - return true; + for (auto i = AgeGroup(0); i < AgeGroup(m_num_agegroups); i++) { + if (this->template get>()[i] < tol_times) { + log_error( + "Constraint check: Parameter TimeExposed {:.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->template get>()[i], 0.0); + return true; + } + if (this->template get>()[i] < 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->template get>()[i], 0.0); + return true; + } + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { + log_error("Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or " + "greater {:.4f}", + this->template get>()[i], 0.0, 1.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 ImpactCommuters {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); + this->template get>(), 0.0, 1.0); return true; } - for (auto i : this->get()) { + for (auto i : this->template get()) { if (std::get(i) < 0.0 || std::get(i) > 1.0) { log_error("Constraint check: Parameter CommutingRatio entry {:.4f} smaller {:.4f} or greater {:.4f}", std::get(i), 0.0, 1.0); @@ -260,8 +278,8 @@ class Parameters : public ParametersBase } if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) > m_num_regions || std::get<1>(i) > m_num_regions) { - log_error("Constraint check: Parameter CommutingRatio has an entry with start or end Region that does " - "not appear in the model."); + log_error("Constraint check: Parameter CommutingRatio has an entry with start or end Region " + "that does not appear in the model."); return true; } } @@ -282,7 +300,7 @@ class Parameters : public ParametersBase template static IOResult deserialize(IOContext& io) { - BOOST_OUTCOME_TRY(base, ParametersBase::deserialize(io)); + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); return success(Parameters(std::move(base))); } diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h index 3936b6180d..82cebe7b76 100644 --- a/cpp/models/ode_sir_mobility/model.h +++ b/cpp/models/ode_sir_mobility/model.h @@ -21,35 +21,41 @@ namespace osirmobility using Flows = TypeList, Flow>; -class Model : public FlowModel, Parameters, Flows> +template +class Model : public FlowModel, + Parameters, Flows> { - using Base = FlowModel, Parameters, Flows>; + using Base = + FlowModel, Parameters, Flows>; public: + using typename Base::ParameterSet; + using typename Base::Populations; + Model(int num_regions, int num_agegroups) - : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}, 0.), - ParameterSet(num_regions, num_agegroups)) + : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}), + ParameterSet(Region(num_regions), AgeGroup(num_agegroups))) { } // Einmal über den Vektor und später nochmal über die Regions - void get_flows(Eigen::Ref pop, Eigen::Ref y, double t, - Eigen::Ref flows) const override + void get_flows(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> flows) const override { - auto& params = this->parameters; - auto& population = this->populations; - - Region n_regions = params.get_num_regions(); - AgeGroup n_agegroups = params.get_num_agegroups(); - - for (auto age_i = AgeGroup(0); age_i < n_agegroups; age_i++) { - for (auto age_j = AgeGroup(0); age_j < n_agegroups; age_j++) { - double coeffStoI = - params.get().get_matrix_at(t)(static_cast((size_t)age_i), - static_cast((size_t)age_j)) * - params.get() / population.get_group_total(age_j); - for (auto edge : params.get()) { + const auto& params = this->parameters; + const auto& population = this->populations; + + const Index n_age_groups = reduce_index>(params.get_num_agegroups()); + const Index n_regions = reduce_index>(params.get_num_regions()); + + for (auto age_i : make_index_range(n_age_groups)) { + for (auto age_j : make_index_range(n_age_groups)) { + double coeffStoI = params.template get>().get_cont_freq_mat().get_matrix_at(t)( + age_i.get(), age_j.get()) * + params.template get>()[age_i] / + population.get_group_total(age_j); + for (auto edge : params.template get()) { auto start_region = get<0>(edge); auto end_region = get<1>(edge); auto strength = get(edge); @@ -57,45 +63,45 @@ class Model : public FlowModel( + flows[Base::template get_flat_flow_index( {start_region, age_i})] += strength * pop[population.get_flat_index({end_region, age_j, InfectionState::Infected})]; // s_m += h_mn/P_m * i_n - flows[get_flat_flow_index( + flows[Base::template get_flat_flow_index( {end_region, age_i})] += strength * pop[population.get_flat_index({start_region, age_j, InfectionState::Infected})]; // s_n += gamma * h_nm/P_n * sum(h_km/P_k * p_nm,k * i_k) - for (auto edge_commuter : params.get()) { + for (auto edge_commuter : params.template get()) { auto start_region_commuter = get<0>(edge_commuter); auto end_region_commuter = get<1>(edge_commuter); auto strength_commuter = get(edge_commuter); if (end_region_commuter != end_region || start_region_commuter == start_region || - ((std::find(params.get()[{start_region, end_region}].begin(), - params.get()[{start_region, end_region}].end(), + ((std::find(params.template get()[{start_region, end_region}].begin(), + params.template get()[{start_region, end_region}].end(), start_region_commuter)) == - params.get()[{start_region, end_region}].end())) { + params.template get()[{start_region, end_region}].end())) { continue; } - flows[get_flat_flow_index( + flows[Base::template get_flat_flow_index( {start_region, age_i})] += - params.get() * strength * strength_commuter * + params.template get>() * strength * strength_commuter * pop[population.get_flat_index({start_region_commuter, age_j, InfectionState::Infected})]; } } - for (auto region = Region(0); region < n_regions; region++) { - flows[get_flat_flow_index( + for (auto region : make_index_range(n_regions)) { + flows[Base::template get_flat_flow_index( {region, age_i})] += pop[population.get_flat_index({region, age_j, InfectionState::Infected})]; - flows[get_flat_flow_index( + flows[Base::template get_flat_flow_index( {region, age_i})] *= coeffStoI * y[population.get_flat_index({region, age_j, InfectionState::Susceptible})]; } } - for (auto region = Region(0); region < n_regions; region++) { - flows[get_flat_flow_index({region, age_i})] = - (1.0 / params.get()) * - y[population.get_flat_index({region, age_i, InfectionState::Infected})]; + for (auto region : make_index_range(n_regions)) { + flows[Base::template get_flat_flow_index( + {region, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region, age_i, InfectionState::Infected})]; } } } diff --git a/cpp/models/ode_sir_mobility/parameters.h b/cpp/models/ode_sir_mobility/parameters.h index ba66109d98..cceb5f2dc3 100644 --- a/cpp/models/ode_sir_mobility/parameters.h +++ b/cpp/models/ode_sir_mobility/parameters.h @@ -2,8 +2,8 @@ #ifndef SIRMOBILITY_PARAMETERS_H #define SIRMOBILITY_PARAMETERS_H +#include "memilio/epidemiology/uncertain_matrix.h" #include "memilio/utils/uncertain_value.h" -#include "memilio/epidemiology/contact_matrix.h" #include "memilio/epidemiology/age_group.h" #include "memilio/utils/parameter_set.h" #include "memilio/utils/custom_index_array.h" @@ -23,11 +23,12 @@ namespace osirmobility /** * @brief Probability of getting infected from a contact. */ +template struct TransmissionProbabilityOnContact { - using Type = UncertainValue; - static Type get_default(Region) + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) { - return Type(1.0); + return Type(size, 1.0); } static std::string name() { @@ -36,13 +37,14 @@ struct TransmissionProbabilityOnContact { }; /** - * @brief The infectious time in day unit. - */ + * @brief The infectious time in day unit. + */ +template struct TimeInfected { - using Type = UncertainValue; - static Type get_default(Region) + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) { - return Type(6.0); + return Type(size, 6.0); } static std::string name() { @@ -53,11 +55,12 @@ struct TimeInfected { /** * @brief The contact patterns within the society are modelled using a ContactMatrix. */ +template struct ContactPatterns { - using Type = ContactMatrix; - static Type get_default(Region) + using Type = UncertainContactMatrix; + static Type get_default(Region, AgeGroup size) { - return Type{1}; + return Type(1, static_cast((size_t)size)); } static std::string name() { @@ -70,7 +73,7 @@ struct ContactPatterns { */ struct CommutingRatio { using Type = std::vector>; - static Type get_default(Region) + static Type get_default(Region, AgeGroup) { return Type({{Region(0), Region(0), 0.}}); } @@ -83,9 +86,10 @@ struct CommutingRatio { /** * @brief The ratio that regulates the infections during commuting. */ +template struct ImpactCommuters { - using Type = UncertainValue; - static Type get_default(Region) + using Type = UncertainValue; + static Type get_default(Region, AgeGroup) { return Type(0.); } @@ -100,7 +104,7 @@ struct ImpactCommuters { */ struct PathIntersections { using Type = CustomIndexArray, Region, Region>; - static Type get_default(Region size) + static Type get_default(Region size, AgeGroup) { return Type({size, size}); } @@ -110,17 +114,19 @@ struct PathIntersections { } }; -using ParametersBase = ParameterSet; +template +using ParametersBase = ParameterSet, TimeInfected, ContactPatterns, + CommutingRatio, ImpactCommuters, PathIntersections>; /** * @brief Parameters of SIR model. */ -class Parameters : public ParametersBase +template +class Parameters : public ParametersBase { public: Parameters(Region num_regions, AgeGroup num_agegroups) - : ParametersBase(num_regions) + : ParametersBase(num_regions, num_agegroups) , m_num_regions{num_regions} , m_num_agegroups(num_agegroups) { @@ -154,28 +160,33 @@ class Parameters : public ParametersBase double tol_times = 1e-1; int corrected = false; - if (this->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; - } - if (this->get() < 0.0 || - this->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; + + for (auto i = AgeGroup(0); i < AgeGroup(m_num_agegroups); i++) { + if (this->template get>()[i] < 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->template get>()[i], tol_times); + this->template get>()[i] = tol_times; + corrected = true; + } + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { + log_warning( + "Constraint check: Parameter TransmissionProbabilityOnContact changed from {:0.4f} to {:d} ", + this->template get>()[i], 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 ImpactCommuters changed from {:.4f} to {:.4f}.", - this->get(), 0.0); - this->get() = 0.0; - corrected = true; + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } - for (auto& i : this->get()) { + for (auto& i : this->template get()) { if (std::get(i) < 0.0 || std::get(i) > 1.0) { log_warning("Constraint check: Parameter CommutingRatio changed from {:.4f} to {:.4f}.", std::get(i), 0.0); @@ -186,8 +197,9 @@ class Parameters : public ParametersBase std::get<1>(i) >= m_num_regions) { log_warning( "Constraint check: Removed entry of Parameter CommutingRatio because of non-existing Regions."); - auto it = std::find(this->get().begin(), this->get().end(), i); - this->get().erase(it); + auto it = std::find(this->template get().begin(), + this->template get().end(), i); + this->template get().erase(it); corrected = true; } } @@ -203,26 +215,30 @@ class Parameters : public ParametersBase { double tol_times = 1e-1; - if (this->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); - return true; - } - if (this->get() < 0.0 || - this->get() > 1.0) { - log_error( - "Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); - return true; + for (auto i = AgeGroup(0); i < AgeGroup(m_num_agegroups); i++) { + + if (this->template get>()[i] < 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->template get>()[i], 0.0); + return true; + } + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { + log_error("Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or " + "greater {:.4f}", + this->template get>()[i], 0.0, 1.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 ImpactCommuters {:.4f} smaller {:.4f} or greater {:.4f}", - this->get(), 0.0, 1.0); + this->template get>(), 0.0, 1.0); return true; } - for (auto i : this->get()) { + for (auto i : this->template get()) { if (std::get(i) < 0.0 || std::get(i) > 1.0) { log_error("Constraint check: Parameter CommutingRatio entry {:.4f} smaller {:.4f} or greater {:.4f}", std::get(i), 0.0, 1.0); @@ -230,8 +246,8 @@ class Parameters : public ParametersBase } if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) > m_num_regions || std::get<1>(i) > m_num_regions) { - log_error("Constraint check: Parameter CommutingRatio has an entry with start or end Region that does " - "not appear in the model."); + log_error("Constraint check: Parameter CommutingRatio has an entry with start or end Region " + "that does not appear in the model."); return true; } } @@ -252,7 +268,7 @@ class Parameters : public ParametersBase template static IOResult deserialize(IOContext& io) { - BOOST_OUTCOME_TRY(base, ParametersBase::deserialize(io)); + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); return success(Parameters(std::move(base))); } From 6ace8cf3d0b92586c6077a36514fd5202d1d62fe Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:05:13 +0200 Subject: [PATCH 11/49] Renaming --- cpp/models/ode_seir_mobility/model.h | 3 ++- cpp/models/ode_seir_mobility/parameters.h | 30 +++++++++++++---------- cpp/models/ode_sir_mobility/model.h | 3 ++- cpp/models/ode_sir_mobility/parameters.h | 25 ++++++++++--------- cpp/tests/test_odesirmobility.cpp | 18 +++++++------- 5 files changed, 44 insertions(+), 35 deletions(-) diff --git a/cpp/models/ode_seir_mobility/model.h b/cpp/models/ode_seir_mobility/model.h index 7308b874a2..a40db1474a 100644 --- a/cpp/models/ode_seir_mobility/model.h +++ b/cpp/models/ode_seir_mobility/model.h @@ -86,7 +86,8 @@ class Model : public FlowModel( {start_region, age_i})] += - params.template get>() * strength * strength_commuter * + params.template get>() * strength * + strength_commuter * pop[population.get_flat_index({start_region_commuter, age_j, InfectionState::Infected})]; } } diff --git a/cpp/models/ode_seir_mobility/parameters.h b/cpp/models/ode_seir_mobility/parameters.h index c2a02f83e4..0b27284c9d 100644 --- a/cpp/models/ode_seir_mobility/parameters.h +++ b/cpp/models/ode_seir_mobility/parameters.h @@ -103,7 +103,7 @@ struct CommutingRatio { * @brief The ratio that regulates the infections during commuting. */ template -struct ImpactCommuters { +struct ImpactTransmissionDuringCommuting { using Type = UncertainValue; static Type get_default(Region, AgeGroup) { @@ -111,7 +111,7 @@ struct ImpactCommuters { } static std::string name() { - return "ImpactCommuters"; + return "ImpactTransmissionDuringCommuting"; } }; @@ -131,8 +131,9 @@ struct PathIntersections { }; template -using ParametersBase = ParameterSet, TimeExposed, TimeInfected, - ContactPatterns, CommutingRatio, ImpactCommuters, PathIntersections>; +using ParametersBase = + ParameterSet, TimeExposed, TimeInfected, ContactPatterns, + CommutingRatio, ImpactTransmissionDuringCommuting, PathIntersections>; /** * @brief Parameters of SEIR model. @@ -187,7 +188,7 @@ class Parameters : public ParametersBase this->template get>()[i] = tol_times; corrected = true; } - if (this->template get() < tol_times) { + if (this->template get>()[i] < 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 " @@ -205,11 +206,12 @@ class Parameters : public ParametersBase corrected = true; } } - if (this->template get>() < 0.0 || this->template get>() > 1.0) { - log_warning("Constraint check: Parameter ImpactCommuters changed from {:.4f} to {:.4f}.", - this->template get>(), 0.0); - this->template get>() = 0.0; - corrected = true; + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_warning("Constraint check: Parameter ImpactTransmissionDuringCommuting changed from {:.4f} to {:.4f}.", + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } for (auto& i : this->template get()) { if (std::get(i) < 0.0 || std::get(i) > 1.0) { @@ -265,9 +267,11 @@ class Parameters : public ParametersBase return true; } } - if (this->template get>() < 0.0 || this->template get>() > 1.0) { - log_error("Constraint check: Parameter ImpactCommuters {:.4f} smaller {:.4f} or greater {:.4f}", - this->template get>(), 0.0, 1.0); + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_error( + "Constraint check: Parameter ImpactTransmissionDuringCommuting {:.4f} smaller {:.4f} or greater {:.4f}", + this->template get>(), 0.0, 1.0); return true; } for (auto i : this->template get()) { diff --git a/cpp/models/ode_sir_mobility/model.h b/cpp/models/ode_sir_mobility/model.h index 82cebe7b76..30ef675728 100644 --- a/cpp/models/ode_sir_mobility/model.h +++ b/cpp/models/ode_sir_mobility/model.h @@ -85,7 +85,8 @@ class Model : public FlowModel( {start_region, age_i})] += - params.template get>() * strength * strength_commuter * + params.template get>() * strength * + strength_commuter * pop[population.get_flat_index({start_region_commuter, age_j, InfectionState::Infected})]; } } diff --git a/cpp/models/ode_sir_mobility/parameters.h b/cpp/models/ode_sir_mobility/parameters.h index cceb5f2dc3..18f7110543 100644 --- a/cpp/models/ode_sir_mobility/parameters.h +++ b/cpp/models/ode_sir_mobility/parameters.h @@ -87,7 +87,7 @@ struct CommutingRatio { * @brief The ratio that regulates the infections during commuting. */ template -struct ImpactCommuters { +struct ImpactTransmissionDuringCommuting { using Type = UncertainValue; static Type get_default(Region, AgeGroup) { @@ -95,7 +95,7 @@ struct ImpactCommuters { } static std::string name() { - return "ImpactCommuters"; + return "ImpactTransmissionDuringCommuting"; } }; @@ -116,7 +116,7 @@ struct PathIntersections { template using ParametersBase = ParameterSet, TimeInfected, ContactPatterns, - CommutingRatio, ImpactCommuters, PathIntersections>; + CommutingRatio, ImpactTransmissionDuringCommuting, PathIntersections>; /** * @brief Parameters of SIR model. @@ -180,11 +180,12 @@ class Parameters : public ParametersBase corrected = true; } } - if (this->template get>() < 0.0 || this->template get>() > 1.0) { - log_warning("Constraint check: Parameter ImpactCommuters changed from {:.4f} to {:.4f}.", - this->template get>(), 0.0); - this->template get>() = 0.0; - corrected = true; + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_warning("Constraint check: Parameter ImpactTransmissionDuringCommuting changed from {:.4f} to {:.4f}.", + this->template get>(), 0.0); + this->template get>() = 0.0; + corrected = true; } for (auto& i : this->template get()) { if (std::get(i) < 0.0 || std::get(i) > 1.0) { @@ -233,9 +234,11 @@ class Parameters : public ParametersBase return true; } } - if (this->template get>() < 0.0 || this->template get>() > 1.0) { - log_error("Constraint check: Parameter ImpactCommuters {:.4f} smaller {:.4f} or greater {:.4f}", - this->template get>(), 0.0, 1.0); + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_error( + "Constraint check: Parameter ImpactTransmissionDuringCommuting {:.4f} smaller {:.4f} or greater {:.4f}", + this->template get>(), 0.0, 1.0); return true; } for (auto i : this->template get()) { diff --git a/cpp/tests/test_odesirmobility.cpp b/cpp/tests/test_odesirmobility.cpp index 13173ec405..247a8622ff 100644 --- a/cpp/tests/test_odesirmobility.cpp +++ b/cpp/tests/test_odesirmobility.cpp @@ -57,7 +57,7 @@ TEST(TestOdeSirMobility, compareWithPreviousRun) model.parameters.get().get_baseline()(0, 0) = 2.7; model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); - model.parameters.set(1.); + model.parameters.set(1.); model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.2}); model.parameters.get().push_back( @@ -141,7 +141,7 @@ TEST(TestOdeSirMobility, checkPopulationConservation) } model.parameters.set(2); model.parameters.set(0.04); - model.parameters.set(1.); + model.parameters.set(1.); model.parameters.get().get_baseline()(0, 0) = 1.; model.parameters.get().add_damping(0.6, mio::SimulationTime(12.5)); model.parameters.get().push_back( @@ -170,7 +170,7 @@ TEST(TestOdeSirMobility, check_constraints_parameters) mio::osirmobility::Model model((size_t)num_regions); model.parameters.set(6); model.parameters.set(0.04); - model.parameters.set(1.); + model.parameters.set(1.); model.parameters.get().get_baseline()(0, 0) = 10.; model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); @@ -189,10 +189,10 @@ TEST(TestOdeSirMobility, check_constraints_parameters) ASSERT_EQ(model.parameters.check_constraints(), 1); model.parameters.set(0.04); - model.parameters.set(10.); + model.parameters.set(10.); ASSERT_EQ(model.parameters.check_constraints(), 1); - model.parameters.set(1.); + model.parameters.set(1.); model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 10.5}); ASSERT_EQ(model.parameters.check_constraints(), 1); @@ -211,7 +211,7 @@ TEST(TestOdeSirMobility, apply_constraints_parameters) mio::osirmobility::Model model((size_t)num_regions); model.parameters.set(6); model.parameters.set(0.04); - model.parameters.set(1.); + model.parameters.set(1.); model.parameters.get().get_baseline()(0, 0) = 10.; model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 0.5}); @@ -229,11 +229,11 @@ TEST(TestOdeSirMobility, apply_constraints_parameters) EXPECT_NEAR(model.parameters.get(), 0.0, 1e-14); model.parameters.set(0.04); - model.parameters.set(10.); + model.parameters.set(10.); EXPECT_EQ(model.parameters.apply_constraints(), 1); - EXPECT_NEAR(model.parameters.get(), 0.0, 1e-14); + EXPECT_NEAR(model.parameters.get(), 0.0, 1e-14); - model.parameters.set(1.); + model.parameters.set(1.); model.parameters.get().push_back( {mio::osirmobility::Region(1), mio::osirmobility::Region(0), 10.5}); EXPECT_EQ(model.parameters.apply_constraints(), 1); From 232f7057080ac866eea0a5f9575f8f1334202109 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:06:16 +0200 Subject: [PATCH 12/49] Renaming --- cpp/examples/ode_sir_mobility.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/ode_sir_mobility.cpp b/cpp/examples/ode_sir_mobility.cpp index 46dca6739e..7aaf725e45 100644 --- a/cpp/examples/ode_sir_mobility.cpp +++ b/cpp/examples/ode_sir_mobility.cpp @@ -170,7 +170,7 @@ int main() model.parameters.set>(2); model.parameters.set>(0.04); - model.parameters.set>(1.); + model.parameters.set>(1.); mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(1.0); From 6045e1fed46e6e3b93d5a1dfb64bbe9ec718c8d5 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:07:31 +0200 Subject: [PATCH 13/49] Set up examples for comparison --- cpp/examples/graph.cpp | 29 +++++-- cpp/examples/ode_seir_mobility.cpp | 129 ++++++++++++----------------- 2 files changed, 77 insertions(+), 81 deletions(-) diff --git a/cpp/examples/graph.cpp b/cpp/examples/graph.cpp index ecd5100882..06dd3ceff6 100644 --- a/cpp/examples/graph.cpp +++ b/cpp/examples/graph.cpp @@ -22,9 +22,12 @@ #include "ode_seir/parameters.h" #include "memilio/mobility/metapopulation_mobility_instant.h" #include "memilio/compartments/simulation.h" +#include "memilio/io/result_io.h" int main() { + mio::set_log_level(mio::LogLevel::off); + const auto t0 = 0.; const auto tmax = 10.; const auto dt = 0.5; //time step of migration, daily migration every second step @@ -34,9 +37,11 @@ int main() // set population model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = 10000; + model.parameters.set>(1.); + // set transition times - model.parameters.set>(1); - model.parameters.set>(1); + model.parameters.set>(3.); + model.parameters.set>(5.); // set contact matrix mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); @@ -47,9 +52,9 @@ int main() auto model_group2 = model; //some contact restrictions in group 1 - mio::ContactMatrixGroup& contact_matrix1 = - model_group1.parameters.get>().get_cont_freq_mat(); - contact_matrix1[0].add_damping(0.5, mio::SimulationTime(5)); + // mio::ContactMatrixGroup& contact_matrix1 = + // model_group1.parameters.get>().get_cont_freq_mat(); + // contact_matrix1[0].add_damping(0.5, mio::SimulationTime(5)); //infection starts in group 1 model_group1.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = 9990; @@ -65,5 +70,19 @@ int main() sim.advance(tmax); + auto result_graph = std::move(sim).get_graph(); + auto result = mio::interpolate_simulation_result(result_graph); + + std::vector county_ids(result_graph.nodes().size()); + std::transform(result_graph.nodes().begin(), result_graph.nodes().end(), county_ids.begin(), [](auto& n) { + return n.id; + }); + + // auto save_result_status = save_result(result, county_ids, 1, "graph_result.h5"); + + for (auto&& node : result_graph.nodes()) { + node.property.get_result().print_table(); + } + return 0; } diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp index c6b9785639..1e5d0cb1cd 100644 --- a/cpp/examples/ode_seir_mobility.cpp +++ b/cpp/examples/ode_seir_mobility.cpp @@ -9,6 +9,7 @@ #include "models/ode_seir_mobility/parameters.h" #include "models/ode_seir_mobility/regions.h" #include "memilio/io/io.h" +#include "memilio/io/result_io.h" mio::IOResult>>> read_path_mobility(const std::string& filename) { @@ -140,12 +141,12 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0.; - ScalarType tmax = 50.; + ScalarType tmax = 10.; ScalarType dt = 1; - ScalarType number_regions = 4; - ScalarType number_age_groups = 1; - ScalarType total_population_per_region = 10; + std::vector region_ids = {1001, 1002}; + ScalarType number_regions = region_ids.size(); + ScalarType number_age_groups = 1; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); @@ -153,90 +154,66 @@ int main() const std::string& trip_chain_data = ""; mio::oseirmobility::Model model(number_regions, number_age_groups); + model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Exposed}] = + 10; + model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Susceptible}] = 9990; + model.populations[{mio::oseirmobility::Region(1), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Exposed}] = + 0; + model.populations[{mio::oseirmobility::Region(1), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Susceptible}] = 10000; - for (size_t i = 0; i < number_regions; i++) { - model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Infected}] = 1; - model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Recovered}] = 0; - model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Susceptible}] = - total_population_per_region - - model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Infected}] - - model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Recovered}]; - } + model.parameters.set>(1.); + + model.parameters.set>(3.); + model.parameters.set>(5.); - model.parameters.set>(1); - model.parameters.set>(2); - model.parameters.set>(0.04); - model.parameters.set>(1.); + model.parameters.set>(0.); mio::ContactMatrixGroup& contact_matrix = - model.parameters.get>().get_cont_freq_mat(); - contact_matrix[0].get_baseline().setConstant(1.0); - contact_matrix[0].add_damping(0.6, mio::SimulationTime(12.5)); + model.parameters.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(2.7); + // contact_matrix[0].add_damping(0.5, mio::SimulationTime(5)); model.parameters.get().push_back( - {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.2}); - model.parameters.get().push_back( - {mio::oseirmobility::Region(1), mio::oseirmobility::Region(2), 0.6}); - model.parameters.get().push_back( - {mio::oseirmobility::Region(2), mio::oseirmobility::Region(0), 0.5}); + {mio::oseirmobility::Region(0), mio::oseirmobility::Region(1), 0.03}); model.parameters.get().push_back( - {mio::oseirmobility::Region(0), mio::oseirmobility::Region(3), 1.0}); - model.parameters.get().push_back( - {mio::oseirmobility::Region(1), mio::oseirmobility::Region(3), 0.2}); - - model.parameters.get()[{mio::oseirmobility::Region(0), - mio::oseirmobility::Region(1)}] = {2}; - model.parameters.get()[{mio::oseirmobility::Region(0), - mio::oseirmobility::Region(3)}] = {2}; - model.parameters.get()[{mio::oseirmobility::Region(1), - mio::oseirmobility::Region(0)}] = {2}; - model.parameters.get()[{mio::oseirmobility::Region(1), - mio::oseirmobility::Region(2)}] = {0}; - model.parameters.get()[{mio::oseirmobility::Region(1), - mio::oseirmobility::Region(3)}] = {2}; - model.parameters.get()[{mio::oseirmobility::Region(2), - mio::oseirmobility::Region(1)}] = {0}; - model.parameters.get()[{mio::oseirmobility::Region(3), - mio::oseirmobility::Region(0)}] = {2}; - model.parameters.get()[{mio::oseirmobility::Region(3), - mio::oseirmobility::Region(1)}] = {2}; - - // auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); - - std::shared_ptr> integrator = - std::make_shared>(); + {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.03}); + + using DefaultIntegratorCore = + mio::ControlledStepperWrapper; + + std::shared_ptr> integrator = std::make_shared(); model.check_constraints(); - auto sir = simulate(t0, tmax, dt, model, integrator); + auto result_from_sim = simulate(t0, tmax, dt, model, integrator); - bool print_to_terminal = true; + auto save_result_status = + mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); - sir.print_table(); + // bool print_to_terminal = true; - if (print_to_terminal) { + // sir.print_table(); - std::vector vars = {"S", "E", "I", "R"}; - printf("\n # t"); - for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { - for (size_t k = 0; k < (size_t)mio::oseirmobility::InfectionState::Count; k++) { - printf(" %s_%d", vars[k].c_str(), (int)i); - } - } + // if (print_to_terminal) { - auto num_points = static_cast(sir.get_num_time_points()); - for (size_t i = 0; i < num_points; i++) { - printf("\n%.14f ", sir.get_time(i)); - for (size_t k = 0; k < (size_t)model.parameters.get_num_regions(); k++) { - for (size_t j = 0; j < (size_t)mio::oseirmobility::InfectionState::Count; j++) { - printf(" %.14f", sir.get_value(i)[j + (size_t)mio::oseirmobility::InfectionState::Count * (int)k]); - } - } - } - printf("\n"); - } + // std::vector vars = {"S", "E", "I", "R"}; + // printf("\n # t"); + // for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { + // for (size_t k = 0; k < (size_t)mio::oseirmobility::InfectionState::Count; k++) { + // printf(" %s_%d", vars[k].c_str(), (int)i); + // } + // } + + // auto num_points = static_cast(sir.get_num_time_points()); + // for (size_t i = 0; i < num_points; i++) { + // printf("\n%.14f ", sir.get_time(i)); + // for (size_t k = 0; k < (size_t)model.parameters.get_num_regions(); k++) { + // for (size_t j = 0; j < (size_t)mio::oseirmobility::InfectionState::Count; j++) { + // printf(" %.14f", sir.get_value(i)[j + (size_t)mio::oseirmobility::InfectionState::Count * (int)k]); + // } + // } + // } + // printf("\n"); + // } } From dfdac2deb65e8b7b034a19e704e2d5e9b5775a55 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:14:10 +0200 Subject: [PATCH 14/49] changes for plots and corrections --- cpp/examples/graph.cpp | 4 +- cpp/examples/ode_seir_mobility.cpp | 8 +- cpp/models/ode_seir_mobility/model.h | 12 +- tools/plot_results_mobilitymodels.py | 164 +++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 tools/plot_results_mobilitymodels.py diff --git a/cpp/examples/graph.cpp b/cpp/examples/graph.cpp index 06dd3ceff6..3b8767a686 100644 --- a/cpp/examples/graph.cpp +++ b/cpp/examples/graph.cpp @@ -26,8 +26,6 @@ int main() { - mio::set_log_level(mio::LogLevel::off); - const auto t0 = 0.; const auto tmax = 10.; const auto dt = 0.5; //time step of migration, daily migration every second step @@ -78,7 +76,7 @@ int main() return n.id; }); - // auto save_result_status = save_result(result, county_ids, 1, "graph_result.h5"); + auto save_result_status = save_result(result, county_ids, 1, "graph_result.h5"); for (auto&& node : result_graph.nodes()) { node.property.get_result().print_table(); diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp index 1e5d0cb1cd..cfba2c1e8f 100644 --- a/cpp/examples/ode_seir_mobility.cpp +++ b/cpp/examples/ode_seir_mobility.cpp @@ -175,9 +175,9 @@ int main() // contact_matrix[0].add_damping(0.5, mio::SimulationTime(5)); model.parameters.get().push_back( - {mio::oseirmobility::Region(0), mio::oseirmobility::Region(1), 0.03}); + {mio::oseirmobility::Region(0), mio::oseirmobility::Region(1), 0.01}); model.parameters.get().push_back( - {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.03}); + {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.01}); using DefaultIntegratorCore = mio::ControlledStepperWrapper; @@ -189,11 +189,11 @@ int main() auto result_from_sim = simulate(t0, tmax, dt, model, integrator); auto save_result_status = - mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); + mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result.h5"); // bool print_to_terminal = true; - // sir.print_table(); + // result_from_sim.print_table(); // if (print_to_terminal) { diff --git a/cpp/models/ode_seir_mobility/model.h b/cpp/models/ode_seir_mobility/model.h index a40db1474a..c99eb732c9 100644 --- a/cpp/models/ode_seir_mobility/model.h +++ b/cpp/models/ode_seir_mobility/model.h @@ -52,10 +52,6 @@ class Model : public FlowModel>().get_cont_freq_mat().get_matrix_at(t)( - age_i.get(), age_j.get()) * - params.template get>()[age_i] / - population.get_group_total(age_j); for (auto edge : params.template get()) { auto start_region = get<0>(edge); auto end_region = get<1>(edge); @@ -92,6 +88,14 @@ class Model : public FlowModel({(size_t)region, 1}); + auto const population_region_age = population_region.template slice({(size_t)age_j, 1}); + double population_size = + std::accumulate(population_region_age.begin(), population_region_age.end(), 0.); + double coeffStoI = + params.template get>().get_cont_freq_mat().get_matrix_at(t)(age_i.get(), + age_j.get()) * + params.template get>()[age_i] / population_size; flows[Base::template get_flat_flow_index( {region, age_i})] += pop[population.get_flat_index({region, age_j, InfectionState::Infected})]; flows[Base::template get_flat_flow_index( diff --git a/tools/plot_results_mobilitymodels.py b/tools/plot_results_mobilitymodels.py new file mode 100644 index 0000000000..4eb997c724 --- /dev/null +++ b/tools/plot_results_mobilitymodels.py @@ -0,0 +1,164 @@ +import h5py +import os +import matplotlib.pyplot as plt + +import memilio.epidata.getDataIntoPandasDataFrame as gd + +# Define compartments. +secir_dict = {0: 'Susceptible', 1: 'Exposed', 2: 'Infected', 3: 'Recovered'} + +# Define color and style to be used while plotting for different models to make plots consistent. +color_dict = {0: '#1f77b4', + 1:'#2ca02c' + } +linestyle_dict = {"ODE": 'solid', + "Graph": 'dashdot' + } + +def compare_all_compartments(files, legendplot, filename_plot="compare_compartments"): + + fig, axs = plt.subplots( + 2, 2, sharex='all', num=filename_plot, tight_layout=False) + + # Add simulation results to plot. + for file in range(len(files)): + # Load data. + h5file = h5py.File(str(files[file]) + '.h5', 'r') + + number_regions = len(list(h5file.keys())) + for region in range(number_regions): + if (len(list(h5file[list(h5file.keys())[region]].keys())) > 3): + data = h5file[list(h5file.keys())[region]] + dates = data['Time'][:] + + number_regions = len(list(h5file[list(h5file.keys())[region]].keys())) - 2 + for region in range(number_regions): + total = data['Group'+str(region+1)][:, :] + if (total.shape[1] != 4): + raise gd.DataError("Expected a different number of compartments.") + # Plot result. + if legendplot[file] in linestyle_dict: + for i in range(4): + axs[int(i/2), i % 2].plot(dates, + total[:, i], label=legendplot[file] +" Region "+ str(region), linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], + color=color_dict[region]) + else: + for i in range(4): + axs[int(i/2), i % 2].plot(dates, + total[:, i], label=legendplot[file], linewidth=1.2) + else: + data = h5file[list(h5file.keys())[region]] + dates = data['Time'][:] + # As there should be only one Group, total is the simulation result. + total = data['Total'][:, :] + if (total.shape[1] != 4): + raise gd.DataError("Expected a different number of compartments.") + # Plot result. + if legendplot[file] in linestyle_dict: + for i in range(4): + axs[int(i/2), i % 2].plot(dates, + total[:, i], label=legendplot[file] +" Region "+ str(region), linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], + color=color_dict[region]) + else: + for i in range(4): + axs[int(i/2), i % 2].plot(dates, + total[:, i], label=legendplot[file], linewidth=1.2) + h5file.close() + + # Define some characteristics of the plot. + for i in range(4): + axs[int(i/2), i % 2].set_title(secir_dict[i], fontsize=8) + axs[int(i/2), i % 2].set_xlim(left=0, right=dates[-1]) + axs[int(i/2), i % 2].grid(True, linestyle='--') + axs[int(i/2), i % 2].tick_params(axis='y', labelsize=7) + axs[int(i/2), i % 2].tick_params(axis='x', labelsize=7) + # axs[int(i/2), i % 2].xaxis.set_ticks(np.arange(0, dates[-1]+1, 5)) + + fig.supxlabel('Time (in days)', fontsize=9) + + lines, labels = axs[0, 0].get_legend_handles_labels() + lgd = fig.legend(lines, labels, ncol=len(legendplot), loc='outside lower center', + fontsize=10, bbox_to_anchor=(0.5, - 0.06), bbox_transform=fig.transFigure) + + plt.tight_layout(pad=0, w_pad=0.5, h_pad=0.1) + plt.subplots_adjust(bottom=0.09) + + # Save result. + if not os.path.isdir('Plots'): + os.makedirs('Plots') + fig.savefig('Plots/'+filename_plot+'.png', + bbox_extra_artists=(lgd,), bbox_inches='tight', dpi=500) + + +def plot_new_infections(files, ylim, legendplot, filename_plot="compare_new_infections"): + + plt.figure(filename_plot) + + # Add simulation results to plot. + for file in range(len(files)): + # Load data. + h5file = h5py.File(str(files[file]) + '.h5', 'r') + + number_regions = len(list(h5file.keys())) + for region in range(number_regions): + if (len(list(h5file[list(h5file.keys())[region]].keys())) > 3): + data = h5file[list(h5file.keys())[region]] + dates = data['Time'][:] + + number_regions = len(list(h5file[list(h5file.keys())[region]].keys())) - 2 + for region_ in range(number_regions): + total = data['Group'+str(region_+1)][:, :] + if (total.shape[1] != 4): + raise gd.DataError( + "Expected a different number of compartments.") + incidence = (total[:-1, 0]-total[1:, 0])/(dates[1:]-dates[:-1]) + # Plot result. + if legendplot[file] in linestyle_dict: + plt.plot(dates[1:], incidence, linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], color=color_dict[region_]) + else: + plt.plot(dates[1:], incidence, linewidth=1.2) + else: + data = h5file[list(h5file.keys())[region]] + dates = data['Time'][:] + # As there should be only one Group, total is the simulation result. + total = data['Total'][:, :] + if (total.shape[1] != 4): + raise gd.DataError( + "Expected a different number of compartments.") + incidence = (total[:-1, 0]-total[1:, 0])/(dates[1:]-dates[:-1]) + # Plot result. + if legendplot[file] in linestyle_dict: + plt.plot(dates[1:], incidence, linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], color=color_dict[region]) + else: + plt.plot(dates[1:], incidence, linewidth=1.2) + + h5file.close() + + plt.xlabel('Time (in days)', fontsize=16) + # plt.xticks(np.arange(0, dates[-1]+1, 5)) + plt.yticks(fontsize=9) + plt.ylabel('New infections per day', fontsize=14) + plt.ylim(bottom=0, top=ylim) + plt.xlim(left=0, right=dates[-1]) + plt.legend(legendplot, fontsize=14, framealpha=0.5) + plt.grid(True, linestyle='--') + plt.tight_layout() + + # Save result. + if not os.path.isdir('Plots'): + os.makedirs('Plots') + plt.savefig('Plots/'+filename_plot+'.png', bbox_inches='tight', dpi=500) + + +if __name__ == '__main__': + data_dir = os.path.join(os.path.dirname(__file__), "..", "cpp", "build") + plot_new_infections([os.path.join(data_dir, "ode_result"), + os.path.join(data_dir, "graph_result")], + 2e3, legendplot=list(["ODE","Graph"])) + compare_all_compartments([os.path.join(data_dir, "ode_result"), + os.path.join(data_dir, "graph_result")], + legendplot=list(["ODE", "Graph"])) From 16a07dad83ca9886f63721176bd3820fcd56ceec Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:44:16 +0200 Subject: [PATCH 15/49] add improved model --- cpp/examples/ode_seir_mobility_improved.cpp | 114 +++++++ .../ode_seir_mobility_improved/CMakeLists.txt | 13 + .../infection_state.h | 26 ++ .../ode_seir_mobility_improved/model.cpp | 10 + cpp/models/ode_seir_mobility_improved/model.h | 96 ++++++ .../ode_seir_mobility_improved/parameters.h | 307 ++++++++++++++++++ .../ode_seir_mobility_improved/regions.h | 26 ++ 7 files changed, 592 insertions(+) create mode 100644 cpp/examples/ode_seir_mobility_improved.cpp create mode 100644 cpp/models/ode_seir_mobility_improved/CMakeLists.txt create mode 100644 cpp/models/ode_seir_mobility_improved/infection_state.h create mode 100644 cpp/models/ode_seir_mobility_improved/model.cpp create mode 100644 cpp/models/ode_seir_mobility_improved/model.h create mode 100644 cpp/models/ode_seir_mobility_improved/parameters.h create mode 100644 cpp/models/ode_seir_mobility_improved/regions.h diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp new file mode 100644 index 0000000000..94a87f63ad --- /dev/null +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -0,0 +1,114 @@ + +#include "memilio/compartments/simulation.h" +#include "memilio/math/euler.h" +#include "memilio/utils/logging.h" +#include "memilio/utils/custom_index_array.h" +#include "memilio/io/mobility_io.h" +#include "models/ode_seir_mobility_improved/infection_state.h" +#include "models/ode_seir_mobility_improved/model.h" +#include "models/ode_seir_mobility_improved/parameters.h" +#include "models/ode_seir_mobility_improved/regions.h" +#include "memilio/io/io.h" +#include "memilio/io/result_io.h" +#include "Eigen/Sparse" + +int main() +{ + mio::set_log_level(mio::LogLevel::debug); + + ScalarType t0 = 0.; + ScalarType tmax = 15.; + ScalarType dt = 1; + + std::vector region_ids = {1001, 1002}; + ScalarType number_regions = region_ids.size(); + ScalarType number_age_groups = 1; + + mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); + + const std::string& mobility_data = ""; + const std::string& trip_chain_data = ""; + + mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; + model.populations[{mio::oseirmobilityimproved::Region(1), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; + model.populations[{mio::oseirmobilityimproved::Region(1), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; + + model.parameters.set>(1.); + + model.parameters.set>(3.); + model.parameters.set>(5.); + + model.parameters.set>(0.); + 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.5, mio::SimulationTime(5)); + + Eigen::SparseMatrix& commuting_strengths = + model.parameters.get>(); + commuting_strengths.insert(0, 0) = 0.95; + commuting_strengths.insert(0, 1) = 0.05; + commuting_strengths.insert(1, 0) = 0.01; + commuting_strengths.insert(1, 1) = 0.99; + + auto& population = model.parameters.get>(); + for (int n = 0; n < commuting_strengths.outerSize(); ++n) { + population[{mio::oseirmobilityimproved::Region(n)}] += + model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + auto x = population[{mio::oseirmobilityimproved::Region(0)}]; + mio::unused(x); + for (Eigen::SparseMatrix::InnerIterator it(commuting_strengths, n); it; ++it) { + auto start_population = model.populations.get_group_total(mio::oseirmobilityimproved::Region(it.row())); + population[{mio::oseirmobilityimproved::Region(it.row())}] -= it.value() * start_population; + x = population[{mio::oseirmobilityimproved::Region(0)}]; + population[{mio::oseirmobilityimproved::Region(it.col())}] += it.value() * start_population; + x = population[{mio::oseirmobilityimproved::Region(0)}]; + } + } + + using DefaultIntegratorCore = + mio::ControlledStepperWrapper; + + std::shared_ptr> integrator = std::make_shared(); + + model.check_constraints(); + + auto result_from_sim = simulate(t0, tmax, dt, model, integrator); + + auto save_result_status = + mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_improved.h5"); + + // bool print_to_terminal = true; + + // result_from_sim.print_table(); + + // if (print_to_terminal) { + + // std::vector vars = {"S", "E", "I", "R"}; + // printf("\n # t"); + // for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { + // for (size_t k = 0; k < (size_t)mio::oseirmobilityimproved::InfectionState::Count; k++) { + // printf(" %s_%d", vars[k].c_str(), (int)i); + // } + // } + + // auto num_points = static_cast(result_from_sim.get_num_time_points()); + // for (size_t i = 0; i < num_points; i++) { + // printf("\n%.14f ", result_from_sim.get_time(i)); + // for (size_t k = 0; k < (size_t)model.parameters.get_num_regions(); k++) { + // for (size_t j = 0; j < (size_t)mio::oseirmobilityimproved::InfectionState::Count; j++) { + // printf(" %.14f", result_from_sim.get_value( + // i)[j + (size_t)mio::oseirmobilityimproved::InfectionState::Count * (int)k]); + // } + // } + // } + // printf("\n"); + // } +} diff --git a/cpp/models/ode_seir_mobility_improved/CMakeLists.txt b/cpp/models/ode_seir_mobility_improved/CMakeLists.txt new file mode 100644 index 0000000000..82261701db --- /dev/null +++ b/cpp/models/ode_seir_mobility_improved/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(ode_seir_mobility_improved + infection_state.h + model.h + model.cpp + parameters.h + regions.h +) +target_link_libraries(ode_seir_mobility_improved PUBLIC memilio) +target_include_directories(ode_seir_mobility_improved PUBLIC + $ + $ +) +target_compile_options(ode_seir_mobility_improved PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/models/ode_seir_mobility_improved/infection_state.h b/cpp/models/ode_seir_mobility_improved/infection_state.h new file mode 100644 index 0000000000..750a6415b7 --- /dev/null +++ b/cpp/models/ode_seir_mobility_improved/infection_state.h @@ -0,0 +1,26 @@ + +#ifndef ODESEIRMOBILITYIMPROVED_INFECTIONSTATE_H +#define ODESEIRMOBILITYIMPROVED_INFECTIONSTATE_H + +namespace mio +{ +namespace oseirmobilityimproved +{ + +/** + * @brief The InfectionState enum describes the possible + * categories for the infectious state of persons + */ +enum class InfectionState +{ + Susceptible, + Exposed, + Infected, + Recovered, + Count +}; + +} // namespace oseirmobilityimproved +} // namespace mio + +#endif // ODESEIR_INFECTIONSTATE_H diff --git a/cpp/models/ode_seir_mobility_improved/model.cpp b/cpp/models/ode_seir_mobility_improved/model.cpp new file mode 100644 index 0000000000..567a8a1e86 --- /dev/null +++ b/cpp/models/ode_seir_mobility_improved/model.cpp @@ -0,0 +1,10 @@ + +#include "ode_seir_mobility_improved/model.h" + +namespace mio +{ +namespace oseirmobilityimproved +{ + +} // namespace oseirmobilityimproved +} // namespace mio diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h new file mode 100644 index 0000000000..90dd9ab9ca --- /dev/null +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -0,0 +1,96 @@ + +#ifndef ODESEIRMOBILITYIMPROVED_MODEL_H +#define ODESEIRMOBILITYIMPROVED_MODEL_H + +#include "memilio/compartments/flow_model.h" +#include "memilio/epidemiology/populations.h" +#include "models/ode_seir_mobility_improved/infection_state.h" +#include "models/ode_seir_mobility_improved/parameters.h" +#include "models/ode_seir_mobility_improved/regions.h" +#include "memilio/epidemiology/age_group.h" + +namespace mio +{ +namespace oseirmobilityimproved +{ + +/******************** + * define the model * + ********************/ + +using Flows = TypeList, + Flow, + Flow>; + +template +class Model : public FlowModel, + Parameters, Flows> +{ + + using Base = + FlowModel, Parameters, Flows>; + +public: + using typename Base::ParameterSet; + using typename Base::Populations; + + Model(int num_regions, int num_agegroups) + : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}), + ParameterSet(Region(num_regions), AgeGroup(num_agegroups))) + { + } + // Einmal über den Vektor und später nochmal über die Regions + + void get_flows(Eigen::Ref> pop, Eigen::Ref> y, FP t, + Eigen::Ref> flows) const override + { + const auto& params = this->parameters; + const auto& population = this->populations; + const auto& commuting_strengths = params.template get>(); + + const Index n_age_groups = reduce_index>(params.get_num_agegroups()); + const Index n_regions = reduce_index>(params.get_num_regions()); + + CustomIndexArray infectives_per_region(n_regions); + for (auto age_i : make_index_range(n_age_groups)) { + for (auto age_j : make_index_range(n_age_groups)) { + for (int n = 0; n < commuting_strengths.outerSize(); ++n) { + infectives_per_region[Region(n)] = + 0.5 * pop[population.get_flat_index({Region(n), age_j, InfectionState::Infected})] * + commuting_strengths.coeff(n, n); + for (Eigen::SparseMatrix::InnerIterator it(commuting_strengths, n); it; ++it) { + infectives_per_region[Region(n)] += + 0.5 * pop[population.get_flat_index({Region(it.row()), age_j, InfectionState::Infected})] * + it.value(); + } + } + double coeffStoI = params.template get>().get_cont_freq_mat().get_matrix_at(t)( + age_i.get(), age_j.get()) * + params.template get>()[age_i]; + for (int n = 0; n < commuting_strengths.outerSize(); ++n) { + for (Eigen::SparseMatrix::InnerIterator it(commuting_strengths, n); it; ++it) { + flows[Base::template get_flat_flow_index( + {Region(it.row()), age_i})] += it.value() * infectives_per_region[Region(it.col())] / + params.template get>()[Region(it.col())]; + } + } + for (auto region : make_index_range(n_regions)) { + flows[Base::template get_flat_flow_index( + {region, age_i})] *= + coeffStoI * y[population.get_flat_index({region, age_j, InfectionState::Susceptible})]; + flows[Base::template get_flat_flow_index( + {region, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; + flows[Base::template get_flat_flow_index( + {region, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region, age_i, InfectionState::Infected})]; + } + } + } + } +}; // namespace oseirmobilityimproved + +} // namespace oseirmobilityimproved +} // namespace mio + +#endif // ODESEIRMOBILITY_MODEL_H diff --git a/cpp/models/ode_seir_mobility_improved/parameters.h b/cpp/models/ode_seir_mobility_improved/parameters.h new file mode 100644 index 0000000000..817881108d --- /dev/null +++ b/cpp/models/ode_seir_mobility_improved/parameters.h @@ -0,0 +1,307 @@ + +#ifndef SEIRMOBILITY_PARAMETERS_H +#define SEIRMOBILITY_PARAMETERS_H + +#include "memilio/epidemiology/uncertain_matrix.h" +#include "memilio/utils/uncertain_value.h" +#include "memilio/epidemiology/age_group.h" +#include "memilio/utils/parameter_set.h" +#include "memilio/utils/custom_index_array.h" +#include "models/ode_seir_mobility_improved/regions.h" +#include "Eigen/Sparse" + +#include + +namespace mio +{ +namespace oseirmobilityimproved +{ + +/**************************************************** + * Define Parameters of the SEIR model with mobility * + ****************************************************/ + +/** + * @brief Probability of getting infected from a contact. + */ +template +struct TransmissionProbabilityOnContact { + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) + { + return Type(size, 1.0); + } + static std::string name() + { + return "TransmissionProbabilityOnContact"; + } +}; + +/** + * @brief the latent time in day unit + */ +template +struct TimeExposed { + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) + { + return Type(size, 5.2); + } + static std::string name() + { + return "TimeExposed"; + } +}; + +/** + * @brief The infectious time in day unit. + */ +template +struct TimeInfected { + using Type = CustomIndexArray, AgeGroup>; + static Type get_default(Region, AgeGroup size) + { + return Type(size, 6.0); + } + static std::string name() + { + return "TimeInfected"; + } +}; + +/** + * @brief The contact patterns within the society are modelled using a ContactMatrix. + */ +template +struct ContactPatterns { + using Type = UncertainContactMatrix; + static Type get_default(Region, AgeGroup size) + { + return Type(1, static_cast((size_t)size)); + } + static std::string name() + { + return "ContactPatterns"; + } +}; + +/** + * @brief The ratio that regulates the infections during commuting. +*/ +template +struct ImpactTransmissionDuringCommuting { + using Type = UncertainValue; + static Type get_default(Region, AgeGroup) + { + return Type(0.); + } + static std::string name() + { + return "ImpactTransmissionDuringCommuting"; + } +}; + +/** + * @brief The Region%s that a person crosses when travelling from one Region to another. +*/ +struct PathIntersections { + using Type = CustomIndexArray, Region, Region>; + static Type get_default(Region, AgeGroup) + { + return Type({Region(0), Region(0)}); + } + static std::string name() + { + return "PathIntersections"; + } +}; + +/** + * @brief The commuting weights are modelled using a SparseMatrix. + */ +template +struct CommutingStrengths { + using Type = Eigen::SparseMatrix; + static Type get_default(Region size, AgeGroup) + { + return Type(static_cast((size_t)size), static_cast((size_t)size)); + } + static std::string name() + { + return "CommutingStrengths"; + } +}; + +/** + * @brief The Region%s that a person crosses when travelling from one Region to another. + */ +template +struct PopulationSizes { + using Type = CustomIndexArray; + static Type get_default(Region size, AgeGroup) + { + return Type(size, 0.); + } + static std::string name() + { + return "PopulationSizes"; + } +}; + +template +using ParametersBase = + ParameterSet, TimeExposed, TimeInfected, ContactPatterns, + ImpactTransmissionDuringCommuting, PathIntersections, CommutingStrengths, PopulationSizes>; + +/** + * @brief Parameters of SEIR model. + */ +template +class Parameters : public ParametersBase +{ +public: + Parameters(Region num_regions, AgeGroup num_agegroups) + : ParametersBase(num_regions, num_agegroups) + , m_num_regions{num_regions} + , m_num_agegroups(num_agegroups) + { + } + + Region get_num_regions() const + { + return m_num_regions; + } + + AgeGroup get_num_agegroups() const + { + return m_num_agegroups; + } + + /** + * @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]. + * + * Attention: This function should be used with care. It is necessary for some test problems to run through quickly, + * but in a manual execution of an example, check_constraints() may be preferred. Note that the apply_constraints() + * function can and will not set Parameters to meaningful values in an epidemiological or virological context, + * as all models are designed to be transferable to multiple diseases. Consequently, only acceptable + * (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. + */ + bool apply_constraints() + { + double tol_times = 1e-1; + + int corrected = false; + + for (auto i = AgeGroup(0); i < AgeGroup(m_num_agegroups); i++) { + if (this->template get>()[i] < 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->template get>()[i], tol_times); + this->template get>()[i] = tol_times; + corrected = true; + } + if (this->template get>()[i] < 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->template get>()[i], tol_times); + this->template get>()[i] = tol_times; + corrected = true; + } + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { + log_warning( + "Constraint check: Parameter TransmissionProbabilityOnContact changed from {:0.4f} to {:d} ", + this->template get>()[i], 0.0); + this->template get>() = 0.0; + corrected = true; + } + } + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_warning("Constraint check: Parameter ImpactTransmissionDuringCommuting changed from {:.4f} to {:.4f}.", + 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 + * if constraints are not satisfied. + * @return Returns true if one constraint is not satisfied, otherwise false. + */ + bool check_constraints() const + { + double tol_times = 1e-1; + + for (auto i = AgeGroup(0); i < AgeGroup(m_num_agegroups); i++) { + if (this->template get>()[i] < tol_times) { + log_error( + "Constraint check: Parameter TimeExposed {:.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->template get>()[i], 0.0); + return true; + } + if (this->template get>()[i] < 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->template get>()[i], 0.0); + return true; + } + if (this->template get>()[i] < 0.0 || + this->template get>()[i] > 1.0) { + log_error("Constraint check: Parameter TransmissionProbabilityOnContact {:.4f} smaller {:.4f} or " + "greater {:.4f}", + this->template get>()[i], 0.0, 1.0); + return true; + } + } + if (this->template get>() < 0.0 || + this->template get>() > 1.0) { + log_error( + "Constraint check: Parameter ImpactTransmissionDuringCommuting {:.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)) //TODO: Adjust + // { + // } + +public: + /** + * deserialize an object of this class. + * @see mio::deserialize + */ + template + static IOResult deserialize(IOContext& io) + { + BOOST_OUTCOME_TRY(auto&& base, ParametersBase::deserialize(io)); + return success(Parameters(std::move(base))); + } + +private: + Region m_num_regions; + AgeGroup m_num_agegroups; +}; + +} // namespace oseirmobilityimproved +} // namespace mio + +#endif // SEIR_PARAMETERS_H diff --git a/cpp/models/ode_seir_mobility_improved/regions.h b/cpp/models/ode_seir_mobility_improved/regions.h new file mode 100644 index 0000000000..f9feca907f --- /dev/null +++ b/cpp/models/ode_seir_mobility_improved/regions.h @@ -0,0 +1,26 @@ + +#ifndef ODESEIRMOBILITYIMPROVED_REGIONS_H +#define ODESEIRMOBILITYIMPROVED_REGIONS_H + +#include "memilio/utils/index.h" + +namespace mio +{ +namespace oseirmobilityimproved +{ + +/** + * @brief The AgeGroup struct is used as a dynamically + * sized tag for all age dependent categories + */ +struct Region : public Index { + Region(size_t val) + : Index(val) + { + } +}; + +} // namespace oseirmobilityimproved +} // namespace mio + +#endif From 23196bc7d2bd792da5f2ed23986857129bd3e184 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:52:56 +0200 Subject: [PATCH 16/49] changes for comparing simulations --- cpp/CMakeLists.txt | 2 ++ cpp/examples/CMakeLists.txt | 8 ++++++++ cpp/examples/graph.cpp | 4 ++-- cpp/examples/ode_seir_mobility.cpp | 13 +++++++------ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index b832cabfab..ed4372290a 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -129,6 +129,8 @@ if(MEMILIO_BUILD_MODELS) add_subdirectory(models/ode_seair) add_subdirectory(models/ode_sir) add_subdirectory(models/ode_sir_mobility) + # add_subdirectory(models/ode_seir_mobility_massaction) + add_subdirectory(models/ode_seir_mobility_improved) add_subdirectory(models/ode_seir_mobility) add_subdirectory(models/sde_sir) add_subdirectory(models/sde_sirs) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 6a12daf6ef..8dc1089d3a 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -38,6 +38,14 @@ add_executable(ode_seir_mobility_example ode_seir_mobility.cpp) target_link_libraries(ode_seir_mobility_example PRIVATE memilio ode_seir_mobility) target_compile_options(ode_seir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +# add_executable(ode_seir_mobility_example_massaction ode_seir_mobility_massaction.cpp) +# target_link_libraries(ode_seir_mobility_example_massaction PRIVATE memilio ode_seir_mobility_massaction) +# target_compile_options(ode_seir_mobility_example_massaction PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + +add_executable(ode_seir_mobility_example_improved ode_seir_mobility_improved.cpp) +target_link_libraries(ode_seir_mobility_example_improved PRIVATE memilio ode_seir_mobility_improved) +target_compile_options(ode_seir_mobility_example_improved PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(sde_sirs_example sde_sirs.cpp) target_link_libraries(sde_sirs_example PRIVATE memilio sde_sirs) target_compile_options(sde_sirs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/examples/graph.cpp b/cpp/examples/graph.cpp index 3b8767a686..bf15588032 100644 --- a/cpp/examples/graph.cpp +++ b/cpp/examples/graph.cpp @@ -27,7 +27,7 @@ int main() { const auto t0 = 0.; - const auto tmax = 10.; + const auto tmax = 15.; const auto dt = 0.5; //time step of migration, daily migration every second step mio::oseir::Model<> model(1); @@ -61,7 +61,7 @@ int main() mio::Graph>>, mio::MigrationEdge<>> 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::oseir::InfectionState::Count, 0.01)); + g.add_edge(0, 1, Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, 0.05)); g.add_edge(1, 0, Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, 0.01)); auto sim = mio::make_migration_sim(t0, dt, std::move(g)); diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp index cfba2c1e8f..d2e20185cf 100644 --- a/cpp/examples/ode_seir_mobility.cpp +++ b/cpp/examples/ode_seir_mobility.cpp @@ -79,8 +79,7 @@ mio::IOResult preprocess(const std::string& filename, mio::oseirmobility:: } std::sort(mobility_paths[i][j].begin(), mobility_paths[i][j].end()); std::vector intersection_int; - std::vector intersection_region(intersection_int.size(), - mio::oseirmobility::Region(0)); + std::vector intersection_region; for (size_t k = 0; k < n_regions; k++) { if (k == i || k == j) { continue; @@ -141,8 +140,8 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0.; - ScalarType tmax = 10.; - ScalarType dt = 1; + ScalarType tmax = 15.; + ScalarType dt = 0.5; std::vector region_ids = {1001, 1002}; ScalarType number_regions = region_ids.size(); @@ -163,6 +162,8 @@ int main() model.populations[{mio::oseirmobility::Region(1), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Susceptible}] = 10000; + // auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); + model.parameters.set>(1.); model.parameters.set>(3.); @@ -175,7 +176,7 @@ int main() // contact_matrix[0].add_damping(0.5, mio::SimulationTime(5)); model.parameters.get().push_back( - {mio::oseirmobility::Region(0), mio::oseirmobility::Region(1), 0.01}); + {mio::oseirmobility::Region(0), mio::oseirmobility::Region(1), 0.05}); model.parameters.get().push_back( {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.01}); @@ -189,7 +190,7 @@ int main() auto result_from_sim = simulate(t0, tmax, dt, model, integrator); auto save_result_status = - mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result.h5"); + mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_standard.h5"); // bool print_to_terminal = true; From bfc2562ddcd7a433db92e291e5a95bf1aee9b675 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:00:00 +0200 Subject: [PATCH 17/49] adjust plot file --- tools/plot_results_mobilitymodels.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/plot_results_mobilitymodels.py b/tools/plot_results_mobilitymodels.py index 4eb997c724..47a853882b 100644 --- a/tools/plot_results_mobilitymodels.py +++ b/tools/plot_results_mobilitymodels.py @@ -11,7 +11,8 @@ color_dict = {0: '#1f77b4', 1:'#2ca02c' } -linestyle_dict = {"ODE": 'solid', +linestyle_dict = {"ODE SI": 'dashed', + "ODE Improved": 'dotted', "Graph": 'dashdot' } @@ -156,9 +157,11 @@ def plot_new_infections(files, ylim, legendplot, filename_plot="compare_new_infe if __name__ == '__main__': data_dir = os.path.join(os.path.dirname(__file__), "..", "cpp", "build") - plot_new_infections([os.path.join(data_dir, "ode_result"), + plot_new_infections([os.path.join(data_dir, "ode_result_standard"), + os.path.join(data_dir, "ode_result_improved"), os.path.join(data_dir, "graph_result")], - 2e3, legendplot=list(["ODE","Graph"])) - compare_all_compartments([os.path.join(data_dir, "ode_result"), + 2e3, legendplot=list(["ODE SI", "ODE Improved","Graph"])) + compare_all_compartments([os.path.join(data_dir, "ode_result_standard"), + os.path.join(data_dir, "ode_result_improved"), os.path.join(data_dir, "graph_result")], - legendplot=list(["ODE", "Graph"])) + legendplot=list(["ODE SI", "ODE Improved","Graph"])) From c6b56e965f7b0b98f49cbf78448ca614d0d3b010 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:21:37 +0200 Subject: [PATCH 18/49] reformat py file --- tools/plot_results_mobilitymodels.py | 126 +++++++++++++++++---------- 1 file changed, 81 insertions(+), 45 deletions(-) diff --git a/tools/plot_results_mobilitymodels.py b/tools/plot_results_mobilitymodels.py index 47a853882b..756454acd3 100644 --- a/tools/plot_results_mobilitymodels.py +++ b/tools/plot_results_mobilitymodels.py @@ -7,17 +7,22 @@ # Define compartments. secir_dict = {0: 'Susceptible', 1: 'Exposed', 2: 'Infected', 3: 'Recovered'} -# Define color and style to be used while plotting for different models to make plots consistent. +# Define color and style to be used while plotting for different models to +# make plots consistent. color_dict = {0: '#1f77b4', - 1:'#2ca02c' + 1: '#2ca02c' } linestyle_dict = {"ODE SI": 'dashed', "ODE Improved": 'dotted', "Graph": 'dashdot' } -def compare_all_compartments(files, legendplot, filename_plot="compare_compartments"): - + +def compare_all_compartments( + files, + legendplot, + filename_plot="compare_compartments"): + fig, axs = plt.subplots( 2, 2, sharex='all', num=filename_plot, tight_layout=False) @@ -32,55 +37,67 @@ def compare_all_compartments(files, legendplot, filename_plot="compare_compartme data = h5file[list(h5file.keys())[region]] dates = data['Time'][:] - number_regions = len(list(h5file[list(h5file.keys())[region]].keys())) - 2 + number_regions = len( + list(h5file[list(h5file.keys())[region]].keys())) - 2 for region in range(number_regions): - total = data['Group'+str(region+1)][:, :] + total = data['Group' + str(region + 1)][:, :] if (total.shape[1] != 4): - raise gd.DataError("Expected a different number of compartments.") + raise gd.DataError( + "Expected a different number of compartments.") # Plot result. if legendplot[file] in linestyle_dict: for i in range(4): - axs[int(i/2), i % 2].plot(dates, - total[:, i], label=legendplot[file] +" Region "+ str(region), linewidth=1.2, - linestyle=linestyle_dict[legendplot[file]], - color=color_dict[region]) + axs[int(i / 2), + i % 2].plot(dates, + total[:, + i], + label=legendplot[file] + " Region " + str(region), + linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], + color=color_dict[region]) else: for i in range(4): - axs[int(i/2), i % 2].plot(dates, - total[:, i], label=legendplot[file], linewidth=1.2) + axs[int(i / 2), i % 2].plot(dates, total[:, i], + label=legendplot[file], linewidth=1.2) else: data = h5file[list(h5file.keys())[region]] dates = data['Time'][:] - # As there should be only one Group, total is the simulation result. + # As there should be only one Group, total is the simulation + # result. total = data['Total'][:, :] if (total.shape[1] != 4): - raise gd.DataError("Expected a different number of compartments.") + raise gd.DataError( + "Expected a different number of compartments.") # Plot result. if legendplot[file] in linestyle_dict: for i in range(4): - axs[int(i/2), i % 2].plot(dates, - total[:, i], label=legendplot[file] +" Region "+ str(region), linewidth=1.2, - linestyle=linestyle_dict[legendplot[file]], - color=color_dict[region]) + axs[int(i / 2), + i % 2].plot(dates, + total[:, + i], + label=legendplot[file] + " Region " + str(region), + linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], + color=color_dict[region]) else: for i in range(4): - axs[int(i/2), i % 2].plot(dates, - total[:, i], label=legendplot[file], linewidth=1.2) + axs[int(i / 2), i % 2].plot(dates, total[:, i], + label=legendplot[file], linewidth=1.2) h5file.close() # Define some characteristics of the plot. for i in range(4): - axs[int(i/2), i % 2].set_title(secir_dict[i], fontsize=8) - axs[int(i/2), i % 2].set_xlim(left=0, right=dates[-1]) - axs[int(i/2), i % 2].grid(True, linestyle='--') - axs[int(i/2), i % 2].tick_params(axis='y', labelsize=7) - axs[int(i/2), i % 2].tick_params(axis='x', labelsize=7) + axs[int(i / 2), i % 2].set_title(secir_dict[i], fontsize=8) + axs[int(i / 2), i % 2].set_xlim(left=0, right=dates[-1]) + axs[int(i / 2), i % 2].grid(True, linestyle='--') + axs[int(i / 2), i % 2].tick_params(axis='y', labelsize=7) + axs[int(i / 2), i % 2].tick_params(axis='x', labelsize=7) # axs[int(i/2), i % 2].xaxis.set_ticks(np.arange(0, dates[-1]+1, 5)) fig.supxlabel('Time (in days)', fontsize=9) lines, labels = axs[0, 0].get_legend_handles_labels() - lgd = fig.legend(lines, labels, ncol=len(legendplot), loc='outside lower center', + lgd = fig.legend(lines, labels, ncol=len(legendplot), loc='outside lower center', fontsize=10, bbox_to_anchor=(0.5, - 0.06), bbox_transform=fig.transFigure) plt.tight_layout(pad=0, w_pad=0.5, h_pad=0.1) @@ -89,11 +106,15 @@ def compare_all_compartments(files, legendplot, filename_plot="compare_compartme # Save result. if not os.path.isdir('Plots'): os.makedirs('Plots') - fig.savefig('Plots/'+filename_plot+'.png', + fig.savefig('Plots/' + filename_plot + '.png', bbox_extra_artists=(lgd,), bbox_inches='tight', dpi=500) -def plot_new_infections(files, ylim, legendplot, filename_plot="compare_new_infections"): +def plot_new_infections( + files, + ylim, + legendplot, + filename_plot="compare_new_infections"): plt.figure(filename_plot) @@ -108,32 +129,42 @@ def plot_new_infections(files, ylim, legendplot, filename_plot="compare_new_infe data = h5file[list(h5file.keys())[region]] dates = data['Time'][:] - number_regions = len(list(h5file[list(h5file.keys())[region]].keys())) - 2 + number_regions = len( + list(h5file[list(h5file.keys())[region]].keys())) - 2 for region_ in range(number_regions): - total = data['Group'+str(region_+1)][:, :] + total = data['Group' + str(region_ + 1)][:, :] if (total.shape[1] != 4): raise gd.DataError( "Expected a different number of compartments.") - incidence = (total[:-1, 0]-total[1:, 0])/(dates[1:]-dates[:-1]) + incidence = (total[:-1, 0] - total[1:, 0] + ) / (dates[1:] - dates[:-1]) # Plot result. if legendplot[file] in linestyle_dict: - plt.plot(dates[1:], incidence, linewidth=1.2, - linestyle=linestyle_dict[legendplot[file]], color=color_dict[region_]) + plt.plot(dates[1:], + incidence, + linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], + color=color_dict[region_]) else: plt.plot(dates[1:], incidence, linewidth=1.2) - else: + else: data = h5file[list(h5file.keys())[region]] dates = data['Time'][:] - # As there should be only one Group, total is the simulation result. + # As there should be only one Group, total is the simulation + # result. total = data['Total'][:, :] if (total.shape[1] != 4): raise gd.DataError( "Expected a different number of compartments.") - incidence = (total[:-1, 0]-total[1:, 0])/(dates[1:]-dates[:-1]) + incidence = (total[:-1, 0] - total[1:, 0]) / \ + (dates[1:] - dates[:-1]) # Plot result. if legendplot[file] in linestyle_dict: - plt.plot(dates[1:], incidence, linewidth=1.2, - linestyle=linestyle_dict[legendplot[file]], color=color_dict[region]) + plt.plot(dates[1:], + incidence, + linewidth=1.2, + linestyle=linestyle_dict[legendplot[file]], + color=color_dict[region]) else: plt.plot(dates[1:], incidence, linewidth=1.2) @@ -152,16 +183,21 @@ def plot_new_infections(files, ylim, legendplot, filename_plot="compare_new_infe # Save result. if not os.path.isdir('Plots'): os.makedirs('Plots') - plt.savefig('Plots/'+filename_plot+'.png', bbox_inches='tight', dpi=500) + plt.savefig( + 'Plots/' + + filename_plot + + '.png', + bbox_inches='tight', + dpi=500) if __name__ == '__main__': data_dir = os.path.join(os.path.dirname(__file__), "..", "cpp", "build") plot_new_infections([os.path.join(data_dir, "ode_result_standard"), - os.path.join(data_dir, "ode_result_improved"), + os.path.join(data_dir, "ode_result_improved"), os.path.join(data_dir, "graph_result")], - 2e3, legendplot=list(["ODE SI", "ODE Improved","Graph"])) + 2e3, legendplot=list(["ODE SI", "ODE Improved", "Graph"])) compare_all_compartments([os.path.join(data_dir, "ode_result_standard"), - os.path.join(data_dir, "ode_result_improved"), - os.path.join(data_dir, "graph_result")], - legendplot=list(["ODE SI", "ODE Improved","Graph"])) + os.path.join(data_dir, "ode_result_improved"), + os.path.join(data_dir, "graph_result")], + legendplot=list(["ODE SI", "ODE Improved", "Graph"])) From 5e51d696b464ee5ebeea82f1b9dc049a7948842b Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:26:59 +0200 Subject: [PATCH 19/49] py file --- tools/plot_results_mobilitymodels.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/plot_results_mobilitymodels.py b/tools/plot_results_mobilitymodels.py index 756454acd3..5290c58c80 100644 --- a/tools/plot_results_mobilitymodels.py +++ b/tools/plot_results_mobilitymodels.py @@ -51,7 +51,8 @@ def compare_all_compartments( i % 2].plot(dates, total[:, i], - label=legendplot[file] + " Region " + str(region), + label=legendplot[file] + + " Region " + str(region), linewidth=1.2, linestyle=linestyle_dict[legendplot[file]], color=color_dict[region]) @@ -75,7 +76,8 @@ def compare_all_compartments( i % 2].plot(dates, total[:, i], - label=legendplot[file] + " Region " + str(region), + label=legendplot[file] + + " Region " + str(region), linewidth=1.2, linestyle=linestyle_dict[legendplot[file]], color=color_dict[region]) From 0eff4d157805acca1d042675429bb7b72a4f38b9 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:28:15 +0200 Subject: [PATCH 20/49] py file --- tools/plot_results_mobilitymodels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/plot_results_mobilitymodels.py b/tools/plot_results_mobilitymodels.py index 5290c58c80..0426ca7747 100644 --- a/tools/plot_results_mobilitymodels.py +++ b/tools/plot_results_mobilitymodels.py @@ -51,7 +51,7 @@ def compare_all_compartments( i % 2].plot(dates, total[:, i], - label=legendplot[file] + + label=legendplot[file] + " Region " + str(region), linewidth=1.2, linestyle=linestyle_dict[legendplot[file]], @@ -76,7 +76,7 @@ def compare_all_compartments( i % 2].plot(dates, total[:, i], - label=legendplot[file] + + label=legendplot[file] + " Region " + str(region), linewidth=1.2, linestyle=linestyle_dict[legendplot[file]], From 3d3b0076df30c371f241936e58ef286b1e336b63 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:56:31 +0200 Subject: [PATCH 21/49] change commuting strengths to contact matrix and implement indicator function --- cpp/examples/graph.cpp | 2 +- cpp/examples/ode_seir_mobility_improved.cpp | 33 ++++++------- cpp/models/ode_seir_mobility_improved/model.h | 47 ++++++++++--------- .../ode_seir_mobility_improved/parameters.h | 8 ++-- 4 files changed, 46 insertions(+), 44 deletions(-) diff --git a/cpp/examples/graph.cpp b/cpp/examples/graph.cpp index b465a5b412..11c43fa4f4 100644 --- a/cpp/examples/graph.cpp +++ b/cpp/examples/graph.cpp @@ -28,7 +28,7 @@ int main() { const auto t0 = 0.; const auto tmax = 15.; - const auto dt = 0.5; //time step of migration, daily migration every second step + const auto dt = 0.5; //time step of mobility, daily mobility every second step mio::oseir::Model<> model(1); diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 94a87f63ad..339a10ab9c 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -18,7 +18,7 @@ int main() ScalarType t0 = 0.; ScalarType tmax = 15.; - ScalarType dt = 1; + ScalarType dt = 0.5; std::vector region_ids = {1001, 1002}; ScalarType number_regions = region_ids.size(); @@ -48,31 +48,28 @@ int main() 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.5, mio::SimulationTime(5)); - Eigen::SparseMatrix& commuting_strengths = - model.parameters.get>(); - commuting_strengths.insert(0, 0) = 0.95; - commuting_strengths.insert(0, 1) = 0.05; - commuting_strengths.insert(1, 0) = 0.01; - commuting_strengths.insert(1, 1) = 0.99; + mio::ContactMatrixGroup& commuting_strengths = + model.parameters.get>().get_cont_freq_mat(); + Eigen::MatrixXd values(2, 2); + values(0, 0) = 0.95; + values(0, 1) = 0.05; + values(1, 0) = 0.01; + values(1, 1) = 0.99; + commuting_strengths[0].get_baseline() = values; auto& population = model.parameters.get>(); - for (int n = 0; n < commuting_strengths.outerSize(); ++n) { + for (int n = 0; n < number_regions; ++n) { population[{mio::oseirmobilityimproved::Region(n)}] += model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); - auto x = population[{mio::oseirmobilityimproved::Region(0)}]; - mio::unused(x); - for (Eigen::SparseMatrix::InnerIterator it(commuting_strengths, n); it; ++it) { - auto start_population = model.populations.get_group_total(mio::oseirmobilityimproved::Region(it.row())); - population[{mio::oseirmobilityimproved::Region(it.row())}] -= it.value() * start_population; - x = population[{mio::oseirmobilityimproved::Region(0)}]; - population[{mio::oseirmobilityimproved::Region(it.col())}] += it.value() * start_population; - x = population[{mio::oseirmobilityimproved::Region(0)}]; + for (int m = 0; m < number_regions; ++m) { + population[{mio::oseirmobilityimproved::Region(n)}] -= + values(n, m) * model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + population[{mio::oseirmobilityimproved::Region(m)}] += + values(n, m) * model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); } } - using DefaultIntegratorCore = mio::ControlledStepperWrapper; diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index 90dd9ab9ca..6ad066a626 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -39,41 +39,46 @@ class Model : public FlowModel> pop, Eigen::Ref> y, FP t, Eigen::Ref> flows) const override { - const auto& params = this->parameters; - const auto& population = this->populations; - const auto& commuting_strengths = params.template get>(); - + const auto& params = this->parameters; + const auto& population = this->populations; + const auto& commuting_strengths = + params.template get>().get_cont_freq_mat().get_matrix_at(t); const Index n_age_groups = reduce_index>(params.get_num_agegroups()); const Index n_regions = reduce_index>(params.get_num_regions()); - CustomIndexArray infectives_per_region(n_regions); for (auto age_i : make_index_range(n_age_groups)) { for (auto age_j : make_index_range(n_age_groups)) { - for (int n = 0; n < commuting_strengths.outerSize(); ++n) { - infectives_per_region[Region(n)] = - 0.5 * pop[population.get_flat_index({Region(n), age_j, InfectionState::Infected})] * - commuting_strengths.coeff(n, n); - for (Eigen::SparseMatrix::InnerIterator it(commuting_strengths, n); it; ++it) { - infectives_per_region[Region(n)] += - 0.5 * pop[population.get_flat_index({Region(it.row()), age_j, InfectionState::Infected})] * - it.value(); + Eigen::VectorXd infectives_per_region = Eigen::VectorXd::Zero((size_t)n_regions); + for (auto region_n : make_index_range(n_regions)) { + if (fmod(t, 1.) < 0.5) { // fmod = modulo for doubles + infectives_per_region(region_n.get()) = + commuting_strengths(region_n.get(), region_n.get()) * + pop[population.get_flat_index({region_n, age_j, InfectionState::Infected})]; + } + else { + for (auto region_m : make_index_range(n_regions)) { + infectives_per_region(region_n.get()) += + commuting_strengths(region_m.get(), region_n.get()) * + pop[population.get_flat_index({region_m, age_j, InfectionState::Infected})]; + } } } - double coeffStoI = params.template get>().get_cont_freq_mat().get_matrix_at(t)( - age_i.get(), age_j.get()) * - params.template get>()[age_i]; - for (int n = 0; n < commuting_strengths.outerSize(); ++n) { - for (Eigen::SparseMatrix::InnerIterator it(commuting_strengths, n); it; ++it) { + for (auto region_n : make_index_range(n_regions)) { + for (auto region_m : make_index_range(n_regions)) { flows[Base::template get_flat_flow_index( - {Region(it.row()), age_i})] += it.value() * infectives_per_region[Region(it.col())] / - params.template get>()[Region(it.col())]; + {region_n, age_i})] += commuting_strengths(region_n.get(), region_m.get()) * + infectives_per_region(region_m.get()) / + params.template get>()[region_m]; } } + + double coeffStoI = params.template get>().get_cont_freq_mat().get_matrix_at(t)( + age_i.get(), age_j.get()) * + params.template get>()[age_i]; for (auto region : make_index_range(n_regions)) { flows[Base::template get_flat_flow_index( {region, age_i})] *= diff --git a/cpp/models/ode_seir_mobility_improved/parameters.h b/cpp/models/ode_seir_mobility_improved/parameters.h index 817881108d..05ac04ae5d 100644 --- a/cpp/models/ode_seir_mobility_improved/parameters.h +++ b/cpp/models/ode_seir_mobility_improved/parameters.h @@ -117,14 +117,14 @@ struct PathIntersections { }; /** - * @brief The commuting weights are modelled using a SparseMatrix. - */ + * @brief The contact patterns within different Region%s are modelled using a ContactMatrix. + */ template struct CommutingStrengths { - using Type = Eigen::SparseMatrix; + using Type = UncertainContactMatrix; static Type get_default(Region size, AgeGroup) { - return Type(static_cast((size_t)size), static_cast((size_t)size)); + return Type(1, static_cast((size_t)size)); } static std::string name() { From 9f0fba713a639d095da9f5b71695d390e44e170c Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:39:43 +0200 Subject: [PATCH 22/49] return to factor 0.5 --- cpp/examples/ode_seir_mobility_improved.cpp | 12 ++++++------ cpp/models/ode_seir_mobility_improved/model.h | 15 ++++----------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 339a10ab9c..a550219562 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -53,10 +53,10 @@ int main() mio::ContactMatrixGroup& commuting_strengths = model.parameters.get>().get_cont_freq_mat(); Eigen::MatrixXd values(2, 2); - values(0, 0) = 0.95; - values(0, 1) = 0.05; - values(1, 0) = 0.01; - values(1, 1) = 0.99; + values(0, 0) = 0.975; + values(0, 1) = 0.025; + values(1, 0) = 0.005; + values(1, 1) = 0.995; commuting_strengths[0].get_baseline() = values; auto& population = model.parameters.get>(); @@ -79,8 +79,8 @@ int main() auto result_from_sim = simulate(t0, tmax, dt, model, integrator); - auto save_result_status = - mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_improved.h5"); + auto save_result_status = mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, + "ode_result_improved_factor.h5"); // bool print_to_terminal = true; diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index 6ad066a626..8b07be34d2 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -54,17 +54,10 @@ class Model : public FlowModel Date: Tue, 8 Oct 2024 11:46:21 +0200 Subject: [PATCH 23/49] read in data and time measurement 400 counties --- cpp/examples/ode_seir_mobility_improved.cpp | 82 +++++++++++++++------ 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index a550219562..2e070998d7 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -10,7 +10,36 @@ #include "models/ode_seir_mobility_improved/regions.h" #include "memilio/io/io.h" #include "memilio/io/result_io.h" -#include "Eigen/Sparse" + +#include + +template +mio::IOResult set_mobility_weights(const std::string& mobility_data, mio::oseirmobilityimproved::Model& model, + size_t number_regions) +{ + // mobility between nodes + BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, + mio::read_mobility_plain(mobility_data + "mobility/" + "commuter_migration_scaled.txt")); + if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || + mobility_data_commuter.cols() != Eigen::Index(number_regions)) { + return mio::failure(mio::StatusCode::InvalidValue, + "Mobility matrices do not have the correct size. You may need to run " + "transformMobilitydata.py from pycode memilio epidata package."); + } + + for (auto age = mio::AgeGroup(0); age < model.parameters.get_num_agegroups(); age++) { + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + auto population_i = model.populations.get_group_total(mio::oseirmobilityimproved::Region(county_idx_i)); + mobility_data_commuter.row(county_idx_i) /= 2 * population_i; + mobility_data_commuter(county_idx_i, county_idx_i) = + 1 - mobility_data_commuter.rowwise().sum()(county_idx_i); + } + model.parameters.template get>() + .get_cont_freq_mat()[0] + .get_baseline() = mobility_data_commuter; + } + return mio::success(); +} int main() { @@ -20,24 +49,25 @@ int main() ScalarType tmax = 15.; ScalarType dt = 0.5; - std::vector region_ids = {1001, 1002}; - ScalarType number_regions = region_ids.size(); + // std::vector region_ids = {1001, 1002}; + ScalarType number_regions = 400; ScalarType number_age_groups = 1; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - const std::string& mobility_data = ""; - const std::string& trip_chain_data = ""; + const std::string& mobility_data = ""; mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; + mio::oseirmobilityimproved::InfectionState::Exposed}] = 100; model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; - model.populations[{mio::oseirmobilityimproved::Region(1), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; - model.populations[{mio::oseirmobilityimproved::Region(1), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 99900; + for (int i = 1; i < number_regions; i++) { + model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; + model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 100000; + } model.parameters.set>(1.); @@ -50,14 +80,9 @@ int main() contact_matrix[0].get_baseline().setConstant(2.7); // contact_matrix[0].add_damping(0.5, mio::SimulationTime(5)); + auto result_preprocess = set_mobility_weights(mobility_data, model, number_regions); mio::ContactMatrixGroup& commuting_strengths = model.parameters.get>().get_cont_freq_mat(); - Eigen::MatrixXd values(2, 2); - values(0, 0) = 0.975; - values(0, 1) = 0.025; - values(1, 0) = 0.005; - values(1, 1) = 0.995; - commuting_strengths[0].get_baseline() = values; auto& population = model.parameters.get>(); for (int n = 0; n < number_regions; ++n) { @@ -65,22 +90,31 @@ int main() model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); for (int m = 0; m < number_regions; ++m) { population[{mio::oseirmobilityimproved::Region(n)}] -= - values(n, m) * model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + commuting_strengths[0].get_baseline()(n, m) * + model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); population[{mio::oseirmobilityimproved::Region(m)}] += - values(n, m) * model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + commuting_strengths[0].get_baseline()(n, m) * + model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); } } - using DefaultIntegratorCore = - mio::ControlledStepperWrapper; + // using DefaultIntegratorCore = + // mio::ControlledStepperWrapper; - std::shared_ptr> integrator = std::make_shared(); + std::shared_ptr> integrator = std::make_shared>(); model.check_constraints(); + printf("Start Simulation\n"); + auto t1 = std::chrono::high_resolution_clock::now(); auto result_from_sim = simulate(t0, tmax, dt, model, integrator); + auto t2 = std::chrono::high_resolution_clock::now(); + + std::chrono::duration ms_double = t2 - t1; + + printf("Runtime: %f\n", ms_double.count()); - auto save_result_status = mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, - "ode_result_improved_factor.h5"); + // auto save_result_status = + // mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); // bool print_to_terminal = true; From 06950d962e009a815c21fd7097014b36b6c5a9d1 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:19:47 +0200 Subject: [PATCH 24/49] small corrections model --- cpp/examples/ode_seir_mobility_improved.cpp | 14 ++++++-------- cpp/models/ode_seir_mobility_improved/model.h | 7 +++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 2e070998d7..06bd82ef49 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -19,7 +19,7 @@ mio::IOResult set_mobility_weights(const std::string& mobility_data, mio:: { // mobility between nodes BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, - mio::read_mobility_plain(mobility_data + "mobility/" + "commuter_migration_scaled.txt")); + mio::read_mobility_plain(mobility_data + "/mobility" + "/commuter_migration_test.txt")); if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || mobility_data_commuter.cols() != Eigen::Index(number_regions)) { return mio::failure(mio::StatusCode::InvalidValue, @@ -55,7 +55,7 @@ int main() mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - const std::string& mobility_data = ""; + const std::string& mobility_data = "/home/gers_ca/code/memilio/data"; mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), @@ -86,15 +86,13 @@ int main() auto& population = model.parameters.get>(); for (int n = 0; n < number_regions; ++n) { - population[{mio::oseirmobilityimproved::Region(n)}] += - model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + auto population_n = model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + population[{mio::oseirmobilityimproved::Region(n)}] += population_n; for (int m = 0; m < number_regions; ++m) { population[{mio::oseirmobilityimproved::Region(n)}] -= - commuting_strengths[0].get_baseline()(n, m) * - model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + 2 * commuting_strengths[0].get_baseline()(n, m) * population_n; population[{mio::oseirmobilityimproved::Region(m)}] += - commuting_strengths[0].get_baseline()(n, m) * - model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); + 2 * commuting_strengths[0].get_baseline()(n, m) * population_n; } } // using DefaultIntegratorCore = diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index 8b07be34d2..cbe06a9fbb 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -49,7 +49,6 @@ class Model : public FlowModel>().get_cont_freq_mat().get_matrix_at(t); const Index n_age_groups = reduce_index>(params.get_num_agegroups()); const Index n_regions = reduce_index>(params.get_num_regions()); - for (auto age_i : make_index_range(n_age_groups)) { for (auto age_j : make_index_range(n_age_groups)) { Eigen::VectorXd infectives_per_region = Eigen::VectorXd::Zero((size_t)n_regions); @@ -61,9 +60,13 @@ class Model : public FlowModel( + {region_n, age_i})] += 0.5 * commuting_strengths(region_n.get(), region_n.get()) * + infectives_per_region(region_n.get()) / + params.template get>()[region_n]; for (auto region_m : make_index_range(n_regions)) { flows[Base::template get_flat_flow_index( - {region_n, age_i})] += commuting_strengths(region_n.get(), region_m.get()) * + {region_n, age_i})] += 0.5 * commuting_strengths(region_n.get(), region_m.get()) * infectives_per_region(region_m.get()) / params.template get>()[region_m]; } From aad78a91cd1ef5a45351b7f08f198319061b4931 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:06:54 +0200 Subject: [PATCH 25/49] corrections again --- cpp/examples/ode_seir_mobility_improved.cpp | 4 ++-- cpp/models/ode_seir_mobility_improved/model.h | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 06bd82ef49..aea5d08376 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -90,9 +90,9 @@ int main() population[{mio::oseirmobilityimproved::Region(n)}] += population_n; for (int m = 0; m < number_regions; ++m) { population[{mio::oseirmobilityimproved::Region(n)}] -= - 2 * commuting_strengths[0].get_baseline()(n, m) * population_n; + commuting_strengths[0].get_baseline()(n, m) * population_n; population[{mio::oseirmobilityimproved::Region(m)}] += - 2 * commuting_strengths[0].get_baseline()(n, m) * population_n; + commuting_strengths[0].get_baseline()(n, m) * population_n; } } // using DefaultIntegratorCore = diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index cbe06a9fbb..e4af53aeac 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -60,13 +60,9 @@ class Model : public FlowModel( - {region_n, age_i})] += 0.5 * commuting_strengths(region_n.get(), region_n.get()) * - infectives_per_region(region_n.get()) / - params.template get>()[region_n]; for (auto region_m : make_index_range(n_regions)) { flows[Base::template get_flat_flow_index( - {region_n, age_i})] += 0.5 * commuting_strengths(region_n.get(), region_m.get()) * + {region_n, age_i})] += commuting_strengths(region_n.get(), region_m.get()) * infectives_per_region(region_m.get()) / params.template get>()[region_m]; } From 2bdb791477726b7d5410421fd1e80a18edb48898 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:07:15 +0200 Subject: [PATCH 26/49] change integrator in graph model --- cpp/examples/graph.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/examples/graph.cpp b/cpp/examples/graph.cpp index 11c43fa4f4..65e913b1ed 100644 --- a/cpp/examples/graph.cpp +++ b/cpp/examples/graph.cpp @@ -61,6 +61,10 @@ int main() mio::Graph>>, mio::MobilityEdge<>> g; g.add_node(1001, model_group1, t0); g.add_node(1002, model_group2, t0); + for (auto& node : g.nodes()) { + node.property.get_simulation().set_integrator(std::make_shared>()); + node.property.get_simulation().get_dt() = dt; + } g.add_edge(0, 1, Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, 0.05)); g.add_edge(1, 0, Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, 0.01)); From 3c6f6306d348298f938b23a9496bfb56fb183401 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 7 Nov 2024 20:18:30 +0100 Subject: [PATCH 27/49] fix bug with age groups and discard transmissions during commuting --- cpp/models/ode_seir_mobility_improved/model.h | 50 ++++++++++------- .../ode_seir_mobility_improved/parameters.h | 54 ++----------------- .../ode_seir_mobility_improved/regions.h | 4 +- 3 files changed, 38 insertions(+), 70 deletions(-) diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index e4af53aeac..3353e5c2e7 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -37,6 +37,8 @@ class Model : public FlowModel({Region(num_regions), AgeGroup(num_agegroups)})) { } @@ -60,31 +62,43 @@ class Model : public FlowModel>().get_cont_freq_mat().get_matrix_at(t)( + age_i.get(), age_j.get()) * + params.template get>()[age_i]; + + flow_SE_helper += + pop[population.get_flat_index({region_n, age_j, InfectionState::Infected})] * Nj_inv; for (auto region_m : make_index_range(n_regions)) { - flows[Base::template get_flat_flow_index( - {region_n, age_i})] += commuting_strengths(region_n.get(), region_m.get()) * - infectives_per_region(region_m.get()) / - params.template get>()[region_m]; + flow_SE_helper += commuting_strengths(region_n.get(), region_m.get()) * + infectives_per_region(region_m.get()) / + m_population_after_commuting[{region_n, age_j}]; } - } - - double coeffStoI = params.template get>().get_cont_freq_mat().get_matrix_at(t)( - age_i.get(), age_j.get()) * - params.template get>()[age_i]; - for (auto region : make_index_range(n_regions)) { flows[Base::template get_flat_flow_index( - {region, age_i})] *= - coeffStoI * y[population.get_flat_index({region, age_j, InfectionState::Susceptible})]; - flows[Base::template get_flat_flow_index( - {region, age_i})] = (1.0 / params.template get>()[age_i]) * - y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; - flows[Base::template get_flat_flow_index( - {region, age_i})] = (1.0 / params.template get>()[age_i]) * - y[population.get_flat_index({region, age_i, InfectionState::Infected})]; + {region_n, age_i})] += + flow_SE_helper * coeffStoI * + y[population.get_flat_index({region_n, age_j, InfectionState::Susceptible})]; } } + for (auto region : make_index_range(n_regions)) { + flows[Base::template get_flat_flow_index( + {region, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; + flows[Base::template get_flat_flow_index( + {region, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region, age_i, InfectionState::Infected})]; + } } } + + mio::Populations m_population_after_commuting; }; // namespace oseirmobilityimproved } // namespace oseirmobilityimproved diff --git a/cpp/models/ode_seir_mobility_improved/parameters.h b/cpp/models/ode_seir_mobility_improved/parameters.h index 05ac04ae5d..49c548207f 100644 --- a/cpp/models/ode_seir_mobility_improved/parameters.h +++ b/cpp/models/ode_seir_mobility_improved/parameters.h @@ -86,38 +86,7 @@ struct ContactPatterns { }; /** - * @brief The ratio that regulates the infections during commuting. -*/ -template -struct ImpactTransmissionDuringCommuting { - using Type = UncertainValue; - static Type get_default(Region, AgeGroup) - { - return Type(0.); - } - static std::string name() - { - return "ImpactTransmissionDuringCommuting"; - } -}; - -/** - * @brief The Region%s that a person crosses when travelling from one Region to another. -*/ -struct PathIntersections { - using Type = CustomIndexArray, Region, Region>; - static Type get_default(Region, AgeGroup) - { - return Type({Region(0), Region(0)}); - } - static std::string name() - { - return "PathIntersections"; - } -}; - -/** - * @brief The contact patterns within different Region%s are modelled using a ContactMatrix. + * @brief The contact patterns between different Region%s are modelled using a ContactMatrix. */ template struct CommutingStrengths { @@ -133,7 +102,7 @@ struct CommutingStrengths { }; /** - * @brief The Region%s that a person crosses when travelling from one Region to another. + * @brief The sizes of the populations after commuting. */ template struct PopulationSizes { @@ -149,9 +118,8 @@ struct PopulationSizes { }; template -using ParametersBase = - ParameterSet, TimeExposed, TimeInfected, ContactPatterns, - ImpactTransmissionDuringCommuting, PathIntersections, CommutingStrengths, PopulationSizes>; +using ParametersBase = ParameterSet, TimeExposed, TimeInfected, + ContactPatterns, CommutingStrengths, PopulationSizes>; /** * @brief Parameters of SEIR model. @@ -224,13 +192,6 @@ class Parameters : public ParametersBase corrected = true; } } - if (this->template get>() < 0.0 || - this->template get>() > 1.0) { - log_warning("Constraint check: Parameter ImpactTransmissionDuringCommuting changed from {:.4f} to {:.4f}.", - this->template get>(), 0.0); - this->template get>() = 0.0; - corrected = true; - } return corrected; } @@ -268,13 +229,6 @@ class Parameters : public ParametersBase return true; } } - if (this->template get>() < 0.0 || - this->template get>() > 1.0) { - log_error( - "Constraint check: Parameter ImpactTransmissionDuringCommuting {:.4f} smaller {:.4f} or greater {:.4f}", - this->template get>(), 0.0, 1.0); - return true; - } return false; } diff --git a/cpp/models/ode_seir_mobility_improved/regions.h b/cpp/models/ode_seir_mobility_improved/regions.h index f9feca907f..d5f7657138 100644 --- a/cpp/models/ode_seir_mobility_improved/regions.h +++ b/cpp/models/ode_seir_mobility_improved/regions.h @@ -10,8 +10,8 @@ namespace oseirmobilityimproved { /** - * @brief The AgeGroup struct is used as a dynamically - * sized tag for all age dependent categories + * @brief The Region struct is used as a dynamically + * sized tag for all region dependent categories */ struct Region : public Index { Region(size_t val) From aa6b393a1380456441c475c760002dc84d51c069 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:56:39 +0100 Subject: [PATCH 28/49] adapt structure of old model --- cpp/examples/ode_seir_mobility.cpp | 146 ++++------------------ cpp/models/ode_seir_mobility/model.h | 72 ++++------- cpp/models/ode_seir_mobility/parameters.h | 89 ++----------- 3 files changed, 53 insertions(+), 254 deletions(-) diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp index d2e20185cf..7fe6903d1d 100644 --- a/cpp/examples/ode_seir_mobility.cpp +++ b/cpp/examples/ode_seir_mobility.cpp @@ -11,106 +11,13 @@ #include "memilio/io/io.h" #include "memilio/io/result_io.h" -mio::IOResult>>> read_path_mobility(const std::string& filename) -{ - BOOST_OUTCOME_TRY(auto&& num_lines, mio::count_lines(filename)); - - if (num_lines == 0) { - std::vector>> arr(0, std::vector>(0)); - return mio::success(arr); - } - - std::fstream file; - file.open(filename, std::ios::in); - if (!file.is_open()) { - return failure(mio::StatusCode::FileNotFound, filename); - } - - std::vector>> arr(std::sqrt(num_lines), - std::vector>(std::sqrt(num_lines))); - - try { - std::string tp; - while (getline(file, tp)) { - auto line = mio::split(tp, ' '); - int indx_x = std::stoi(line[0]); - int indx_y = std::stoi(line[1]); - if (indx_x != indx_y) { - auto path = std::accumulate(line.begin() + 2, line.end(), std::string("")); - - // string -> vector of integers - std::vector path_vec; - - // Remove the square brackets and \r - path = path.substr(1, path.size() - 3); - std::stringstream ss(path); - std::string token; - - // get numbers and save them in path_vec - while (std::getline(ss, token, ',')) { - path_vec.push_back(std::stoi(token)); - } - - // Sorted by end location - for (int number : path_vec) { - if (number != indx_x && number != indx_y) { - arr[indx_x][indx_y].push_back(number); - } - } - } - } - } - catch (std::runtime_error& ex) { - return failure(mio::StatusCode::InvalidFileFormat, filename + ": " + ex.what()); - } - - return mio::success(arr); -} - -template -mio::IOResult preprocess(const std::string& filename, mio::oseirmobility::Model& model) -{ - BOOST_OUTCOME_TRY(auto&& mobility_paths, read_path_mobility(filename)); - size_t n_regions = (size_t)model.parameters.get_num_regions(); - for (size_t i = 0; i < n_regions; i++) { - for (size_t j = 0; j < n_regions; j++) { - if (j == i) { - continue; - } - std::sort(mobility_paths[i][j].begin(), mobility_paths[i][j].end()); - std::vector intersection_int; - std::vector intersection_region; - for (size_t k = 0; k < n_regions; k++) { - if (k == i || k == j) { - continue; - } - std::sort(mobility_paths[k][j].begin(), mobility_paths[k][j].end()); - std::set_intersection(mobility_paths[i][j].begin(), mobility_paths[i][j].end(), - mobility_paths[k][j].begin(), mobility_paths[k][j].end(), - std::back_inserter(intersection_int)); - - if (intersection_int.begin() != intersection_int.end()) { - intersection_region.push_back(mio::oseirmobility::Region(k)); - intersection_int.pop_back(); - } - } - if (intersection_region.begin() != intersection_region.end()) { - model.parameters.template get()[{ - mio::oseirmobility::Region(i), mio::oseirmobility::Region(j)}] = intersection_region; - } - } - } - return mio::success(); -} - template -mio::IOResult set_mobility_weights(const std::string& mobility_data, const std::string& trip_chains, - mio::oseirmobility::Model& model, size_t number_regions) +mio::IOResult set_mobility_weights(const std::string& mobility_data, mio::oseirmobility::Model& model, + size_t number_regions) { - BOOST_OUTCOME_TRY(preprocess(trip_chains, model)); // mobility between nodes BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, - mio::read_mobility_plain(mobility_data + "mobility" + "commuter_migration_scaled.txt")); + mio::read_mobility_plain(mobility_data + "/mobility" + "/commuter_migration_test.txt")); if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || mobility_data_commuter.cols() != Eigen::Index(number_regions)) { return mio::failure(mio::StatusCode::InvalidValue, @@ -118,20 +25,13 @@ mio::IOResult set_mobility_weights(const std::string& mobility_data, const "transformMobilitydata.py from pycode memilio epidata package."); } - for (auto age = mio::AgeGroup(0); age < model.parameters.get_num_agegroups(); age++) { - for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { - for (size_t county_idx_j = 0; county_idx_j < number_regions; ++county_idx_j) { - //commuters - auto population_i = model.populations.get_group_total(mio::oseirmobility::Region(county_idx_i)); - auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / population_i; - if (commuter_coeff_ij > 4e-5) { - model.parameters.template get().push_back( - {mio::oseirmobility::Region(county_idx_i), mio::oseirmobility::Region(county_idx_j), - commuter_coeff_ij}); - } - } - } + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + auto population_i = model.populations.get_group_total(mio::oseirmobility::Region(county_idx_i)); + mobility_data_commuter.row(county_idx_i) /= population_i; } + model.parameters.template get>().get_cont_freq_mat()[0].get_baseline() = + mobility_data_commuter; + return mio::success(); } @@ -141,44 +41,40 @@ int main() ScalarType t0 = 0.; ScalarType tmax = 15.; - ScalarType dt = 0.5; + ScalarType dt = 0.1; - std::vector region_ids = {1001, 1002}; - ScalarType number_regions = region_ids.size(); + ScalarType number_regions = 2; + std::vector region_ids(number_regions); + iota(region_ids.begin(), region_ids.end(), 1); ScalarType number_age_groups = 1; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - const std::string& mobility_data = ""; - const std::string& trip_chain_data = ""; + const std::string& mobility_data = ""; mio::oseirmobility::Model model(number_regions, number_age_groups); model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Exposed}] = 10; model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Susceptible}] = 9990; - model.populations[{mio::oseirmobility::Region(1), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Exposed}] = - 0; - model.populations[{mio::oseirmobility::Region(1), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Susceptible}] = 10000; - - // auto result_preprocess = set_mobility_weights(mobility_data, trip_chain_data, model, number_regions); + for (int i = 1; i < number_regions; i++) { + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Exposed}] = 0; + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Susceptible}] = 10000; + } model.parameters.set>(1.); model.parameters.set>(3.); model.parameters.set>(5.); - model.parameters.set>(0.); 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.5, mio::SimulationTime(5)); - model.parameters.get().push_back( - {mio::oseirmobility::Region(0), mio::oseirmobility::Region(1), 0.05}); - model.parameters.get().push_back( - {mio::oseirmobility::Region(1), mio::oseirmobility::Region(0), 0.01}); + auto result_preprocess = set_mobility_weights(mobility_data, model, number_regions); using DefaultIntegratorCore = mio::ControlledStepperWrapper; diff --git a/cpp/models/ode_seir_mobility/model.h b/cpp/models/ode_seir_mobility/model.h index c99eb732c9..3bc20c567d 100644 --- a/cpp/models/ode_seir_mobility/model.h +++ b/cpp/models/ode_seir_mobility/model.h @@ -46,64 +46,42 @@ class Model : public FlowModelparameters; const auto& population = this->populations; - + const auto& commuting_strengths = + params.template get>().get_cont_freq_mat().get_matrix_at(t); const Index n_age_groups = reduce_index>(params.get_num_agegroups()); const Index n_regions = reduce_index>(params.get_num_regions()); for (auto age_i : make_index_range(n_age_groups)) { for (auto age_j : make_index_range(n_age_groups)) { - for (auto edge : params.template get()) { - auto start_region = get<0>(edge); - auto end_region = get<1>(edge); - auto strength = get(edge); - if (start_region == end_region) { - continue; - } - // s_n += h_mn/P_m * i_m - flows[Base::template get_flat_flow_index( - {start_region, age_i})] += - strength * pop[population.get_flat_index({end_region, age_j, InfectionState::Infected})]; - // s_m += h_mn/P_m * i_n - flows[Base::template get_flat_flow_index( - {end_region, age_i})] += - strength * pop[population.get_flat_index({start_region, age_j, InfectionState::Infected})]; - - // s_n += gamma * h_nm/P_n * sum(h_km/P_k * p_nm,k * i_k) - for (auto edge_commuter : params.template get()) { - auto start_region_commuter = get<0>(edge_commuter); - auto end_region_commuter = get<1>(edge_commuter); - auto strength_commuter = get(edge_commuter); - if (end_region_commuter != end_region || start_region_commuter == start_region || - ((std::find(params.template get()[{start_region, end_region}].begin(), - params.template get()[{start_region, end_region}].end(), - start_region_commuter)) == - params.template get()[{start_region, end_region}].end())) { + for (auto region_n : make_index_range(n_regions)) { + FP flow_SE_helper = 0; + const size_t Sj = population.get_flat_index({region_n, age_j, InfectionState::Susceptible}); + const size_t Ej = population.get_flat_index({region_n, age_j, InfectionState::Exposed}); + const size_t Ij = population.get_flat_index({region_n, age_j, InfectionState::Infected}); + const size_t Rj = population.get_flat_index({region_n, age_j, InfectionState::Recovered}); + + const double Nj_inv = 1.0 / (pop[Sj] + pop[Ej] + pop[Ij] + pop[Rj]); + + double coeffStoI = params.template get>().get_cont_freq_mat().get_matrix_at(t)( + age_i.get(), age_j.get()) * + params.template get>()[age_i] * Nj_inv; + + for (auto region_m : make_index_range(n_regions)) { + if (region_n == region_m) { + flow_SE_helper += + pop[population.get_flat_index({region_n, age_j, InfectionState::Infected})]; continue; } - flows[Base::template get_flat_flow_index( - {start_region, age_i})] += - params.template get>() * strength * - strength_commuter * - pop[population.get_flat_index({start_region_commuter, age_j, InfectionState::Infected})]; + flow_SE_helper += (commuting_strengths(region_n.get(), region_m.get()) + + commuting_strengths(region_m.get(), region_n.get())) * + pop[population.get_flat_index({region_m, age_j, InfectionState::Infected})]; } - } - for (auto region : make_index_range(n_regions)) { - auto const population_region = population.template slice({(size_t)region, 1}); - auto const population_region_age = population_region.template slice({(size_t)age_j, 1}); - double population_size = - std::accumulate(population_region_age.begin(), population_region_age.end(), 0.); - double coeffStoI = - params.template get>().get_cont_freq_mat().get_matrix_at(t)(age_i.get(), - age_j.get()) * - params.template get>()[age_i] / population_size; - flows[Base::template get_flat_flow_index( - {region, age_i})] += pop[population.get_flat_index({region, age_j, InfectionState::Infected})]; flows[Base::template get_flat_flow_index( - {region, age_i})] *= - coeffStoI * y[population.get_flat_index({region, age_j, InfectionState::Susceptible})]; + {region_n, age_i})] += + flow_SE_helper * coeffStoI * + y[population.get_flat_index({region_n, age_j, InfectionState::Susceptible})]; } } - for (auto region : make_index_range(n_regions)) { flows[Base::template get_flat_flow_index( {region, age_i})] = (1.0 / params.template get>()[age_i]) * diff --git a/cpp/models/ode_seir_mobility/parameters.h b/cpp/models/ode_seir_mobility/parameters.h index 0b27284c9d..ca6fd99f1f 100644 --- a/cpp/models/ode_seir_mobility/parameters.h +++ b/cpp/models/ode_seir_mobility/parameters.h @@ -85,55 +85,24 @@ struct ContactPatterns { }; /** - * @brief The mean number of people migrating from one Region to another during a TimeStep. + * @brief The contact patterns between different Region%s are modelled using a ContactMatrix. */ -struct CommutingRatio { - using Type = std::vector>; - static Type get_default(Region, AgeGroup) - { - return Type({{Region(0), Region(0), 0.}}); - } - static std::string name() - { - return "CommutingRatio"; - } -}; - -/** - * @brief The ratio that regulates the infections during commuting. -*/ template -struct ImpactTransmissionDuringCommuting { - using Type = UncertainValue; - static Type get_default(Region, AgeGroup) - { - return Type(0.); - } - static std::string name() - { - return "ImpactTransmissionDuringCommuting"; - } -}; - -/** - * @brief The Region%s that a person crosses when travelling from one Region to another. -*/ -struct PathIntersections { - using Type = CustomIndexArray, Region, Region>; +struct CommutingStrengths { + using Type = UncertainContactMatrix; static Type get_default(Region size, AgeGroup) { - return Type({size, size}); + return Type(1, static_cast((size_t)size)); } static std::string name() { - return "PathIntersections"; + return "CommutingStrengths"; } }; template -using ParametersBase = - ParameterSet, TimeExposed, TimeInfected, ContactPatterns, - CommutingRatio, ImpactTransmissionDuringCommuting, PathIntersections>; +using ParametersBase = ParameterSet, TimeExposed, TimeInfected, + ContactPatterns, CommutingStrengths>; /** * @brief Parameters of SEIR model. @@ -206,30 +175,6 @@ class Parameters : public ParametersBase corrected = true; } } - if (this->template get>() < 0.0 || - this->template get>() > 1.0) { - log_warning("Constraint check: Parameter ImpactTransmissionDuringCommuting changed from {:.4f} to {:.4f}.", - this->template get>(), 0.0); - this->template get>() = 0.0; - corrected = true; - } - for (auto& i : this->template get()) { - if (std::get(i) < 0.0 || std::get(i) > 1.0) { - log_warning("Constraint check: Parameter CommutingRatio changed from {:.4f} to {:.4f}.", - std::get(i), 0.0); - std::get(i) = 0.0; - corrected = true; - } - if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) >= m_num_regions || - std::get<1>(i) >= m_num_regions) { - log_warning( - "Constraint check: Removed entry of Parameter CommutingRatio because of non-existing Regions."); - auto it = std::find(this->template get().begin(), - this->template get().end(), i); - this->template get().erase(it); - corrected = true; - } - } return corrected; } @@ -267,26 +212,6 @@ class Parameters : public ParametersBase return true; } } - if (this->template get>() < 0.0 || - this->template get>() > 1.0) { - log_error( - "Constraint check: Parameter ImpactTransmissionDuringCommuting {:.4f} smaller {:.4f} or greater {:.4f}", - this->template get>(), 0.0, 1.0); - return true; - } - for (auto i : this->template get()) { - if (std::get(i) < 0.0 || std::get(i) > 1.0) { - log_error("Constraint check: Parameter CommutingRatio entry {:.4f} smaller {:.4f} or greater {:.4f}", - std::get(i), 0.0, 1.0); - return true; - } - if (std::get<0>(i) < Region(0) || std::get<1>(i) < Region(0) || std::get<0>(i) > m_num_regions || - std::get<1>(i) > m_num_regions) { - log_error("Constraint check: Parameter CommutingRatio has an entry with start or end Region " - "that does not appear in the model."); - return true; - } - } return false; } From e6e6d70e29f9af0e13c3bbf0f21ce9927a649aeb Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:29:20 +0100 Subject: [PATCH 29/49] add computation of basis reproduction number for old model --- cpp/models/ode_seir_mobility/model.h | 123 +++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 9 deletions(-) diff --git a/cpp/models/ode_seir_mobility/model.h b/cpp/models/ode_seir_mobility/model.h index 3bc20c567d..1d2aa11eba 100644 --- a/cpp/models/ode_seir_mobility/model.h +++ b/cpp/models/ode_seir_mobility/model.h @@ -8,6 +8,12 @@ #include "models/ode_seir_mobility/parameters.h" #include "models/ode_seir_mobility/regions.h" #include "memilio/epidemiology/age_group.h" +#include "memilio/utils/time_series.h" + +GCC_CLANG_DIAGNOSTIC(push) +GCC_CLANG_DIAGNOSTIC(ignored "-Wshadow") +#include +GCC_CLANG_DIAGNOSTIC(pop) namespace mio { @@ -39,7 +45,6 @@ class Model : public FlowModel> pop, Eigen::Ref> y, FP t, Eigen::Ref> flows) const override @@ -52,8 +57,8 @@ class Model : public FlowModel n_regions = reduce_index>(params.get_num_regions()); for (auto age_i : make_index_range(n_age_groups)) { - for (auto age_j : make_index_range(n_age_groups)) { - for (auto region_n : make_index_range(n_regions)) { + for (auto region_n : make_index_range(n_regions)) { + for (auto age_j : make_index_range(n_age_groups)) { FP flow_SE_helper = 0; const size_t Sj = population.get_flat_index({region_n, age_j, InfectionState::Susceptible}); const size_t Ej = population.get_flat_index({region_n, age_j, InfectionState::Exposed}); @@ -81,17 +86,117 @@ class Model : public FlowModel( - {region, age_i})] = (1.0 / params.template get>()[age_i]) * - y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; + {region_n, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region_n, age_i, InfectionState::Exposed})]; flows[Base::template get_flat_flow_index( - {region, age_i})] = (1.0 / params.template get>()[age_i]) * - y[population.get_flat_index({region, age_i, InfectionState::Infected})]; + {region_n, age_i})] = (1.0 / params.template get>()[age_i]) * + y[population.get_flat_index({region_n, age_i, InfectionState::Infected})]; } } } + + /** + *@brief Computes the reproduction number at a given index time of the Model output obtained by the Simulation. + *@param t_idx The index time at which the reproduction number is computed. + *@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) + { + 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"); + } + + auto const& params = this->parameters; + auto const& pop = this->populations; + + const size_t num_age_groups = (size_t)params.get_num_agegroups(); + const size_t num_regions = (size_t)params.get_num_regions(); + constexpr size_t num_infected_compartments = 2; + const size_t total_infected_compartments = num_infected_compartments * num_age_groups * num_regions; + + ContactMatrixGroup const& contact_matrix = params.template get>(); + ContactMatrixGroup const& commuting_strengths = 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); + + for (auto i = AgeGroup(0); i < AgeGroup(num_age_groups); i++) { + for (auto n = Region(0); n < Region(num_regions); n++) { + size_t Si = pop.get_flat_index({n, i, InfectionState::Susceptible}); + for (auto j = AgeGroup(0); j < AgeGroup(num_age_groups); j++) { + for (auto m = Region(0); m < Region(num_regions); m++) { + auto const population_region = pop.template slice({(size_t)m, 1}); + auto const population_region_age = population_region.template slice({(size_t)j, 1}); + auto Njm = std::accumulate(population_region_age.begin(), population_region_age.end(), 0.); + + if (n == m) { + double coeffStoE = contact_matrix.get_matrix_at(y.get_time(t_idx))(i.get(), j.get()) * + params.template get>()[i] / + Njm; + F((size_t)i * num_regions + (size_t)n, + num_age_groups * num_regions + (size_t)j * num_regions + (size_t)m) = + coeffStoE * y.get_value(t_idx)[Si]; + } + else { + double coeffStoE = + contact_matrix.get_matrix_at(y.get_time(t_idx))(i.get(), j.get()) * + params.template get>()[i] * + (commuting_strengths.get_matrix_at(y.get_time(t_idx))(n.get(), m.get()) + + commuting_strengths.get_matrix_at(y.get_time(t_idx))(m.get(), n.get())) / + Njm; + F((size_t)i * num_regions + (size_t)n, + num_age_groups * num_regions + (size_t)j * num_regions + (size_t)m) = + coeffStoE * y.get_value(t_idx)[Si]; + } + } + } + + double T_Ei = params.template get>()[i]; + double T_Ii = params.template get>()[i]; + V((size_t)i * num_regions + (size_t)n, (size_t)i * num_regions + (size_t)n) = 1.0 / T_Ei; + V(num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n, + (size_t)i * num_regions + (size_t)n) = -1.0 / T_Ei; + V(num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n, + num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n) = 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; + + ces.compute(NextGenMatrix); + const Eigen::VectorXcd eigen_vals = ces.eigenvalues(); + + Eigen::VectorXd eigen_vals_abs; + eigen_vals_abs.resize(eigen_vals.size()); + + 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()); + } + + /** + *@brief Computes the reproduction number for all time points of the Model output obtained by the Simulation. + *@param y The TimeSeries obtained from the Model Simulation. + *@returns vector containing all reproduction numbers + */ + Eigen::VectorXd get_reproduction_numbers(const mio::TimeSeries& y) + { + auto num_time_points = y.get_num_time_points(); + Eigen::VectorXd temp(num_time_points); + for (size_t i = 0; i < static_cast(num_time_points); i++) { + temp[i] = get_reproduction_number(i, y).value(); + } + return temp; + } }; } // namespace oseirmobility From 649467c3680d2a664cf244fdb95ad8001a3c6049 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Wed, 20 Nov 2024 19:33:04 +0100 Subject: [PATCH 30/49] add computation of basis reproduction numbers and changes for counting FLOPs --- cpp/examples/graph.cpp | 1 - cpp/examples/ode_seir_mobility.cpp | 3 + cpp/examples/ode_seir_mobility_improved.cpp | 63 ++++++----- cpp/models/ode_seir/model.h | 2 +- cpp/models/ode_seir_mobility_improved/model.h | 106 ++++++++++++++++++ 5 files changed, 144 insertions(+), 31 deletions(-) diff --git a/cpp/examples/graph.cpp b/cpp/examples/graph.cpp index 65e913b1ed..b060e1bcb2 100644 --- a/cpp/examples/graph.cpp +++ b/cpp/examples/graph.cpp @@ -63,7 +63,6 @@ int main() g.add_node(1002, model_group2, t0); for (auto& node : g.nodes()) { node.property.get_simulation().set_integrator(std::make_shared>()); - node.property.get_simulation().get_dt() = dt; } g.add_edge(0, 1, Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, 0.05)); g.add_edge(1, 0, Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, 0.01)); diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp index 7fe6903d1d..4cdab666b7 100644 --- a/cpp/examples/ode_seir_mobility.cpp +++ b/cpp/examples/ode_seir_mobility.cpp @@ -88,6 +88,9 @@ int main() auto save_result_status = mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_standard.h5"); + auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); + std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; + // bool print_to_terminal = true; // result_from_sim.print_table(); diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index aea5d08376..4f7e819512 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -27,17 +27,15 @@ mio::IOResult set_mobility_weights(const std::string& mobility_data, mio:: "transformMobilitydata.py from pycode memilio epidata package."); } - for (auto age = mio::AgeGroup(0); age < model.parameters.get_num_agegroups(); age++) { - for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { - auto population_i = model.populations.get_group_total(mio::oseirmobilityimproved::Region(county_idx_i)); - mobility_data_commuter.row(county_idx_i) /= 2 * population_i; - mobility_data_commuter(county_idx_i, county_idx_i) = - 1 - mobility_data_commuter.rowwise().sum()(county_idx_i); - } - model.parameters.template get>() - .get_cont_freq_mat()[0] - .get_baseline() = mobility_data_commuter; + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + auto population_i = model.populations.get_group_total(mio::oseirmobilityimproved::Region(county_idx_i)); + mobility_data_commuter.row(county_idx_i) /= population_i; + mobility_data_commuter(county_idx_i, county_idx_i) = 1 - mobility_data_commuter.rowwise().sum()(county_idx_i); } + model.parameters.template get>() + .get_cont_freq_mat()[0] + .get_baseline() = mobility_data_commuter; + return mio::success(); } @@ -47,26 +45,27 @@ int main() ScalarType t0 = 0.; ScalarType tmax = 15.; - ScalarType dt = 0.5; + ScalarType dt = 0.1; - // std::vector region_ids = {1001, 1002}; - ScalarType number_regions = 400; + ScalarType number_regions = 2; + std::vector region_ids(number_regions); + iota(region_ids.begin(), region_ids.end(), 1); ScalarType number_age_groups = 1; mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - const std::string& mobility_data = "/home/gers_ca/code/memilio/data"; + const std::string& mobility_data = ""; mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 100; + mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 99900; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; for (int i = 1; i < number_regions; i++) { model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(0), mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 100000; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; } model.parameters.set>(1.); @@ -74,7 +73,6 @@ int main() model.parameters.set>(3.); model.parameters.set>(5.); - model.parameters.set>(0.); mio::ContactMatrixGroup& contact_matrix = model.parameters.get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(2.7); @@ -84,15 +82,20 @@ int main() mio::ContactMatrixGroup& commuting_strengths = model.parameters.get>().get_cont_freq_mat(); - auto& population = model.parameters.get>(); - for (int n = 0; n < number_regions; ++n) { - auto population_n = model.populations.get_group_total(mio::oseirmobilityimproved::Region(n)); - population[{mio::oseirmobilityimproved::Region(n)}] += population_n; - for (int m = 0; m < number_regions; ++m) { - population[{mio::oseirmobilityimproved::Region(n)}] -= - commuting_strengths[0].get_baseline()(n, m) * population_n; - population[{mio::oseirmobilityimproved::Region(m)}] += - commuting_strengths[0].get_baseline()(n, m) * population_n; + auto& population = model.m_population_after_commuting; + for (int region_n = 0; region_n < number_regions; ++region_n) { + for (int age = 0; age < number_age_groups; ++age) { + auto const population_region = + model.populations.template slice({(size_t)region_n, 1}); + auto const population_region_age = population_region.template slice({(size_t)age, 1}); + auto population_n = std::accumulate(population_region_age.begin(), population_region_age.end(), 0.); + population[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] += population_n; + for (int region_m = 0; region_m < number_regions; ++region_m) { + population[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] -= + commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; + population[{mio::oseirmobilityimproved::Region(region_m), mio::AgeGroup(age)}] += + commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; + } } } // using DefaultIntegratorCore = @@ -111,9 +114,11 @@ int main() printf("Runtime: %f\n", ms_double.count()); - // auto save_result_status = - // mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); + auto save_result_status = + mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); + auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); + std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; // bool print_to_terminal = true; // result_from_sim.print_table(); diff --git a/cpp/models/ode_seir/model.h b/cpp/models/ode_seir/model.h index fa3bf4db93..3614ebc25e 100644 --- a/cpp/models/ode_seir/model.h +++ b/cpp/models/ode_seir/model.h @@ -80,7 +80,7 @@ class Model const size_t Ii = this->populations.get_flat_index({i, InfectionState::Infected}); for (auto j : make_index_range(age_groups)) { - const size_t Sj = this->populations.get_flat_index({i, InfectionState::Susceptible}); + const size_t Sj = this->populations.get_flat_index({j, InfectionState::Susceptible}); const size_t Ej = this->populations.get_flat_index({j, InfectionState::Exposed}); const size_t Ij = this->populations.get_flat_index({j, InfectionState::Infected}); const size_t Rj = this->populations.get_flat_index({j, InfectionState::Recovered}); diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index 3353e5c2e7..aa29e3cb0a 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -8,6 +8,12 @@ #include "models/ode_seir_mobility_improved/parameters.h" #include "models/ode_seir_mobility_improved/regions.h" #include "memilio/epidemiology/age_group.h" +#include "memilio/utils/time_series.h" + +GCC_CLANG_DIAGNOSTIC(push) +GCC_CLANG_DIAGNOSTIC(ignored "-Wshadow") +#include +GCC_CLANG_DIAGNOSTIC(pop) namespace mio { @@ -98,6 +104,106 @@ class Model : public FlowModel 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"); + } + + auto const& params = this->parameters; + auto const& pop = this->populations; + + const size_t num_age_groups = (size_t)params.get_num_agegroups(); + const size_t num_regions = (size_t)params.get_num_regions(); + constexpr size_t num_infected_compartments = 2; + const size_t total_infected_compartments = num_infected_compartments * num_age_groups * num_regions; + + ContactMatrixGroup const& contact_matrix = params.template get>(); + ContactMatrixGroup const& commuting_strengths = 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); + + for (auto i = AgeGroup(0); i < AgeGroup(num_age_groups); i++) { + for (auto n = Region(0); n < Region(num_regions); n++) { + size_t Si = pop.get_flat_index({n, i, InfectionState::Susceptible}); + for (auto j = AgeGroup(0); j < AgeGroup(num_age_groups); j++) { + for (auto m = Region(0); m < Region(num_regions); m++) { + auto const population_region = pop.template slice({(size_t)m, 1}); + auto const population_region_age = population_region.template slice({(size_t)j, 1}); + auto Njm = std::accumulate(population_region_age.begin(), population_region_age.end(), 0.); + + double coeffStoE = 0.5 * contact_matrix.get_matrix_at(y.get_time(t_idx))(i.get(), j.get()) * + params.template get>()[i]; + if (n == m) { + F((size_t)i * num_regions + (size_t)n, + num_age_groups * num_regions + (size_t)j * num_regions + (size_t)m) += + coeffStoE * y.get_value(t_idx)[Si] / Njm; + } + for (auto k = Region(0); k < Region(num_regions); k++) { + auto test = commuting_strengths.get_matrix_at(y.get_time(t_idx))(n.get(), k.get()); + mio::unused(test); + F((size_t)i * num_regions + (size_t)n, + num_age_groups * num_regions + (size_t)j * num_regions + (size_t)m) += + coeffStoE * y.get_value(t_idx)[Si] * + commuting_strengths.get_matrix_at(y.get_time(t_idx))(n.get(), k.get()) * + commuting_strengths.get_matrix_at(y.get_time(t_idx))(m.get(), k.get()) / + m_population_after_commuting[{k, j}]; + } + } + } + + double T_Ei = params.template get>()[i]; + double T_Ii = params.template get>()[i]; + V((size_t)i * num_regions + (size_t)n, (size_t)i * num_regions + (size_t)n) = 1.0 / T_Ei; + V(num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n, + (size_t)i * num_regions + (size_t)n) = -1.0 / T_Ei; + V(num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n, + num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n) = 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; + + ces.compute(NextGenMatrix); + const Eigen::VectorXcd eigen_vals = ces.eigenvalues(); + + Eigen::VectorXd eigen_vals_abs; + eigen_vals_abs.resize(eigen_vals.size()); + + 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()); + } + + /** + *@brief Computes the reproduction number for all time points of the Model output obtained by the Simulation. + *@param y The TimeSeries obtained from the Model Simulation. + *@returns vector containing all reproduction numbers + */ + Eigen::VectorXd get_reproduction_numbers(const mio::TimeSeries& y) + { + auto num_time_points = y.get_num_time_points(); + Eigen::VectorXd temp(num_time_points); + for (size_t i = 0; i < static_cast(num_time_points); i++) { + temp[i] = get_reproduction_number(i, y).value(); + } + return temp; + } + mio::Populations m_population_after_commuting; }; // namespace oseirmobilityimproved From 4239fa748ca6248cc7dc3c4773a0cac4856b3442 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:40:44 +0100 Subject: [PATCH 31/49] small cleanup --- cpp/examples/ode_seir_mobility_improved.cpp | 37 ++++----------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 4f7e819512..32d2685647 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -85,10 +85,11 @@ int main() auto& population = model.m_population_after_commuting; for (int region_n = 0; region_n < number_regions; ++region_n) { for (int age = 0; age < number_age_groups; ++age) { - auto const population_region = - model.populations.template slice({(size_t)region_n, 1}); - auto const population_region_age = population_region.template slice({(size_t)age, 1}); - auto population_n = std::accumulate(population_region_age.begin(), population_region_age.end(), 0.); + double population_n = 0; + for (size_t state = 0; state < (size_t)mio::oseirmobilityimproved::InfectionState::Count; state++) { + population_n += model.populations[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age), + mio::oseirmobilityimproved::InfectionState(state)}]; + } population[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] += population_n; for (int region_m = 0; region_m < number_regions; ++region_m) { population[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] -= @@ -119,30 +120,6 @@ int main() auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; - // bool print_to_terminal = true; - - // result_from_sim.print_table(); - - // if (print_to_terminal) { - - // std::vector vars = {"S", "E", "I", "R"}; - // printf("\n # t"); - // for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { - // for (size_t k = 0; k < (size_t)mio::oseirmobilityimproved::InfectionState::Count; k++) { - // printf(" %s_%d", vars[k].c_str(), (int)i); - // } - // } - - // auto num_points = static_cast(result_from_sim.get_num_time_points()); - // for (size_t i = 0; i < num_points; i++) { - // printf("\n%.14f ", result_from_sim.get_time(i)); - // for (size_t k = 0; k < (size_t)model.parameters.get_num_regions(); k++) { - // for (size_t j = 0; j < (size_t)mio::oseirmobilityimproved::InfectionState::Count; j++) { - // printf(" %.14f", result_from_sim.get_value( - // i)[j + (size_t)mio::oseirmobilityimproved::InfectionState::Count * (int)k]); - // } - // } - // } - // printf("\n"); - // } + + result_from_sim.print_table(); } From 2c4de486d2e4f99f0cc8f34860464227847bd6b8 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Sun, 1 Dec 2024 21:21:13 +0100 Subject: [PATCH 32/49] read in population data --- cpp/examples/CMakeLists.txt | 4 + cpp/examples/graph_extended.cpp | 109 ++++++++++ cpp/examples/ode_seir.cpp | 44 ++-- cpp/examples/ode_seir_mobility_improved.cpp | 200 ++++++++++++------ cpp/models/ode_seir_mobility_improved/model.h | 25 +-- 5 files changed, 287 insertions(+), 95 deletions(-) create mode 100644 cpp/examples/graph_extended.cpp diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index b92f70dfd6..733ca7cf47 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -100,6 +100,10 @@ add_executable(graph_example graph.cpp) target_link_libraries(graph_example PRIVATE memilio ode_seir) target_compile_options(graph_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(graph_example_extended graph_extended.cpp) +target_link_libraries(graph_example_extended PRIVATE memilio ode_seir) +target_compile_options(graph_example_extended PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(graph_stochastic_mobility_example graph_stochastic_mobility.cpp) target_link_libraries(graph_stochastic_mobility_example PRIVATE memilio ode_secir) target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/examples/graph_extended.cpp b/cpp/examples/graph_extended.cpp new file mode 100644 index 0000000000..e963c1e631 --- /dev/null +++ b/cpp/examples/graph_extended.cpp @@ -0,0 +1,109 @@ + +#include "ode_seir/model.h" +#include "ode_seir/infection_state.h" +#include "ode_seir/parameters.h" +#include "memilio/mobility/metapopulation_mobility_instant.h" +#include "memilio/compartments/simulation.h" +#include "memilio/io/result_io.h" + +#include + +mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double dt) +{ + // global parameters + const int num_age_groups = 1; + mio::oseir::Parameters params(num_age_groups); + mio::Populations population( + {mio::AgeGroup(num_age_groups), mio::oseir::InfectionState::Count}); + params.set>(1.); + + // set transition times + params.set>(3.); + params.set>(5.); + + // set contact matrix + params.get>().get_cont_freq_mat()[0].get_baseline().setConstant(7.95); + + population[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = 10000; + + // graph of counties with populations and local parameters + // and mobility between counties + mio::Graph>>, mio::MobilityEdge<>> params_graph; + + std::vector> nodes(2, mio::oseir::Model(int(size_t(params.get_num_groups())))); + for (auto& node : nodes) { + node.parameters = params; + node.populations = population; + } + nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = 9990; + nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] = 10; + + for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { + params_graph.add_node(node_idx, nodes[node_idx]); + } + + BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, + mio::read_mobility_plain((data_dir / "mobility" / "commuter_migration_test.txt").string())); + if (mobility_data_commuter.rows() != Eigen::Index(params_graph.nodes().size()) || + mobility_data_commuter.cols() != Eigen::Index(params_graph.nodes().size())) { + return mio::failure(mio::StatusCode::InvalidValue, + "Mobility matrices do not have the correct size. You may need to run " + "transformMobilitydata.py from pycode memilio epidata package."); + } + + for (size_t county_idx_i = 0; county_idx_i < params_graph.nodes().size(); ++county_idx_i) { + for (size_t county_idx_j = 0; county_idx_j < params_graph.nodes().size(); ++county_idx_j) { + auto& populations = params_graph.nodes()[county_idx_i].property.get_simulation().get_model().populations; + + auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / populations.get_total(); + params_graph.add_edge( + county_idx_i, county_idx_j, + Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, commuter_coeff_ij)); + } + } + + for (auto& node : params_graph.nodes()) { + node.property.get_simulation().set_integrator(std::make_shared>()); + } + + auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); + + printf("Start Simulation\n"); + auto t1 = std::chrono::high_resolution_clock::now(); + sim.advance(tmax); + auto t2 = std::chrono::high_resolution_clock::now(); + + std::chrono::duration ms_double = t2 - t1; + + printf("Runtime: %f\n", ms_double.count()); + + auto result_graph = std::move(sim).get_graph(); + auto result = mio::interpolate_simulation_result(result_graph); + + std::vector county_ids(result_graph.nodes().size()); + std::transform(result_graph.nodes().begin(), result_graph.nodes().end(), county_ids.begin(), [](auto& n) { + return n.id; + }); + + auto save_result_status = save_result(result, county_ids, 1, "graph_result.h5"); + result_graph.nodes()[0].property.get_result().print_table(); + result_graph.nodes()[1].property.get_result().print_table(); + // for (auto&& node : result_graph.nodes()) { + // node.property.get_result().print_table(); + // } + + return mio::success(); +} + +int main() +{ + const auto t0 = 0.; + const auto tmax = 1.; + const auto dt = 0.5; //time step of mobility, daily mobility every second step + + const std::string& data_dir = ""; + + auto result = run(data_dir, t0, tmax, dt); + + return 0; +} diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 875372959e..c4e0bd0269 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -22,6 +22,7 @@ #include "ode_seir/parameters.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" +#include "memilio/math/euler.h" #include "memilio/utils/time_series.h" #include "memilio/utils/time_series.h" @@ -31,34 +32,41 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0; - ScalarType tmax = 50.; - ScalarType dt = 1.0; + ScalarType tmax = 0.2; + ScalarType dt = 0.1; mio::log_info("Simulating ODE SEIR; t={} ... {} with dt = {}.", t0, tmax, dt); - mio::oseir::Model model(1); + int number_agegroups = 6; + mio::oseir::Model model(number_agegroups); - 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[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = - total_population - model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] - - model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Infected}] - - model.populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Recovered}]; + ScalarType total_population = 286922; + for (int i = 0; i < number_agegroups; i++) { + model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Exposed}] = 10; + model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Infected}] = 0; + model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Recovered}] = 0; + model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Susceptible}] = + total_population - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Exposed}] - + model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Infected}] - + model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Recovered}]; + } - model.parameters.set>(5.2); - model.parameters.set>(6); - model.parameters.set>(0.1); + model.parameters.set>(3.); + model.parameters.set>(5.); + model.parameters.set>(1.); 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].get_baseline().setConstant(7.95); + // contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); model.check_constraints(); + std::shared_ptr> integrator = std::make_shared>(); - auto seir = simulate(t0, tmax, dt, model); + auto seir = simulate(t0, tmax, dt, model, integrator); + + auto reproduction_numbers = model.get_reproduction_numbers(seir); + std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; seir.print_table({"S", "E", "I", "R"}); - std::cout << "\nnumber total: " << seir.get_last_value().sum() << "\n"; + // std::cout << "\nnumber total: " << seir.get_last_value().sum() << "\n"; } diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 32d2685647..db065dcebf 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -10,95 +10,170 @@ #include "models/ode_seir_mobility_improved/regions.h" #include "memilio/io/io.h" #include "memilio/io/result_io.h" +#include "memilio/io/epi_data.h" #include template -mio::IOResult set_mobility_weights(const std::string& mobility_data, mio::oseirmobilityimproved::Model& model, - size_t number_regions) +mio::IOResult set_population_data(mio::oseirmobilityimproved::Model& model, + const std::string& population_data_path) { - // mobility between nodes - BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, - mio::read_mobility_plain(mobility_data + "/mobility" + "/commuter_migration_test.txt")); - if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || - mobility_data_commuter.cols() != Eigen::Index(number_regions)) { - return mio::failure(mio::StatusCode::InvalidValue, - "Mobility matrices do not have the correct size. You may need to run " - "transformMobilitydata.py from pycode memilio epidata package."); - } - - for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { - auto population_i = model.populations.get_group_total(mio::oseirmobilityimproved::Region(county_idx_i)); - mobility_data_commuter.row(county_idx_i) /= population_i; - mobility_data_commuter(county_idx_i, county_idx_i) = 1 - mobility_data_commuter.rowwise().sum()(county_idx_i); + BOOST_OUTCOME_TRY(auto&& node_ids, mio::get_node_ids(population_data_path, true, true)); + + BOOST_OUTCOME_TRY(const auto&& population_data, mio::read_population_data(population_data_path, true)); + + for (auto&& entry : population_data) { + auto it = std::find_if(node_ids.begin(), node_ids.end(), [&entry](auto r) { + return r == 0 || + (entry.county_id && mio::regions::StateId(r) == mio::regions::get_state_id(int(*entry.county_id))) || + (entry.county_id && mio::regions::CountyId(r) == *entry.county_id) || + (entry.district_id && mio::regions::DistrictId(r) == *entry.district_id); + }); + if (it != node_ids.end()) { + auto region_idx = size_t(it - node_ids.begin()); + for (size_t age = 0; age < (size_t)model.parameters.get_num_agegroups(); age++) { + model.populations[{mio::oseirmobilityimproved::Region(region_idx), mio::AgeGroup(age), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = + entry.population[mio::AgeGroup(age)]; + } + } } - model.parameters.template get>() - .get_cont_freq_mat()[0] - .get_baseline() = mobility_data_commuter; return mio::success(); } -int main() +template +mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& model, const std::string& mobility_data) { - mio::set_log_level(mio::LogLevel::debug); - - ScalarType t0 = 0.; - ScalarType tmax = 15.; - ScalarType dt = 0.1; - - ScalarType number_regions = 2; - std::vector region_ids(number_regions); - iota(region_ids.begin(), region_ids.end(), 1); - ScalarType number_age_groups = 1; - - mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); + size_t number_regions = (size_t)model.parameters.get_num_regions(); + if (number_regions == 1) { + model.parameters.template get>() + .get_cont_freq_mat()[0] + .get_baseline() + .setConstant(1.0); + + return mio::success(); + } + else { + // mobility between nodes + BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, mio::read_mobility_plain(mobility_data)); + if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || + mobility_data_commuter.cols() != Eigen::Index(number_regions)) { + return mio::failure(mio::StatusCode::InvalidValue, + "Mobility matrices do not have the correct size. You may need to run " + "transformMobilitydata.py from pycode memilio epidata package."); + } - const std::string& mobility_data = ""; + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + auto population_i = model.populations.get_group_total(mio::oseirmobilityimproved::Region(county_idx_i)); + auto test = population_i; + mobility_data_commuter.row(county_idx_i) /= population_i; + mio::unused(test); + mobility_data_commuter(county_idx_i, county_idx_i) = + 1 - mobility_data_commuter.rowwise().sum()(county_idx_i); + } + model.parameters.template get>() + .get_cont_freq_mat()[0] + .get_baseline() = mobility_data_commuter; - mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); - model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; - model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; - for (int i = 1; i < number_regions; i++) { - model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; - model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(0), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; + return mio::success(); } +} - model.parameters.set>(1.); - - model.parameters.set>(3.); - model.parameters.set>(5.); +template +mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Model& model, + const std::string& population_data_path, + const std::string& mobility_data) +{ + BOOST_OUTCOME_TRY(set_population_data(model, population_data_path)); + mio::unused(population_data_path); + + auto& populations = model.populations; + auto& parameters = model.parameters; + + size_t number_regions = (size_t)parameters.get_num_regions(); + size_t number_age_groups = (size_t)parameters.get_num_agegroups(); + + // for (size_t j = 0; j < number_age_groups; j++) { + // populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), + // mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; + // populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), + // mio::oseirmobilityimproved::InfectionState::Susceptible}] = 631207; + // auto test = populations.get_group_total(mio::oseirmobilityimproved::Region(0)); + // mio::unused(test); + // for (size_t i = 1; i < number_regions; i++) { + // model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), + // mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; + // model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), + // mio::oseirmobilityimproved::InfectionState::Susceptible}] = 503707; + // } + // } + + BOOST_OUTCOME_TRY(set_mobility_weights(model, mobility_data)); + + populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(3), + mio::oseirmobilityimproved::InfectionState::Susceptible}] -= 100; + populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(3), + mio::oseirmobilityimproved::InfectionState::Exposed}] += 100; 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.5, mio::SimulationTime(5)); + parameters.template get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(7.95); + + parameters.template set>(1.); + + parameters.template set>(3.); + parameters.template set>(5.); - auto result_preprocess = set_mobility_weights(mobility_data, model, number_regions); mio::ContactMatrixGroup& commuting_strengths = - model.parameters.get>().get_cont_freq_mat(); + parameters.template get>().get_cont_freq_mat(); - auto& population = model.m_population_after_commuting; - for (int region_n = 0; region_n < number_regions; ++region_n) { - for (int age = 0; age < number_age_groups; ++age) { + auto& population_after_commuting = model.m_population_after_commuting; + for (size_t region_n = 0; region_n < number_regions; ++region_n) { + for (size_t age = 0; age < number_age_groups; ++age) { double population_n = 0; for (size_t state = 0; state < (size_t)mio::oseirmobilityimproved::InfectionState::Count; state++) { - population_n += model.populations[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age), - mio::oseirmobilityimproved::InfectionState(state)}]; + population_n += populations[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age), + mio::oseirmobilityimproved::InfectionState(state)}]; } - population[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] += population_n; - for (int region_m = 0; region_m < number_regions; ++region_m) { - population[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] -= + population_after_commuting[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] += + population_n; + for (size_t region_m = 0; region_m < number_regions; ++region_m) { + population_after_commuting[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] -= commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; - population[{mio::oseirmobilityimproved::Region(region_m), mio::AgeGroup(age)}] += + population_after_commuting[{mio::oseirmobilityimproved::Region(region_m), mio::AgeGroup(age)}] += commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; + auto test = + population_after_commuting[{mio::oseirmobilityimproved::Region(region_m), mio::AgeGroup(age)}]; + mio::unused(test); } } } + + return mio::success(); +} + +int main() +{ + mio::set_log_level(mio::LogLevel::debug); + + ScalarType t0 = 0.; + ScalarType tmax = 1.0; + ScalarType dt = 0.1; + + ScalarType number_regions = 3; + std::vector region_ids(number_regions); + iota(region_ids.begin(), region_ids.end(), 1); + ScalarType number_age_groups = 6; + + mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); + + const std::string& mobility_data = ""; + const std::string& population_data = ""; + + mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); + auto result_prepare_simulation = set_parameters_and_population(model, population_data, mobility_data); + // using DefaultIntegratorCore = // mio::ControlledStepperWrapper; @@ -114,12 +189,11 @@ int main() std::chrono::duration ms_double = t2 - t1; printf("Runtime: %f\n", ms_double.count()); + result_from_sim.print_table({"S", "E", "I", "R"}); auto save_result_status = mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; - - result_from_sim.print_table(); } diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index aa29e3cb0a..182c46cc22 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -135,22 +135,19 @@ class Model : public FlowModel({(size_t)m, 1}); - auto const population_region_age = population_region.template slice({(size_t)j, 1}); + auto const population_region = pop.template slice({m.get(), 1}); + auto const population_region_age = population_region.template slice({j.get(), 1}); auto Njm = std::accumulate(population_region_age.begin(), population_region_age.end(), 0.); double coeffStoE = 0.5 * contact_matrix.get_matrix_at(y.get_time(t_idx))(i.get(), j.get()) * params.template get>()[i]; if (n == m) { - F((size_t)i * num_regions + (size_t)n, - num_age_groups * num_regions + (size_t)j * num_regions + (size_t)m) += - coeffStoE * y.get_value(t_idx)[Si] / Njm; + F(i.get() * num_regions + n.get(), num_age_groups * num_regions + j.get() * num_regions + + m.get()) += coeffStoE * y.get_value(t_idx)[Si] / Njm; } for (auto k = Region(0); k < Region(num_regions); k++) { - auto test = commuting_strengths.get_matrix_at(y.get_time(t_idx))(n.get(), k.get()); - mio::unused(test); - F((size_t)i * num_regions + (size_t)n, - num_age_groups * num_regions + (size_t)j * num_regions + (size_t)m) += + F(i.get() * num_regions + n.get(), + num_age_groups * num_regions + j.get() * num_regions + m.get()) += coeffStoE * y.get_value(t_idx)[Si] * commuting_strengths.get_matrix_at(y.get_time(t_idx))(n.get(), k.get()) * commuting_strengths.get_matrix_at(y.get_time(t_idx))(m.get(), k.get()) / @@ -161,11 +158,11 @@ class Model : public FlowModel>()[i]; double T_Ii = params.template get>()[i]; - V((size_t)i * num_regions + (size_t)n, (size_t)i * num_regions + (size_t)n) = 1.0 / T_Ei; - V(num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n, - (size_t)i * num_regions + (size_t)n) = -1.0 / T_Ei; - V(num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n, - num_age_groups * num_regions + (size_t)i * num_regions + (size_t)n) = 1.0 / T_Ii; + V(i.get() * num_regions + n.get(), i.get() * num_regions + n.get()) = 1.0 / T_Ei; + V(num_age_groups * num_regions + i.get() * num_regions + n.get(), i.get() * num_regions + n.get()) = + -1.0 / T_Ei; + V(num_age_groups * num_regions + i.get() * num_regions + n.get(), + num_age_groups * num_regions + i.get() * num_regions + n.get()) = 1.0 / T_Ii; } } From ce86fb6bd7e5f904aca4c0202b6d23a5486561a0 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:00:17 +0100 Subject: [PATCH 33/49] debugging artefacts --- cpp/examples/ode_seir_mobility_improved.cpp | 23 ++------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index db065dcebf..f03421a0de 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -86,7 +86,6 @@ mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Mo const std::string& mobility_data) { BOOST_OUTCOME_TRY(set_population_data(model, population_data_path)); - mio::unused(population_data_path); auto& populations = model.populations; auto& parameters = model.parameters; @@ -94,21 +93,6 @@ mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Mo size_t number_regions = (size_t)parameters.get_num_regions(); size_t number_age_groups = (size_t)parameters.get_num_agegroups(); - // for (size_t j = 0; j < number_age_groups; j++) { - // populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - // mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; - // populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - // mio::oseirmobilityimproved::InfectionState::Susceptible}] = 631207; - // auto test = populations.get_group_total(mio::oseirmobilityimproved::Region(0)); - // mio::unused(test); - // for (size_t i = 1; i < number_regions; i++) { - // model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), - // mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; - // model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), - // mio::oseirmobilityimproved::InfectionState::Susceptible}] = 503707; - // } - // } - BOOST_OUTCOME_TRY(set_mobility_weights(model, mobility_data)); populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(3), @@ -143,9 +127,6 @@ mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Mo commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; population_after_commuting[{mio::oseirmobilityimproved::Region(region_m), mio::AgeGroup(age)}] += commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; - auto test = - population_after_commuting[{mio::oseirmobilityimproved::Region(region_m), mio::AgeGroup(age)}]; - mio::unused(test); } } } @@ -158,10 +139,10 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0.; - ScalarType tmax = 1.0; + ScalarType tmax = 0.2; ScalarType dt = 0.1; - ScalarType number_regions = 3; + ScalarType number_regions = 53; std::vector region_ids(number_regions); iota(region_ids.begin(), region_ids.end(), 1); ScalarType number_age_groups = 6; From d3781189cb410233830adc0f7c6b7ea9e02e32ae Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:38:00 +0100 Subject: [PATCH 34/49] more cleanup and provide demo population for a number of agegroups different than 6 --- cpp/examples/ode_seir_mobility_improved.cpp | 35 +++++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index f03421a0de..0ab5a3d46d 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -39,6 +39,7 @@ mio::IOResult set_population_data(mio::oseirmobilityimproved::Model& m } } + printf("Setting population data successful.\n"); return mio::success(); } @@ -66,9 +67,7 @@ mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { auto population_i = model.populations.get_group_total(mio::oseirmobilityimproved::Region(county_idx_i)); - auto test = population_i; mobility_data_commuter.row(county_idx_i) /= population_i; - mio::unused(test); mobility_data_commuter(county_idx_i, county_idx_i) = 1 - mobility_data_commuter.rowwise().sum()(county_idx_i); } @@ -76,6 +75,7 @@ mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& .get_cont_freq_mat()[0] .get_baseline() = mobility_data_commuter; + printf("Setting mobility weights successful.\n"); return mio::success(); } } @@ -85,21 +85,36 @@ mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Mo const std::string& population_data_path, const std::string& mobility_data) { - BOOST_OUTCOME_TRY(set_population_data(model, population_data_path)); - auto& populations = model.populations; auto& parameters = model.parameters; size_t number_regions = (size_t)parameters.get_num_regions(); size_t number_age_groups = (size_t)parameters.get_num_agegroups(); + if (number_age_groups != 6) { + printf("Data is not compatible, using demo population instead.\n"); + for (size_t j = 0; j < number_age_groups; j++) { + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; + for (size_t i = 1; i < number_regions; i++) { + model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; + model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; + } + } + } + else { + BOOST_OUTCOME_TRY(set_population_data(model, population_data_path)); + populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(1), + mio::oseirmobilityimproved::InfectionState::Susceptible}] -= 100; + populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(1), + mio::oseirmobilityimproved::InfectionState::Exposed}] += 100; + } BOOST_OUTCOME_TRY(set_mobility_weights(model, mobility_data)); - populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(3), - mio::oseirmobilityimproved::InfectionState::Susceptible}] -= 100; - populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(3), - mio::oseirmobilityimproved::InfectionState::Exposed}] += 100; - mio::ContactMatrixGroup& contact_matrix = parameters.template get>().get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(7.95); @@ -170,7 +185,7 @@ int main() std::chrono::duration ms_double = t2 - t1; printf("Runtime: %f\n", ms_double.count()); - result_from_sim.print_table({"S", "E", "I", "R"}); + result_from_sim.print_table(); auto save_result_status = mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); From 33a2dd585a792f0985af8822ab92e039e9c331dd Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:12:46 +0100 Subject: [PATCH 35/49] read in contact matrices, set age resolved parameters, restructure --- cpp/examples/ode_seir_mobility_improved.cpp | 130 ++++++++++++++++---- 1 file changed, 108 insertions(+), 22 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 0ab5a3d46d..128dd295a4 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -14,13 +14,105 @@ #include +/** + * Set epidemiological parameters of Sars-CoV-2 for a immune-naive + * population and wild type variant. + * @param params Object that the parameters will be added to. + * @returns Currently generates no errors. + */ +mio::IOResult set_covid_parameters(mio::oseirmobilityimproved::Parameters& params) +{ + params.template set>(3.335); + if ((size_t)params.get_num_agegroups() == 6) { + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; + } + else { + params.template set>(8.); + + params.template set>(0.07); + } + + printf("Setting epidemiological parameters successful.\n"); + return mio::success(); +} + +/** + * indices of contact matrix corresponding to locations where contacts occur. + */ +enum class ContactLocation +{ + Home = 0, + School, + Work, + Other, + Count, +}; + +static const std::map contact_locations = {{ContactLocation::Home, "home"}, + {ContactLocation::School, "school_pf_eig"}, + {ContactLocation::Work, "work"}, + {ContactLocation::Other, "other"}}; + +/** + * Set contact matrices. + * Reads contact matrices from files in the data directory. + * @param data_dir data directory. + * @param params Object that the contact matrices will be added to. + * @returns any io errors that happen during reading of the files. + */ +mio::IOResult set_contact_matrices(const fs::path& data_dir, + mio::oseirmobilityimproved::Parameters& params) +{ + if ((size_t)params.get_num_agegroups() == 6) { + //TODO: io error handling + auto contact_matrices = mio::ContactMatrixGroup(contact_locations.size(), size_t(params.get_num_agegroups())); + for (auto&& contact_location : contact_locations) { + BOOST_OUTCOME_TRY(auto&& baseline, + mio::read_mobility_plain( + (data_dir / "contacts" / ("baseline_" + contact_location.second + ".txt")).string())); + BOOST_OUTCOME_TRY(auto&& minimum, + mio::read_mobility_plain( + (data_dir / "contacts" / ("minimum_" + contact_location.second + ".txt")).string())); + contact_matrices[size_t(contact_location.first)].get_baseline() = baseline; + contact_matrices[size_t(contact_location.first)].get_minimum() = minimum; + } + params.get>() = + mio::UncertainContactMatrix(contact_matrices); + } + + else { + mio::ContactMatrixGroup& contact_matrix = + params.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(7.95 / (size_t)params.get_num_agegroups()); + } + + printf("Setting contact matrices successful.\n"); + return mio::success(); +} + template -mio::IOResult set_population_data(mio::oseirmobilityimproved::Model& model, - const std::string& population_data_path) +mio::IOResult set_population_data(mio::oseirmobilityimproved::Model& model, const fs::path& data_dir) { - BOOST_OUTCOME_TRY(auto&& node_ids, mio::get_node_ids(population_data_path, true, true)); + BOOST_OUTCOME_TRY( + auto&& node_ids, + mio::get_node_ids((data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true, + true)); - BOOST_OUTCOME_TRY(const auto&& population_data, mio::read_population_data(population_data_path, true)); + BOOST_OUTCOME_TRY(const auto&& population_data, + mio::read_population_data( + (data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true)); for (auto&& entry : population_data) { auto it = std::find_if(node_ids.begin(), node_ids.end(), [&entry](auto r) { @@ -44,7 +136,7 @@ mio::IOResult set_population_data(mio::oseirmobilityimproved::Model& m } template -mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& model, const std::string& mobility_data) +mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& model, const fs::path& data_dir) { size_t number_regions = (size_t)model.parameters.get_num_regions(); if (number_regions == 1) { @@ -57,7 +149,8 @@ mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& } else { // mobility between nodes - BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, mio::read_mobility_plain(mobility_data)); + BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, + mio::read_mobility_plain((data_dir / "mobility" / "commuter_mobility_nrw.txt").string())); if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || mobility_data_commuter.cols() != Eigen::Index(number_regions)) { return mio::failure(mio::StatusCode::InvalidValue, @@ -82,8 +175,7 @@ mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& template mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Model& model, - const std::string& population_data_path, - const std::string& mobility_data) + const fs::path& data_dir) { auto& populations = model.populations; auto& parameters = model.parameters; @@ -97,32 +189,27 @@ mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Mo model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 99990; for (size_t i = 1; i < number_regions; i++) { model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 100000; } } } else { - BOOST_OUTCOME_TRY(set_population_data(model, population_data_path)); + BOOST_OUTCOME_TRY(set_population_data(model, data_dir)); populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(1), mio::oseirmobilityimproved::InfectionState::Susceptible}] -= 100; populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(1), mio::oseirmobilityimproved::InfectionState::Exposed}] += 100; } - BOOST_OUTCOME_TRY(set_mobility_weights(model, mobility_data)); - - mio::ContactMatrixGroup& contact_matrix = - parameters.template get>().get_cont_freq_mat(); - contact_matrix[0].get_baseline().setConstant(7.95); + BOOST_OUTCOME_TRY(set_mobility_weights(model, data_dir)); - parameters.template set>(1.); + BOOST_OUTCOME_TRY(set_contact_matrices(data_dir, parameters)) - parameters.template set>(3.); - parameters.template set>(5.); + BOOST_OUTCOME_TRY(set_covid_parameters(parameters)); mio::ContactMatrixGroup& commuting_strengths = parameters.template get>().get_cont_freq_mat(); @@ -164,11 +251,10 @@ int main() mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - const std::string& mobility_data = ""; - const std::string& population_data = ""; + const std::string& data_dir = ""; mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); - auto result_prepare_simulation = set_parameters_and_population(model, population_data, mobility_data); + auto result_prepare_simulation = set_parameters_and_population(model, data_dir); // using DefaultIntegratorCore = // mio::ControlledStepperWrapper; From c1bd747660fc6be40be5e954ff69bb1bf7ca4701 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:11:08 +0100 Subject: [PATCH 36/49] add bool for using population from data --- cpp/examples/ode_seir_mobility_improved.cpp | 43 ++++++++++++--------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 128dd295a4..c3ff687a90 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -20,10 +20,11 @@ * @param params Object that the parameters will be added to. * @returns Currently generates no errors. */ -mio::IOResult set_covid_parameters(mio::oseirmobilityimproved::Parameters& params) +mio::IOResult set_covid_parameters(mio::oseirmobilityimproved::Parameters& params, + bool synthetic_population) { params.template set>(3.335); - if ((size_t)params.get_num_agegroups() == 6) { + if (!synthetic_population) { params.get>()[mio::AgeGroup(0)] = 8.0096875; params.get>()[mio::AgeGroup(1)] = 8.0096875; params.get>()[mio::AgeGroup(2)] = 8.2182; @@ -73,9 +74,10 @@ static const std::map contact_locations = {{Contac * @returns any io errors that happen during reading of the files. */ mio::IOResult set_contact_matrices(const fs::path& data_dir, - mio::oseirmobilityimproved::Parameters& params) + mio::oseirmobilityimproved::Parameters& params, + bool synthetic_population) { - if ((size_t)params.get_num_agegroups() == 6) { + if (!synthetic_population) { //TODO: io error handling auto contact_matrices = mio::ContactMatrixGroup(contact_locations.size(), size_t(params.get_num_agegroups())); for (auto&& contact_location : contact_locations) { @@ -91,7 +93,6 @@ mio::IOResult set_contact_matrices(const fs::path& data_dir, params.get>() = mio::UncertainContactMatrix(contact_matrices); } - else { mio::ContactMatrixGroup& contact_matrix = params.get>().get_cont_freq_mat(); @@ -175,7 +176,7 @@ mio::IOResult set_mobility_weights(mio::oseirmobilityimproved::Model& template mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Model& model, - const fs::path& data_dir) + const fs::path& data_dir, bool synthetic_population) { auto& populations = model.populations; auto& parameters = model.parameters; @@ -183,33 +184,33 @@ mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Mo size_t number_regions = (size_t)parameters.get_num_regions(); size_t number_age_groups = (size_t)parameters.get_num_agegroups(); - if (number_age_groups != 6) { - printf("Data is not compatible, using demo population instead.\n"); + if (synthetic_population) { + printf("Data is not compatible, using synthetic population instead.\n"); for (size_t j = 0; j < number_age_groups; j++) { model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 99990; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; for (size_t i = 1; i < number_regions; i++) { model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 100000; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; } } } else { BOOST_OUTCOME_TRY(set_population_data(model, data_dir)); - populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(1), + populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), mio::oseirmobilityimproved::InfectionState::Susceptible}] -= 100; - populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(1), + populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), mio::oseirmobilityimproved::InfectionState::Exposed}] += 100; } BOOST_OUTCOME_TRY(set_mobility_weights(model, data_dir)); - BOOST_OUTCOME_TRY(set_contact_matrices(data_dir, parameters)) + BOOST_OUTCOME_TRY(set_contact_matrices(data_dir, parameters, synthetic_population)) - BOOST_OUTCOME_TRY(set_covid_parameters(parameters)); + BOOST_OUTCOME_TRY(set_covid_parameters(parameters, synthetic_population)); mio::ContactMatrixGroup& commuting_strengths = parameters.template get>().get_cont_freq_mat(); @@ -241,20 +242,24 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0.; - ScalarType tmax = 0.2; + ScalarType tmax = 5.; ScalarType dt = 0.1; ScalarType number_regions = 53; std::vector region_ids(number_regions); iota(region_ids.begin(), region_ids.end(), 1); ScalarType number_age_groups = 6; + bool synthetic_population = false; + if (number_age_groups != 6) { + synthetic_population = true; + } mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); const std::string& data_dir = ""; mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); - auto result_prepare_simulation = set_parameters_and_population(model, data_dir); + auto result_prepare_simulation = set_parameters_and_population(model, data_dir, synthetic_population); // using DefaultIntegratorCore = // mio::ControlledStepperWrapper; @@ -271,11 +276,11 @@ int main() std::chrono::duration ms_double = t2 - t1; printf("Runtime: %f\n", ms_double.count()); - result_from_sim.print_table(); + // result_from_sim.print_table(); auto save_result_status = mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); - auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); - std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; + // auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); + // std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; } From df22f5417e4abf0e4cfefb6591b0df0e7c010842 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:12:19 +0100 Subject: [PATCH 37/49] read in data for graph model --- cpp/examples/graph_extended.cpp | 213 ++++++++++++++++++++++++++++---- 1 file changed, 188 insertions(+), 25 deletions(-) diff --git a/cpp/examples/graph_extended.cpp b/cpp/examples/graph_extended.cpp index e963c1e631..67e671ac89 100644 --- a/cpp/examples/graph_extended.cpp +++ b/cpp/examples/graph_extended.cpp @@ -5,45 +5,209 @@ #include "memilio/mobility/metapopulation_mobility_instant.h" #include "memilio/compartments/simulation.h" #include "memilio/io/result_io.h" +#include "memilio/io/epi_data.h" #include +/** + * indices of contact matrix corresponding to locations where contacts occur. + */ +enum class ContactLocation +{ + Home = 0, + School, + Work, + Other, + Count, +}; + +static const std::map contact_locations = {{ContactLocation::Home, "home"}, + {ContactLocation::School, "school_pf_eig"}, + {ContactLocation::Work, "work"}, + {ContactLocation::Other, "other"}}; + +/** + * Set contact matrices. + * Reads contact matrices from files in the data directory. + * @param data_dir data directory. + * @param params Object that the contact matrices will be added to. + * @returns any io errors that happen during reading of the files. + */ +mio::IOResult set_contact_matrices(const fs::path& data_dir, mio::oseir::Parameters& params, + bool synthetic_population) +{ + if (!synthetic_population) { + //TODO: io error handling + auto contact_matrices = mio::ContactMatrixGroup(contact_locations.size(), size_t(params.get_num_groups())); + for (auto&& contact_location : contact_locations) { + BOOST_OUTCOME_TRY(auto&& baseline, + mio::read_mobility_plain( + (data_dir / "contacts" / ("baseline_" + contact_location.second + ".txt")).string())); + BOOST_OUTCOME_TRY(auto&& minimum, + mio::read_mobility_plain( + (data_dir / "contacts" / ("minimum_" + contact_location.second + ".txt")).string())); + contact_matrices[size_t(contact_location.first)].get_baseline() = baseline; + contact_matrices[size_t(contact_location.first)].get_minimum() = minimum; + } + params.get>() = mio::UncertainContactMatrix(contact_matrices); + } + else { + mio::ContactMatrixGroup& contact_matrix = params.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(7.95 / (size_t)params.get_num_groups()); + } + + printf("Setting contact matrices successful.\n"); + return mio::success(); +} + +/** + * Set epidemiological parameters of Sars-CoV-2 for a immune-naive + * population and wild type variant. + * @param params Object that the parameters will be added to. + * @returns Currently generates no errors. + */ +mio::IOResult set_covid_parameters(mio::oseir::Parameters& params, bool synthetic_population) +{ + params.template set>(3.335); + if (!synthetic_population) { + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; + } + else { + params.template set>(8.); + + params.template set>(0.07); + } + + printf("Setting epidemiological parameters successful.\n"); + return mio::success(); +} + +mio::IOResult>> set_population_data(const fs::path& data_dir, + mio::oseir::Parameters& params) +{ + BOOST_OUTCOME_TRY( + auto&& node_ids, + mio::get_node_ids((data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true, + true)); + size_t number_regions = node_ids.size(); + + std::vector> nodes(number_regions, + mio::oseir::Model(int(size_t(params.get_num_groups())))); + + for (auto& node : nodes) { + node.parameters = params; + } + + BOOST_OUTCOME_TRY(const auto&& population_data, + mio::read_population_data( + (data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true)); + + std::vector> vnum_population(node_ids.size(), + std::vector((size_t)params.get_num_groups(), 0.0)); + + for (auto&& entry : population_data) { + auto it = std::find_if(node_ids.begin(), node_ids.end(), [&entry](auto r) { + return r == 0 || + (entry.county_id && mio::regions::StateId(r) == mio::regions::get_state_id(int(*entry.county_id))) || + (entry.county_id && mio::regions::CountyId(r) == *entry.county_id) || + (entry.district_id && mio::regions::DistrictId(r) == *entry.district_id); + }); + if (it != node_ids.end()) { + auto region_idx = size_t(it - node_ids.begin()); + auto& num_population = vnum_population[region_idx]; + for (size_t age = 0; age < num_population.size(); age++) { + num_population[age] += entry.population[mio::AgeGroup(age)]; + } + } + } + + for (size_t region = 0; region < node_ids.size(); region++) { + auto num_groups = nodes[region].parameters.get_num_groups(); + for (auto i = mio::AgeGroup(0); i < num_groups; i++) { + nodes[region].populations.template set_difference_from_group_total( + {i, mio::oseir::InfectionState::Susceptible}, vnum_population[region][size_t(i)]); + } + } + nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] -= 100; + nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] += 100; + + return mio::success(nodes); +} + +mio::IOResult>> +set_synthetic_population_data(mio::oseir::Parameters& params) +{ + size_t number_regions = 3; + + std::vector> nodes(number_regions, + mio::oseir::Model(int(size_t(params.get_num_groups())))); + + mio::Populations population( + {params.get_num_groups(), mio::oseir::InfectionState::Count}); + + for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { + population[{i, mio::oseir::InfectionState::Susceptible}] = 10000; + } + for (auto& node : nodes) { + node.parameters = params; + node.populations = population; + } + for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { + nodes[0].populations[{i, mio::oseir::InfectionState::Exposed}] = 10; + nodes[0].populations[{i, mio::oseir::InfectionState::Susceptible}] = 9990; + } + + return mio::success(nodes); +} + mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double dt) { // global parameters - const int num_age_groups = 1; + bool synthetic_population = false; + const int num_age_groups = 6; + if (num_age_groups != 6) { + synthetic_population = true; + } mio::oseir::Parameters params(num_age_groups); - mio::Populations population( - {mio::AgeGroup(num_age_groups), mio::oseir::InfectionState::Count}); - params.set>(1.); - // set transition times - params.set>(3.); - params.set>(5.); + BOOST_OUTCOME_TRY(set_covid_parameters(params, synthetic_population)); // set contact matrix - params.get>().get_cont_freq_mat()[0].get_baseline().setConstant(7.95); - - population[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = 10000; + BOOST_OUTCOME_TRY(set_contact_matrices(data_dir, params, synthetic_population)); // graph of counties with populations and local parameters // and mobility between counties mio::Graph>>, mio::MobilityEdge<>> params_graph; - std::vector> nodes(2, mio::oseir::Model(int(size_t(params.get_num_groups())))); - for (auto& node : nodes) { - node.parameters = params; - node.populations = population; + if (synthetic_population) { + BOOST_OUTCOME_TRY(auto&& nodes, set_synthetic_population_data(params)); + for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { + params_graph.add_node(node_idx, nodes[node_idx]); + } + printf("Setting synthetic population successful.\n"); } - nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] = 9990; - nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] = 10; - - for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { - params_graph.add_node(node_idx, nodes[node_idx]); + else { + BOOST_OUTCOME_TRY(auto&& nodes, set_population_data(data_dir, params)); + for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { + params_graph.add_node(node_idx, nodes[node_idx]); + } + printf("Setting population from data successful.\n"); } BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, - mio::read_mobility_plain((data_dir / "mobility" / "commuter_migration_test.txt").string())); + mio::read_mobility_plain((data_dir / "mobility" / "commuter_mobility_nrw.txt").string())); if (mobility_data_commuter.rows() != Eigen::Index(params_graph.nodes().size()) || mobility_data_commuter.cols() != Eigen::Index(params_graph.nodes().size())) { return mio::failure(mio::StatusCode::InvalidValue, @@ -58,7 +222,8 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double auto commuter_coeff_ij = mobility_data_commuter(county_idx_i, county_idx_j) / populations.get_total(); params_graph.add_edge( county_idx_i, county_idx_j, - Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count, commuter_coeff_ij)); + Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count * size_t(params.get_num_groups()), + commuter_coeff_ij)); } } @@ -88,9 +253,7 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double auto save_result_status = save_result(result, county_ids, 1, "graph_result.h5"); result_graph.nodes()[0].property.get_result().print_table(); result_graph.nodes()[1].property.get_result().print_table(); - // for (auto&& node : result_graph.nodes()) { - // node.property.get_result().print_table(); - // } + result_graph.nodes()[2].property.get_result().print_table(); return mio::success(); } @@ -98,7 +261,7 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double int main() { const auto t0 = 0.; - const auto tmax = 1.; + const auto tmax = 5.; const auto dt = 0.5; //time step of mobility, daily mobility every second step const std::string& data_dir = ""; From 7b307794d6282422afb32e3058fe759e98d98b27 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:12:32 +0100 Subject: [PATCH 38/49] simulation for nrw, plots and a bug fix --- cpp/examples/graph_extended.cpp | 39 +-- cpp/examples/ode_seir_mobility_improved.cpp | 18 +- cpp/models/ode_seir_mobility_improved/model.h | 2 +- pycode/examples/plot/plotResultsMapNRW.py | 235 ++++++++++++++++++ .../memilio/epidata/getNRWCounties.py | 54 ++++ 5 files changed, 319 insertions(+), 29 deletions(-) create mode 100644 pycode/examples/plot/plotResultsMapNRW.py create mode 100644 pycode/memilio-epidata/memilio/epidata/getNRWCounties.py diff --git a/cpp/examples/graph_extended.cpp b/cpp/examples/graph_extended.cpp index 67e671ac89..311994f33e 100644 --- a/cpp/examples/graph_extended.cpp +++ b/cpp/examples/graph_extended.cpp @@ -85,22 +85,18 @@ mio::IOResult set_covid_parameters(mio::oseir::Parameters& params, params.get>()[mio::AgeGroup(5)] = 0.175; } else { - params.template set>(8.); + params.template set>(8.097612257); - params.template set>(0.07); + params.template set>(0.07333); } printf("Setting epidemiological parameters successful.\n"); return mio::success(); } -mio::IOResult>> set_population_data(const fs::path& data_dir, - mio::oseir::Parameters& params) +mio::IOResult>> +set_population_data(const fs::path& data_dir, mio::oseir::Parameters& params, std::vector node_ids) { - BOOST_OUTCOME_TRY( - auto&& node_ids, - mio::get_node_ids((data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true, - true)); size_t number_regions = node_ids.size(); std::vector> nodes(number_regions, @@ -140,8 +136,8 @@ mio::IOResult>> set_population_data(const {i, mio::oseir::InfectionState::Susceptible}, vnum_population[region][size_t(i)]); } } - nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] -= 100; - nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] += 100; + nodes[27].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] -= 100; + nodes[27].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] += 100; return mio::success(nodes); } @@ -149,7 +145,7 @@ mio::IOResult>> set_population_data(const mio::IOResult>> set_synthetic_population_data(mio::oseir::Parameters& params) { - size_t number_regions = 3; + size_t number_regions = 53; std::vector> nodes(number_regions, mio::oseir::Model(int(size_t(params.get_num_groups())))); @@ -158,15 +154,15 @@ set_synthetic_population_data(mio::oseir::Parameters& params) {params.get_num_groups(), mio::oseir::InfectionState::Count}); for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { - population[{i, mio::oseir::InfectionState::Susceptible}] = 10000; + population[{i, mio::oseir::InfectionState::Susceptible}] = 1000000; } for (auto& node : nodes) { node.parameters = params; node.populations = population; } for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { - nodes[0].populations[{i, mio::oseir::InfectionState::Exposed}] = 10; - nodes[0].populations[{i, mio::oseir::InfectionState::Susceptible}] = 9990; + nodes[0].populations[{i, mio::oseir::InfectionState::Exposed}] = 100; + nodes[0].populations[{i, mio::oseir::InfectionState::Susceptible}] = 999900; } return mio::success(nodes); @@ -191,17 +187,22 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double // and mobility between counties mio::Graph>>, mio::MobilityEdge<>> params_graph; + BOOST_OUTCOME_TRY( + auto&& node_ids, + mio::get_node_ids((data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true, + true)); + if (synthetic_population) { BOOST_OUTCOME_TRY(auto&& nodes, set_synthetic_population_data(params)); for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { - params_graph.add_node(node_idx, nodes[node_idx]); + params_graph.add_node(node_ids[node_idx], nodes[node_idx]); } printf("Setting synthetic population successful.\n"); } else { - BOOST_OUTCOME_TRY(auto&& nodes, set_population_data(data_dir, params)); + BOOST_OUTCOME_TRY(auto&& nodes, set_population_data(data_dir, params, node_ids)); for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { - params_graph.add_node(node_idx, nodes[node_idx]); + params_graph.add_node(node_ids[node_idx], nodes[node_idx]); } printf("Setting population from data successful.\n"); } @@ -250,7 +251,7 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double return n.id; }); - auto save_result_status = save_result(result, county_ids, 1, "graph_result.h5"); + auto save_result_status = save_result(result, county_ids, num_age_groups, "graph_result_nrw.h5"); result_graph.nodes()[0].property.get_result().print_table(); result_graph.nodes()[1].property.get_result().print_table(); result_graph.nodes()[2].property.get_result().print_table(); @@ -261,7 +262,7 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double int main() { const auto t0 = 0.; - const auto tmax = 5.; + const auto tmax = 15.; const auto dt = 0.5; //time step of mobility, daily mobility every second step const std::string& data_dir = ""; diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index c3ff687a90..7a2577b517 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -40,9 +40,9 @@ mio::IOResult set_covid_parameters(mio::oseirmobilityimproved::Parameters< params.get>()[mio::AgeGroup(5)] = 0.175; } else { - params.template set>(8.); + params.template set>(8.097612257); - params.template set>(0.07); + params.template set>(0.07333); } printf("Setting epidemiological parameters successful.\n"); @@ -188,22 +188,22 @@ mio::IOResult set_parameters_and_population(mio::oseirmobilityimproved::Mo printf("Data is not compatible, using synthetic population instead.\n"); for (size_t j = 0; j < number_age_groups; j++) { model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 10; + mio::oseirmobilityimproved::InfectionState::Exposed}] = 100; model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 9990; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 999900; for (size_t i = 1; i < number_regions; i++) { model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 1000000; } } } else { BOOST_OUTCOME_TRY(set_population_data(model, data_dir)); - populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), + populations[{mio::oseirmobilityimproved::Region(27), mio::AgeGroup(0), mio::oseirmobilityimproved::InfectionState::Susceptible}] -= 100; - populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), + populations[{mio::oseirmobilityimproved::Region(27), mio::AgeGroup(0), mio::oseirmobilityimproved::InfectionState::Exposed}] += 100; } BOOST_OUTCOME_TRY(set_mobility_weights(model, data_dir)); @@ -242,7 +242,7 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0.; - ScalarType tmax = 5.; + ScalarType tmax = 15.; ScalarType dt = 0.1; ScalarType number_regions = 53; @@ -279,7 +279,7 @@ int main() // result_from_sim.print_table(); auto save_result_status = - mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_test.h5"); + mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_nrw.h5"); // auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); // std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index 182c46cc22..4853be89ad 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -90,7 +90,7 @@ class Model : public FlowModel( {region_n, age_i})] += flow_SE_helper * coeffStoI * - y[population.get_flat_index({region_n, age_j, InfectionState::Susceptible})]; + y[population.get_flat_index({region_n, age_i, InfectionState::Susceptible})]; } } for (auto region : make_index_range(n_regions)) { diff --git a/pycode/examples/plot/plotResultsMapNRW.py b/pycode/examples/plot/plotResultsMapNRW.py new file mode 100644 index 0000000000..ff0f5692a2 --- /dev/null +++ b/pycode/examples/plot/plotResultsMapNRW.py @@ -0,0 +1,235 @@ + +import datetime as dt +import os.path + +import numpy as np +import pandas as pd + +import geopandas +from matplotlib.gridspec import GridSpec + +from memilio.epidata import geoModificationGermany as geoger + +import memilio.epidata.getPopulationData as gpd +from memilio.epidata import getDataIntoPandasDataFrame as gd +import memilio.plot.plotMap as pm + +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors + + +def plot_map_nrw(data: pd.DataFrame, + scale_colors: np.array([0, 1]), + legend: list = [], + title: str = '', + plot_colorbar: bool = True, + output_path: str = '', + fig_name: str = 'customPlot', + dpi: int = 300, + outercolor='white', + log_scale=False): + """! Plots region-specific information onto a interactive html map and + returning svg and png image. Allows the comparisons of a variable list of + data sets. + + @param[in] data Data to be plotted. First column must contain regional + specifier, following columns will be plotted for comparison. + @param[in] scale_colors Array of min-max-values to scale colorbar. + @param[in] legend List subtitles for different columns. Can be list of + empty strings. + @param[in] title Title of the plot. + @param[in] plot_colorbar Defines if a colorbar will be plotted. + @param[in] output_path Output path for the figure. + @param[in] fig_name Name of the figure created. + @param[in] dpi Dots-per-inch value for the exported figure. + @param[in] outercolor Background color of the plot image. + @param[in] log_scale Defines if the colorbar is plotted in log scale. + """ + region_classifier = data.columns[0] + region_data = data[region_classifier].to_numpy().astype(int) + + data_columns = data.columns[1:] + # Read and filter map data. + if np.isin(region_data, geoger.get_county_ids()).all(): + try: + map_data = geopandas.read_file( + os.path.join( + os.getcwd(), + 'tools/vg2500_12-31.utm32s.shape/vg2500/VG2500_KRS.shp')) + if '16056' in map_data.ARS.values: + map_data = pm.merge_eisenach(map_data) + # Remove information for plot. + map_data = map_data[['ARS', 'GEN', 'NUTS', 'geometry']] + # Use string values as in shape data file. + data[region_classifier] = data[region_classifier].astype( + 'str').str.zfill(5) + except FileNotFoundError: + pm.print_manual_download( + 'Georeferenzierung: UTM32s, Format: shape (ZIP, 5 MB)', + 'https://gdz.bkg.bund.de/index.php/default/verwaltungsgebiete-1-2-500-000-stand-31-12-vg2500-12-31.html') + else: + raise gd.DataError('Provide shape files regions to be plotted.') + + # Remove regions that are not input data table. + map_data = map_data[map_data.ARS.isin(data[region_classifier])] + + data['new_index'] = map_data.index.array + data = data.set_index('new_index') + + map_data[data_columns] = data.loc[:, data_columns] + + for i in range(len(data_columns)): + if legend[i] == '': + fname = 'data_column_' + str(i) + else: + fname = str(legend[i].replace(' ', '_')) + pm.save_interactive(data[data_columns[i]], os.path.join( + output_path, fname) + '.html', map_data, scale_colors) + + fig = plt.figure(figsize=(3.5 * len(data_columns), 3), facecolor=outercolor) + # Use n+2 many columns (1: legend + 2: empty space + 3-n: data sets) and + # n+2 rows where the top row is used for a potential title, the second row + # for the content and all other rows have height zero. + height_ratios = [0.05, 1, 0] + if len(data_columns) > 1: + height_ratios = height_ratios + [ + 0.0 for i in range(len(data_columns)-1)] + gs = GridSpec( + len(data_columns) + 2, len(data_columns) + 2, figure=fig, + width_ratios=[1 for i in range(len(data_columns))] + [0.1, 0.2], + height_ratios=height_ratios) + + # Use top row for title. + tax = fig.add_subplot(gs[0, :]) + tax.set_axis_off() + tax.set_title(title, fontsize=16) + if plot_colorbar: + # Prepare colorbar. + cax = fig.add_subplot(gs[1, -2]) + + else: + cax = None + + if log_scale: + norm = mcolors.LogNorm(vmin=scale_colors[0], vmax=scale_colors[1]) + + for i in range(len(data_columns)): + + cmap = 'viridis' + ax = fig.add_subplot(gs[1, i]) + if log_scale: + map_data.plot(data_columns[i], ax=ax, legend=False, + norm=norm, cmap=cmap) + + elif cax is not None: + map_data.plot(data_columns[i], ax=ax, cax=cax, legend=True, + vmin=scale_colors[0], vmax=scale_colors[1]) + else: + # Do not plot colorbar. + map_data.plot(data_columns[i], ax=ax, legend=False, + vmin=scale_colors[0], vmax=scale_colors[1]) + + ax.set_title(legend[i], fontsize=12) + ax.set_axis_off() + + sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) + sm.set_array([]) + cbar = fig.colorbar(sm, cax=cax) + cbar.set_ticks([scale_colors[0], scale_colors[1]]) + cbar.set_ticklabels([f'{scale_colors[0]:.4e}', f'{scale_colors[1]:.4e}']) + + plt.subplots_adjust(bottom=0.1) + plt.savefig(os.path.join(output_path, fig_name + '.png'), dpi=dpi) + + +if __name__ == '__main__': + + files_input = {'Data set 1': 'cpp/build/ode_result_nrw', + 'Data set 2': 'cpp/build/graph_result_nrw'} # Result file of equation-based model has to be first + file_format = 'h5' + # Define age groups which will be considered through filtering + # Keep keys and values as well as its assignment constant, remove entries + # if only part of the population should be plotted or considered, e.g., by + # setting: + # age_groups = {1: '5-14', 2: '15-34'} + age_groups = {0: '0-4', 1: '5-14', 2: '15-34', + 3: '35-59', 4: '60-79', 5: '80+'} + if len(age_groups) == 6: + filter_age = None + else: + if file_format == 'json': + filter_age = [val for val in age_groups.values()] + else: + filter_age = ['Group' + str(key) for key in age_groups.keys()] + + relative = True + + date = 14 + + i = 0 + for file in files_input.values(): + # MEmilio backend hdf5 example + if(i == 0): # result file of equation-based model has to be first + df = pm.extract_data( + file, region_spec=None, column=None, date=date, + filters={'Group': filter_age, 'InfectionState': [2]}, + output='matrix', + file_format=file_format) + df['Group'] = df.Group.str.extract('(\d+)') + df['Group'] = df['Group'].apply(pd.to_numeric, errors='coerce') + # df['Region'] = df['Group'] + df['Region'] = (df['Group']-1) // len(age_groups) + df = df.groupby(['Region'], as_index=False).agg({'Count': "sum"}) + + ids = geoger.get_county_ids() + ids = [id for id in ids if str(id).startswith('5')] + # ids = [5111, 5112, 5113] + + if len(ids) != len(df): + raise gd.DataError("Data is not compatible with number of NRW counties.") + + df['Region'] = ids + else: + df = pm.extract_data( + file, region_spec=None, column=None, date=date, + filters={'Group': filter_age, 'InfectionState': [2]}, + file_format=file_format) + + df = df.apply(pd.to_numeric, errors='coerce') + + if relative: + + try: + population = pd.read_json( + 'data/pydata/Germany/county_current_population.json') + # pandas>1.5 raise FileNotFoundError instead of ValueError + except (ValueError, FileNotFoundError): + print("Population data was not found. Download it from the internet.") + population = gpd.get_population_data( + read_data=False, file_format=file_format, + out_folder='data/pydata/Germany/', no_raw=True, + merge_eisenach=True) + + # For fitting of different age groups we need format ">X". + age_group_values = list(age_groups.values()) + age_group_values[-1] = age_group_values[-1].replace('80+', '>79') + # scale data + df = pm.scale_dataframe_relative(df, age_group_values, population) + + if i == 0: + dfs_all = pd.DataFrame(df.iloc[:, 0]) + + dfs_all[df.columns[-1] + ' ' + str(i)] = df[df.columns[-1]] + i += 1 + + min_val = dfs_all[dfs_all.columns[1:]].min().min() + max_val = dfs_all[dfs_all.columns[1:]].max().max() + + plot_map_nrw( + dfs_all, scale_colors=[min_val, max_val], + legend=['', ''], + title='NRW - Simulation Day 10', plot_colorbar=True, + output_path=os.path.dirname(__file__), + fig_name='NRWPlot', dpi=300, + outercolor='white', + log_scale=True) diff --git a/pycode/memilio-epidata/memilio/epidata/getNRWCounties.py b/pycode/memilio-epidata/memilio/epidata/getNRWCounties.py new file mode 100644 index 0000000000..a2c04550df --- /dev/null +++ b/pycode/memilio-epidata/memilio/epidata/getNRWCounties.py @@ -0,0 +1,54 @@ +import os + +import numpy as np +import pandas as pd + +from memilio.epidata import geoModificationGermany as geoger +from memilio.epidata import getDataIntoPandasDataFrame as gd + +def main(): + """! Main program entry.""" + + arg_dict = gd.cli("commuter_official") + + directory = arg_dict['out_folder'].split('/pydata')[0] + directory_mobility = os.path.join(directory, 'mobility/') + directory_population = os.path.join(directory, 'pydata/Germany/') + mobility_file = 'commuter_mobility' + population_file = 'county_current_population' + + mobility_matrix = pd.read_csv( + os.path.join(directory_mobility + mobility_file + '.txt'), + sep=' ', header=None) + + # get county and state IDs + countyIDs = geoger.get_county_ids() + stateIDs = geoger.get_state_ids() + # get state ID to county ID map + stateID_to_countyID = geoger.get_stateid_to_countyids_map() + + # iterate over state_to_county map and replace IDs by numbering 0, ..., n + state_indices = [] + county_indices = [] + for state, counties in stateID_to_countyID.items(): + state_indices.append(stateIDs.index(state)) + county_indices.append( + np.array([countyIDs.index(county) for county in counties])) + + mobility_matrix_nrw = mobility_matrix.loc[county_indices[4], county_indices[4]] + + gd.write_dataframe( + mobility_matrix_nrw, directory_mobility, mobility_file + '_nrw', 'txt', + param_dict={'sep': ' ', 'header': None, 'index': False}) + + population = pd.read_json(os.path.join(directory_population + population_file + '.json')) + population_nrw = population.loc[county_indices[4]] + gd.write_dataframe(population_nrw, directory_population, population_file + '_nrw', 'json') + + + + + +if __name__ == "__main__": + + main() From f49a4976efc2d1bbae370e7f6e6e43aacb1e7e0c Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:10:51 +0100 Subject: [PATCH 39/49] add timing example --- cpp/examples/ode_seir_mobility_timing.cpp | 180 ++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 cpp/examples/ode_seir_mobility_timing.cpp diff --git a/cpp/examples/ode_seir_mobility_timing.cpp b/cpp/examples/ode_seir_mobility_timing.cpp new file mode 100644 index 0000000000..55d568fd24 --- /dev/null +++ b/cpp/examples/ode_seir_mobility_timing.cpp @@ -0,0 +1,180 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Carlotta Gerstein +* +* 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/compartments/simulation.h" +#include "memilio/math/euler.h" +#include "memilio/utils/custom_index_array.h" +#include "models/ode_seir_mobility_improved/infection_state.h" +#include "models/ode_seir_mobility_improved/model.h" +#include "models/ode_seir_mobility_improved/parameters.h" +#include "models/ode_seir_mobility_improved/regions.h" + +#include + +template +void set_contact_matrix(mio::oseirmobilityimproved::Model& model) +{ + Eigen::MatrixXd contact_matrix_eigen(6, 6); + contact_matrix_eigen << 3.9547, 1.1002, 2.9472, 2.05, 0.3733, 0.0445, 0.3327, 3.5892, 1.236, 1.9208, 0.2681, 0.0161, + 0.246, 0.7124, 5.6518, 3.2939, 0.2043, 0.0109, 0.1742, 0.8897, 3.3124, 4.5406, 0.4262, 0.0214, 0.0458, 0.1939, + 0.5782, 1.3825, 1.473, 0.0704, 0.1083, 0.1448, 0.4728, 0.9767, 0.6266, 0.1724; + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline() = contact_matrix_eigen; +} + +/** + * Set epidemiological parameters of Sars-CoV-2 for a immune-naive + * population and wild type variant. + * @param params Object that the parameters will be added to. + * @returns Currently generates no errors. + */ +void set_covid_parameters(mio::oseirmobilityimproved::Parameters& params) +{ + params.template set>(3.335); + + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; +} + +template +void set_mobility_weights(mio::oseirmobilityimproved::Model& model) +{ + size_t number_regions = (size_t)model.parameters.get_num_regions(); + double fraction_commuter = 1. / (2 * number_regions); + Eigen::MatrixXd mobility_data_commuter = + Eigen::MatrixXd::Constant(number_regions, number_regions, fraction_commuter) - + fraction_commuter * + Eigen::MatrixXd::Identity(number_regions, number_regions); // Ensure that the diagonal is zero + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + mobility_data_commuter(county_idx_i, county_idx_i) = 1 - mobility_data_commuter.rowwise().sum()(county_idx_i); + } + model.parameters.template get>() + .get_cont_freq_mat()[0] + .get_baseline() = mobility_data_commuter; +} + +template +void set_parameters_and_population(mio::oseirmobilityimproved::Model& model) +{ + auto& populations = model.populations; + auto& parameters = model.parameters; + + size_t number_regions = (size_t)parameters.get_num_regions(); + size_t number_age_groups = (size_t)parameters.get_num_agegroups(); + for (size_t j = 0; j < number_age_groups; j++) { + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Exposed}] = 100; + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 999900; + for (size_t i = 1; i < number_regions; i++) { + model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; + model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 1000000; + } + } + set_mobility_weights(model); + + set_contact_matrix(model); + + set_covid_parameters(parameters); + + mio::ContactMatrixGroup& commuting_strengths = + parameters.template get>().get_cont_freq_mat(); + + auto& population_after_commuting = model.m_population_after_commuting; + for (size_t region_n = 0; region_n < number_regions; ++region_n) { + for (size_t age = 0; age < number_age_groups; ++age) { + double population_n = 0; + for (size_t state = 0; state < (size_t)mio::oseirmobilityimproved::InfectionState::Count; state++) { + population_n += populations[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age), + mio::oseirmobilityimproved::InfectionState(state)}]; + } + population_after_commuting[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] += + population_n; + for (size_t region_m = 0; region_m < number_regions; ++region_m) { + population_after_commuting[{mio::oseirmobilityimproved::Region(region_n), mio::AgeGroup(age)}] -= + commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; + population_after_commuting[{mio::oseirmobilityimproved::Region(region_m), mio::AgeGroup(age)}] += + commuting_strengths[0].get_baseline()(region_n, region_m) * population_n; + } + } + } +} + +void simulate(size_t num_warm_up_runs, size_t num_runs, ScalarType tmax) +{ + ScalarType t0 = 0.; + ScalarType dt = 0.1; + + constexpr size_t number_regions = NUM_REGIONS; + std::vector region_ids(number_regions); + iota(region_ids.begin(), region_ids.end(), 1); + ScalarType number_age_groups = 6; + + mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); + set_parameters_and_population(model); + + // using DefaultIntegratorCore = + // mio::ControlledStepperWrapper; + + std::shared_ptr> integrator = std::make_shared>(); + + std::cout << "{ \"Regions\": " << number_regions << ", " << std::endl; + + // Warm up runs. + for (size_t i = 0; i < num_warm_up_runs; i++) { + simulate(t0, tmax, dt, model, integrator); + } + + // Runs with timing. + ScalarType total = 0; + for (size_t i = 0; i < num_runs; i++) { + total -= omp_get_wtime(); + auto result_from_sim = simulate(t0, tmax, dt, model, integrator); + total += omp_get_wtime(); + } + std::cout << "\"Time\": " << total / num_runs << "\n}," << std::endl; +} + +int main(int argc, char** argv) +{ + const ScalarType tmax = 2; + size_t warm_up = 10; + size_t num_runs = 100; + if (argc > 2) { + warm_up = std::stod(argv[1]); + num_runs = std::stod(argv[2]); + } + simulate(warm_up, num_runs, tmax); + return 0; +} From 64744ab5b4d11baee354743dc4e9617c0da4f013 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:24:54 +0100 Subject: [PATCH 40/49] cmake changes for timing --- cpp/examples/CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 733ca7cf47..09df245e27 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -38,9 +38,16 @@ add_executable(ode_seir_mobility_example ode_seir_mobility.cpp) target_link_libraries(ode_seir_mobility_example PRIVATE memilio ode_seir_mobility) target_compile_options(ode_seir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -# add_executable(ode_seir_mobility_example_massaction ode_seir_mobility_massaction.cpp) -# target_link_libraries(ode_seir_mobility_example_massaction PRIVATE memilio ode_seir_mobility_massaction) -# target_compile_options(ode_seir_mobility_example_massaction PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +if (MEMILIO_ENABLE_OPENMP) + if(NOT DEFINED NUM_REGIONS) + set(NUM_REGIONS "42") + endif() + add_definitions(-DNUM_REGIONS=${NUM_REGIONS}) + + add_executable(ode_seir_mobility_example_timing ode_seir_mobility_timing.cpp) + target_link_libraries(ode_seir_mobility_example_timing PRIVATE memilio ode_seir_mobility_improved) + target_compile_options(ode_seir_mobility_example_timing PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +endif() add_executable(ode_seir_mobility_example_improved ode_seir_mobility_improved.cpp) target_link_libraries(ode_seir_mobility_example_improved PRIVATE memilio ode_seir_mobility_improved) From a26210457da6d391010d81776d5c583e94228a3c Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:52:44 +0100 Subject: [PATCH 41/49] fix bug for measuring runtimes and graph timing example --- cpp/examples/CMakeLists.txt | 17 +-- cpp/examples/graph_timing.cpp | 154 ++++++++++++++++++++++ cpp/examples/ode_seir_mobility_timing.cpp | 22 ++-- 3 files changed, 173 insertions(+), 20 deletions(-) create mode 100644 cpp/examples/graph_timing.cpp diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 09df245e27..aac77c0dbc 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -39,14 +39,9 @@ target_link_libraries(ode_seir_mobility_example PRIVATE memilio ode_seir_mobilit target_compile_options(ode_seir_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) if (MEMILIO_ENABLE_OPENMP) - if(NOT DEFINED NUM_REGIONS) - set(NUM_REGIONS "42") - endif() - add_definitions(-DNUM_REGIONS=${NUM_REGIONS}) - - add_executable(ode_seir_mobility_example_timing ode_seir_mobility_timing.cpp) - target_link_libraries(ode_seir_mobility_example_timing PRIVATE memilio ode_seir_mobility_improved) - target_compile_options(ode_seir_mobility_example_timing PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(ode_seir_mobility_timing ode_seir_mobility_timing.cpp) + target_link_libraries(ode_seir_mobility_timing PRIVATE memilio ode_seir_mobility_improved) + target_compile_options(ode_seir_mobility_timing PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() add_executable(ode_seir_mobility_example_improved ode_seir_mobility_improved.cpp) @@ -111,6 +106,12 @@ add_executable(graph_example_extended graph_extended.cpp) target_link_libraries(graph_example_extended PRIVATE memilio ode_seir) target_compile_options(graph_example_extended PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +if (MEMILIO_ENABLE_OPENMP) + add_executable(graph_timing graph_timing.cpp) + target_link_libraries(graph_timing PRIVATE memilio ode_seir) + target_compile_options(graph_timing PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +endif() + add_executable(graph_stochastic_mobility_example graph_stochastic_mobility.cpp) target_link_libraries(graph_stochastic_mobility_example PRIVATE memilio ode_secir) target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/examples/graph_timing.cpp b/cpp/examples/graph_timing.cpp new file mode 100644 index 0000000000..62c04f7eab --- /dev/null +++ b/cpp/examples/graph_timing.cpp @@ -0,0 +1,154 @@ + +#include "ode_seir/model.h" +#include "ode_seir/infection_state.h" +#include "ode_seir/parameters.h" +#include "memilio/mobility/metapopulation_mobility_instant.h" +#include "memilio/compartments/simulation.h" + +#include + +void set_contact_matrices(mio::oseir::Parameters& params) +{ + Eigen::MatrixXd contact_matrix_eigen(6, 6); + contact_matrix_eigen << 3.9547, 1.1002, 2.9472, 2.05, 0.3733, 0.0445, 0.3327, 3.5892, 1.236, 1.9208, 0.2681, 0.0161, + 0.246, 0.7124, 5.6518, 3.2939, 0.2043, 0.0109, 0.1742, 0.8897, 3.3124, 4.5406, 0.4262, 0.0214, 0.0458, 0.1939, + 0.5782, 1.3825, 1.473, 0.0704, 0.1083, 0.1448, 0.4728, 0.9767, 0.6266, 0.1724; + mio::ContactMatrixGroup& contact_matrix = params.template get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline() = contact_matrix_eigen; +} + +/** + * Set epidemiological parameters of Sars-CoV-2 for a immune-naive + * population and wild type variant. + * @param params Object that the parameters will be added to. + * @returns Currently generates no errors. + */ +void set_covid_parameters(mio::oseir::Parameters& params) +{ + params.template set>(3.335); + + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; +} + +void set_population_data(mio::oseir::Parameters& params, + mio::Graph>>, + mio::MobilityEdge<>>& params_graph, + size_t number_regions) +{ + std::vector> nodes(number_regions, + mio::oseir::Model(int(size_t(params.get_num_groups())))); + + mio::Populations population( + {params.get_num_groups(), mio::oseir::InfectionState::Count}); + + for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { + population[{i, mio::oseir::InfectionState::Susceptible}] = 1000000; + } + for (auto& node : nodes) { + node.parameters = params; + node.populations = population; + } + for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { + nodes[0].populations[{i, mio::oseir::InfectionState::Exposed}] = 100; + nodes[0].populations[{i, mio::oseir::InfectionState::Susceptible}] = 999900; + } + + for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { + params_graph.add_node(node_idx, nodes[node_idx]); + } +} + +void set_parameters_and_population(mio::Graph>>, + mio::MobilityEdge<>>& params_graph, + size_t number_regions) +{ + const int num_age_groups = 6; + + mio::oseir::Parameters params(num_age_groups); + + set_covid_parameters(params); + + // set contact matrix + set_contact_matrices(params); + + set_population_data(params, params_graph, number_regions); + + for (size_t county_idx_i = 0; county_idx_i < params_graph.nodes().size(); ++county_idx_i) { + for (size_t county_idx_j = 0; county_idx_j < params_graph.nodes().size(); ++county_idx_j) { + double commuter_coeff_ij = 1. / (2 * number_regions); + if (county_idx_i == county_idx_j) { + commuter_coeff_ij = 0; + } + params_graph.add_edge( + county_idx_i, county_idx_j, + Eigen::VectorXd::Constant((size_t)mio::oseir::InfectionState::Count * size_t(params.get_num_groups()), + commuter_coeff_ij)); + } + } + + for (auto& node : params_graph.nodes()) { + node.property.get_simulation().set_integrator(std::make_shared>()); + } +} + +void simulate(size_t num_warm_up_runs, size_t num_runs, size_t number_regions, ScalarType tmax) +{ + ScalarType t0 = 0.; + ScalarType dt = 0.1; + + mio::Graph>>, mio::MobilityEdge<>> params_graph; + + set_parameters_and_population(params_graph, number_regions); + + // using DefaultIntegratorCore = + // mio::ControlledStepperWrapper; + + std::shared_ptr> integrator = std::make_shared>(); + + std::cout << "{ \"Regions\": " << number_regions << ", " << std::endl; + + // Warm up runs. + for (size_t i = 0; i < num_warm_up_runs; i++) { + auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); + sim.advance(tmax); + } + + // Runs with timing. + ScalarType total = 0; + for (size_t i = 0; i < num_runs; i++) { + auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); + total -= omp_get_wtime(); + sim.advance(tmax); + total += omp_get_wtime(); + auto result_graph = std::move(sim).get_graph(); + result_graph.nodes()[0].property.get_result().print_table(); + } + std::cout << "\"Time\": " << total / num_runs << "\n}," << std::endl; +} + +int main(int argc, char** argv) +{ + const ScalarType tmax = 20; + size_t warm_up = 10; + size_t num_runs = 100; + size_t num_regions = 10; + if (argc > 3) { + warm_up = std::stod(argv[1]); + num_runs = std::stod(argv[2]); + num_regions = std::stod(argv[3]); + } + simulate(warm_up, num_runs, num_regions, tmax); + return 0; +} diff --git a/cpp/examples/ode_seir_mobility_timing.cpp b/cpp/examples/ode_seir_mobility_timing.cpp index 55d568fd24..f74fe37404 100644 --- a/cpp/examples/ode_seir_mobility_timing.cpp +++ b/cpp/examples/ode_seir_mobility_timing.cpp @@ -131,14 +131,10 @@ void set_parameters_and_population(mio::oseirmobilityimproved::Model& model) } } -void simulate(size_t num_warm_up_runs, size_t num_runs, ScalarType tmax) +void simulate(size_t num_warm_up_runs, size_t num_runs, size_t number_regions, ScalarType tmax) { - ScalarType t0 = 0.; - ScalarType dt = 0.1; - - constexpr size_t number_regions = NUM_REGIONS; - std::vector region_ids(number_regions); - iota(region_ids.begin(), region_ids.end(), 1); + ScalarType t0 = 0.; + ScalarType dt = 0.1; ScalarType number_age_groups = 6; mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); @@ -168,13 +164,15 @@ void simulate(size_t num_warm_up_runs, size_t num_runs, ScalarType tmax) int main(int argc, char** argv) { - const ScalarType tmax = 2; + const ScalarType tmax = 20; size_t warm_up = 10; size_t num_runs = 100; - if (argc > 2) { - warm_up = std::stod(argv[1]); - num_runs = std::stod(argv[2]); + size_t num_regions = 10; + if (argc > 3) { + warm_up = std::stod(argv[1]); + num_runs = std::stod(argv[2]); + num_regions = std::stod(argv[3]); } - simulate(warm_up, num_runs, tmax); + simulate(warm_up, num_runs, num_regions, tmax); return 0; } From 565141c6bf6e4e94c401ee0b85bf5c390b103acf Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:04:02 +0100 Subject: [PATCH 42/49] fix bug for graph timing --- cpp/examples/graph_timing.cpp | 51 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/cpp/examples/graph_timing.cpp b/cpp/examples/graph_timing.cpp index 62c04f7eab..692df8b4b4 100644 --- a/cpp/examples/graph_timing.cpp +++ b/cpp/examples/graph_timing.cpp @@ -103,7 +103,7 @@ void set_parameters_and_population(mio::Graph; + auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); + auto start_time = omp_get_wtime(); + sim.advance(tmax); + auto end_time = omp_get_wtime(); - std::shared_ptr> integrator = std::make_shared>(); + return end_time - start_time; +} - std::cout << "{ \"Regions\": " << number_regions << ", " << std::endl; +int main(int argc, char** argv) +{ + const ScalarType tmax = 1; + size_t warm_up = 1; + size_t num_runs = 2; + size_t num_regions = 5; + if (argc > 3) { + warm_up = std::stod(argv[1]); + num_runs = std::stod(argv[2]); + num_regions = std::stod(argv[3]); + } + std::cout << "{ \"Regions\": " << num_regions << ", " << std::endl; // Warm up runs. - for (size_t i = 0; i < num_warm_up_runs; i++) { - auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); - sim.advance(tmax); + for (size_t i = 0; i < warm_up; i++) { + double warm_up_time = simulate(num_regions, tmax); + mio::unused(warm_up_time); } // Runs with timing. ScalarType total = 0; for (size_t i = 0; i < num_runs; i++) { - auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); - total -= omp_get_wtime(); - sim.advance(tmax); - total += omp_get_wtime(); - auto result_graph = std::move(sim).get_graph(); - result_graph.nodes()[0].property.get_result().print_table(); + double run_time = simulate(num_regions, tmax); + total += run_time; } std::cout << "\"Time\": " << total / num_runs << "\n}," << std::endl; -} -int main(int argc, char** argv) -{ - const ScalarType tmax = 20; - size_t warm_up = 10; - size_t num_runs = 100; - size_t num_regions = 10; - if (argc > 3) { - warm_up = std::stod(argv[1]); - num_runs = std::stod(argv[2]); - num_regions = std::stod(argv[3]); - } - simulate(warm_up, num_runs, num_regions, tmax); return 0; } From 9f36912859295ce12496f37ecad5cb40a5d877bb Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:29:25 +0100 Subject: [PATCH 43/49] fix things for runtime measurements --- cpp/examples/graph_timing.cpp | 13 +++++++------ cpp/examples/ode_seir_mobility_timing.cpp | 5 ++--- cpp/memilio/compartments/simulation.h | 9 ++++++--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cpp/examples/graph_timing.cpp b/cpp/examples/graph_timing.cpp index 692df8b4b4..3f6d2ae172 100644 --- a/cpp/examples/graph_timing.cpp +++ b/cpp/examples/graph_timing.cpp @@ -106,13 +106,14 @@ void set_parameters_and_population(mio::Graph>>, mio::MobilityEdge<>> params_graph; set_parameters_and_population(params_graph, number_regions); - auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); + auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); + mio::set_log_level(mio::LogLevel::off); auto start_time = omp_get_wtime(); sim.advance(tmax); auto end_time = omp_get_wtime(); @@ -122,10 +123,10 @@ double simulate(size_t number_regions, ScalarType tmax) int main(int argc, char** argv) { - const ScalarType tmax = 1; - size_t warm_up = 1; - size_t num_runs = 2; - size_t num_regions = 5; + const ScalarType tmax = 20; + size_t warm_up = 10; + size_t num_runs = 100; + size_t num_regions = 10; if (argc > 3) { warm_up = std::stod(argv[1]); num_runs = std::stod(argv[2]); diff --git a/cpp/examples/ode_seir_mobility_timing.cpp b/cpp/examples/ode_seir_mobility_timing.cpp index f74fe37404..17c080884e 100644 --- a/cpp/examples/ode_seir_mobility_timing.cpp +++ b/cpp/examples/ode_seir_mobility_timing.cpp @@ -155,9 +155,8 @@ void simulate(size_t num_warm_up_runs, size_t num_runs, size_t number_regions, S // Runs with timing. ScalarType total = 0; for (size_t i = 0; i < num_runs; i++) { - total -= omp_get_wtime(); - auto result_from_sim = simulate(t0, tmax, dt, model, integrator); - total += omp_get_wtime(); + double runtime = simulate(t0, tmax, dt, model, integrator); + total += runtime; } std::cout << "\"Time\": " << total / num_runs << "\n}," << std::endl; } diff --git a/cpp/memilio/compartments/simulation.h b/cpp/memilio/compartments/simulation.h index 8b3307f87f..23a627d7f9 100644 --- a/cpp/memilio/compartments/simulation.h +++ b/cpp/memilio/compartments/simulation.h @@ -26,6 +26,8 @@ #include "memilio/math/stepper_wrapper.h" #include "memilio/utils/time_series.h" +#include + namespace mio { @@ -213,16 +215,17 @@ using is_compartment_model_simulation = * @tparam Sim A Simulation that can simulate the model. */ template > -TimeSeries simulate(FP t0, FP tmax, FP dt, Model const& model, - std::shared_ptr> integrator = nullptr) +double simulate(FP t0, FP tmax, FP dt, Model const& model, std::shared_ptr> integrator = nullptr) { model.check_constraints(); Sim sim(model, t0, dt); if (integrator) { sim.set_integrator(integrator); } + double start_time = omp_get_wtime(); sim.advance(tmax); - return sim.get_result(); + double end_time = omp_get_wtime(); + return end_time - start_time; } } // namespace mio From ee57c8f82ef7242aba3e196056dfaf16025d7f9d Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:38:45 +0100 Subject: [PATCH 44/49] add plotfiles, small correction in the model and maybe optimizations --- cpp/models/ode_seir_mobility_improved/model.h | 63 ++++++----- tools/plot_mobility_runtimes.py | 99 ++++++++++++++++ .../plot_results_mobility.py | 107 ++++++++++++------ 3 files changed, 204 insertions(+), 65 deletions(-) create mode 100644 tools/plot_mobility_runtimes.py rename pycode/examples/plot/plotResultsMapNRW.py => tools/plot_results_mobility.py (78%) diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index 4853be89ad..470266fe5e 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -57,49 +57,56 @@ class Model : public FlowModel>().get_cont_freq_mat().get_matrix_at(t); const Index n_age_groups = reduce_index>(params.get_num_agegroups()); const Index n_regions = reduce_index>(params.get_num_regions()); - for (auto age_i : make_index_range(n_age_groups)) { - for (auto age_j : make_index_range(n_age_groups)) { + for (size_t age_i = 0; age_i < (size_t)n_age_groups; age_i++) { + for (size_t age_j = 0; age_j < (size_t)n_age_groups; age_j++) { Eigen::VectorXd infectives_per_region = Eigen::VectorXd::Zero((size_t)n_regions); - for (auto region_n : make_index_range(n_regions)) { - for (auto region_m : make_index_range(n_regions)) { - infectives_per_region(region_n.get()) += - commuting_strengths(region_m.get(), region_n.get()) * - pop[population.get_flat_index({region_m, age_j, InfectionState::Infected})]; + for (size_t region_n = 0; region_n < (size_t)n_regions; region_n++) { + for (size_t region_m = 0; region_m < (size_t)n_regions; region_m++) { + infectives_per_region(region_n) += + commuting_strengths(region_m, region_n) * + pop[population.get_flat_index( + {Region(region_m), AgeGroup(age_j), InfectionState::Infected})]; } } - for (auto region_n : make_index_range(n_regions)) { + for (size_t region_n = 0; region_n < (size_t)n_regions; region_n++) { FP flow_SE_helper = 0; - const size_t Sj = population.get_flat_index({region_n, age_j, InfectionState::Susceptible}); - const size_t Ej = population.get_flat_index({region_n, age_j, InfectionState::Exposed}); - const size_t Ij = population.get_flat_index({region_n, age_j, InfectionState::Infected}); - const size_t Rj = population.get_flat_index({region_n, age_j, InfectionState::Recovered}); + const size_t Ej = + population.get_flat_index({Region(region_n), AgeGroup(age_j), InfectionState::Exposed}); + const size_t Ij = + population.get_flat_index({Region(region_n), AgeGroup(age_j), InfectionState::Infected}); + const size_t Rj = + population.get_flat_index({Region(region_n), AgeGroup(age_j), InfectionState::Recovered}); + const size_t Sj = + population.get_flat_index({Region(region_n), AgeGroup(age_j), InfectionState::Susceptible}); const double Nj_inv = 1.0 / (pop[Sj] + pop[Ej] + pop[Ij] + pop[Rj]); - double coeffStoI = 0.5 * - params.template get>().get_cont_freq_mat().get_matrix_at(t)( - age_i.get(), age_j.get()) * - params.template get>()[age_i]; + double coeffStoI = + 0.5 * + params.template get>().get_cont_freq_mat().get_matrix_at(t)(age_i, age_j) * + params.template get>()[AgeGroup(age_i)]; flow_SE_helper += - pop[population.get_flat_index({region_n, age_j, InfectionState::Infected})] * Nj_inv; - for (auto region_m : make_index_range(n_regions)) { - flow_SE_helper += commuting_strengths(region_n.get(), region_m.get()) * - infectives_per_region(region_m.get()) / - m_population_after_commuting[{region_n, age_j}]; + pop[population.get_flat_index({Region(region_n), AgeGroup(age_j), InfectionState::Infected})] * + Nj_inv; + for (size_t region_m = 0; region_m < (size_t)n_regions; region_m++) { + flow_SE_helper += commuting_strengths(region_n, region_m) * infectives_per_region(region_m) / + m_population_after_commuting[{Region(region_m), AgeGroup(age_j)}]; } flows[Base::template get_flat_flow_index( - {region_n, age_i})] += + {Region(region_n), AgeGroup(age_i)})] += flow_SE_helper * coeffStoI * - y[population.get_flat_index({region_n, age_i, InfectionState::Susceptible})]; + y[population.get_flat_index({Region(region_n), AgeGroup(age_i), InfectionState::Susceptible})]; } } - for (auto region : make_index_range(n_regions)) { + for (size_t region = 0; region < (size_t)n_regions; region++) { flows[Base::template get_flat_flow_index( - {region, age_i})] = (1.0 / params.template get>()[age_i]) * - y[population.get_flat_index({region, age_i, InfectionState::Exposed})]; + {Region(region), AgeGroup(age_i)})] = + (1.0 / params.template get>()[AgeGroup(age_i)]) * + y[population.get_flat_index({Region(region), AgeGroup(age_i), InfectionState::Exposed})]; flows[Base::template get_flat_flow_index( - {region, age_i})] = (1.0 / params.template get>()[age_i]) * - y[population.get_flat_index({region, age_i, InfectionState::Infected})]; + {Region(region), AgeGroup(age_i)})] = + (1.0 / params.template get>()[AgeGroup(age_i)]) * + y[population.get_flat_index({Region(region), AgeGroup(age_i), InfectionState::Infected})]; } } } diff --git a/tools/plot_mobility_runtimes.py b/tools/plot_mobility_runtimes.py new file mode 100644 index 0000000000..437f34149c --- /dev/null +++ b/tools/plot_mobility_runtimes.py @@ -0,0 +1,99 @@ +import matplotlib.pyplot as plt +import pandas as pd + +import os +import json +import re + +colors = ["tab:blue", "tab:orange", "tab:green", + "tab:red", "tab:purple", "tab:brown"] +fontsize_labels = 16 +fontsize_legends = 12 + +models = ['Equation-based model', 'Graph-based model'] + +def plot_runtime(file, name=''): + fig = plt.figure() + df = pd.read_json(file) + + plt.plot(df["Regions"], df["Time"], + linestyle='--', marker='o', linewidth=1.2) + plt.ylim(bottom=0.) + plt.xlim(left=0., right=df["Regions"].max()+1) + plt.xlabel('Number of regions', fontsize=fontsize_labels) + plt.ylabel('Run time [seconds]', fontsize=fontsize_labels) + plt.yticks(fontsize=fontsize_legends) + plt.xticks(fontsize=fontsize_legends) + plt.grid(True, linestyle='--') + plt.tight_layout() + + plot_dir = os.path.join(os.path.dirname(__file__), '../Plots') + name = os.path.splitext(os.path.basename(file))[0] + plt.savefig(os.path.join(plot_dir, name), bbox_inches='tight', dpi=500) + +def compare_runtimes(files, name='', title='', models=[]): + merged_df = pd.DataFrame() + i = 0 + for file in files: + df = pd.read_json(file) + + df.rename(columns={'Time': models[i]}, inplace=True) + + if merged_df.empty: + merged_df = df + else: + merged_df = pd.merge(merged_df, df, on='Regions', how='outer') + i = i+1 + + merged_df = merged_df.set_index('Regions') + for column in merged_df.columns: + # plt.plot(merged_df['Regions'], column, + # linestyle='--', marker='o', linewidth=1.2) + plt.plot(merged_df.index, merged_df[column], label=column, + linestyle='--', marker='o', linewidth=1.2) + plt.ylim(bottom=0.) + plt.xlim(left=merged_df.index.min()-1, right=merged_df.index.max()+1) + plt.xlabel('Number of regions', fontsize=fontsize_labels) + plt.ylabel('Run time [seconds]', fontsize=fontsize_labels) + plt.yticks(fontsize=fontsize_legends) + plt.xticks(fontsize=fontsize_legends) + plt.grid(True, linestyle='--') + plt.legend() + plt.title(title) + plt.tight_layout() + + plot_dir = os.path.join(os.path.dirname(__file__), '../Plots') + plt.savefig(os.path.join(plot_dir, name), bbox_inches='tight', dpi=500) + plt.close() + +if __name__ == "__main__": + result_dir = os.path.join(os.path.dirname(__file__), '../results') + + result_equationbased_start = os.path.join(result_dir, 'timing_equationbased_start.json') + result_equationbased = os.path.join(result_dir, 'timing_equationbased.json') + result_equationbased_O3 = os.path.join(result_dir, 'timing_equationbased_O3.json') + result_equationbased_O2 = os.path.join(result_dir, 'timing_equationbased_O2.json') + result_equationbased_O1 = os.path.join(result_dir, 'timing_equationbased_O1.json') + result_equationbased_O0 = os.path.join(result_dir, 'timing_equationbased_O0.json') + result_graphbased_start = os.path.join(result_dir, 'timing_graphbased_start.json') + result_graphbased = os.path.join(result_dir, 'timing_graphbased.json') + result_graphbased_smallsteps = os.path.join(result_dir, 'timing_graphbased_01steps.json') + result_graphbased_unoptimized = os.path.join(result_dir, 'timing_graphbased_unoptimized.json') + + result_equationbased_mod4_0 = os.path.join(result_dir, 'timing_equationbased_mod4_0.json') + result_equationbased_mod4_1 = os.path.join(result_dir, 'timing_equationbased_mod4_1.json') + result_equationbased_mod4_2 = os.path.join(result_dir, 'timing_equationbased_mod4_2.json') + result_equationbased_mod4_3 = os.path.join(result_dir, 'timing_equationbased_mod4_3.json') + + results_start = [result_equationbased_start, result_graphbased_start] + results = [result_equationbased, result_graphbased, result_graphbased_smallsteps] + results_unoptimized = [result_equationbased_O3, result_equationbased_O2, result_equationbased_O1, result_equationbased_O0] + results_mod4 = [result_equationbased_mod4_0, result_equationbased_mod4_1, result_equationbased_mod4_2, result_equationbased_mod4_3] + + # plot_runtime(result_equationbased) + # plot_runtime(result_graphbased) + + # compare_runtimes(results_start,name='compare_runtimes_start', title='Runtimes for Euler Method', models=models) + compare_runtimes(results, name='compare_runtimes', title='Runtimes for Euler Method', models=['Equation-based model', 'Graph-based model', 'Graph-based model with dt=0.1']) + compare_runtimes(results_unoptimized, name='compare_runtimes_unoptimized', title='Runtimes for Euler Method', models=['-O3', '-O2', '-O1', '-O0']) + compare_runtimes(results_mod4, name='compare_runtimes_mod4', title='Runtimes for Euler Method', models=['%4=0', '%4=1', '%4=2', '%4=3']) diff --git a/pycode/examples/plot/plotResultsMapNRW.py b/tools/plot_results_mobility.py similarity index 78% rename from pycode/examples/plot/plotResultsMapNRW.py rename to tools/plot_results_mobility.py index ff0f5692a2..99854994ae 100644 --- a/pycode/examples/plot/plotResultsMapNRW.py +++ b/tools/plot_results_mobility.py @@ -1,6 +1,7 @@ import datetime as dt import os.path +import h5py import numpy as np import pandas as pd @@ -17,6 +18,11 @@ import matplotlib.pyplot as plt import matplotlib.colors as mcolors +compartments = {'Susceptible': 0, + 'Exposed': 1, + 'Infected': 2, + 'Recovered': 3} + def plot_map_nrw(data: pd.DataFrame, scale_colors: np.array([0, 1]), @@ -115,7 +121,7 @@ def plot_map_nrw(data: pd.DataFrame, for i in range(len(data_columns)): - cmap = 'viridis' + cmap = 'inferno' ax = fig.add_subplot(gs[1, i]) if log_scale: map_data.plot(data_columns[i], ax=ax, legend=False, @@ -140,35 +146,33 @@ def plot_map_nrw(data: pd.DataFrame, plt.subplots_adjust(bottom=0.1) plt.savefig(os.path.join(output_path, fig_name + '.png'), dpi=dpi) - - -if __name__ == '__main__': - - files_input = {'Data set 1': 'cpp/build/ode_result_nrw', - 'Data set 2': 'cpp/build/graph_result_nrw'} # Result file of equation-based model has to be first - file_format = 'h5' - # Define age groups which will be considered through filtering - # Keep keys and values as well as its assignment constant, remove entries - # if only part of the population should be plotted or considered, e.g., by - # setting: - # age_groups = {1: '5-14', 2: '15-34'} + plt.close() + +def plot_maps(files, output_dir, name=''): + + for date in range(10, 50, 10): + dfs_all = extract_nrw_data_and_combine(files=files, date=date) + + min_val = dfs_all[dfs_all.columns[1:]].min().min() + max_val = dfs_all[dfs_all.columns[1:]].max().max() + + plot_map_nrw( + dfs_all, scale_colors=[min_val, max_val], + legend=['', ''], + title='NRW - Simulation Day '+str(date), plot_colorbar=True, + output_path=output_dir, + fig_name=name+str(date), dpi=900, + outercolor='white', + log_scale=True) + +def extract_nrw_data_and_combine(files, date): age_groups = {0: '0-4', 1: '5-14', 2: '15-34', 3: '35-59', 4: '60-79', 5: '80+'} - if len(age_groups) == 6: - filter_age = None - else: - if file_format == 'json': - filter_age = [val for val in age_groups.values()] - else: - filter_age = ['Group' + str(key) for key in age_groups.keys()] - + filter_age = None relative = True - date = 14 - i = 0 - for file in files_input.values(): - # MEmilio backend hdf5 example + for file in files.values(): if(i == 0): # result file of equation-based model has to be first df = pm.extract_data( file, region_spec=None, column=None, date=date, @@ -194,7 +198,7 @@ def plot_map_nrw(data: pd.DataFrame, file, region_spec=None, column=None, date=date, filters={'Group': filter_age, 'InfectionState': [2]}, file_format=file_format) - + df = df.apply(pd.to_numeric, errors='coerce') if relative: @@ -222,14 +226,43 @@ def plot_map_nrw(data: pd.DataFrame, dfs_all[df.columns[-1] + ' ' + str(i)] = df[df.columns[-1]] i += 1 - min_val = dfs_all[dfs_all.columns[1:]].min().min() - max_val = dfs_all[dfs_all.columns[1:]].max().max() - - plot_map_nrw( - dfs_all, scale_colors=[min_val, max_val], - legend=['', ''], - title='NRW - Simulation Day 10', plot_colorbar=True, - output_path=os.path.dirname(__file__), - fig_name='NRWPlot', dpi=300, - outercolor='white', - log_scale=True) + return dfs_all + + +def plot_total_compartment(files, output_dir, compartment = 'Infected', name='', title=''): + + i = 0 + for file in files.values(): + # Load data. + h5file = h5py.File(file + '.h5', 'r') + if i == 0: + dates = h5file['1']['Time'][:] + data = h5file['1']['Total'][:,compartments[compartment]] + plt.plot(dates, data, label='Equation-based model') + else: + df = pd.DataFrame() + regions = list(h5file.keys()) + for i in range(len(regions)): + df['Region'+str(i)] = h5file[regions[i]]['Total'][:, compartments[compartment]] + df['Total'] = df.sum(axis=1) + df['Time'] = h5file['5111']['Time'][:] # hardcoded + plt.plot(df['Time'], df['Total'], label='Graph-based model') + + i = i+1 + + plt.title(title) + plt.legend() + plt.savefig(os.path.join(output_dir, name + '.png'), dpi=300) + plt.close() + + +if __name__ == '__main__': + + files_input = {'Data set 1': 'cpp/build/ode_result_nrw', + 'Data set 2': 'cpp/build/graph_result_nrw'} # Result file of equation-based model has to be first + file_format = 'h5' + + plot_dir = os.path.join(os.path.dirname(__file__), '../Plots') + + plot_maps(files=files_input,output_dir=plot_dir, name='NRWPlotDay') + plot_total_compartment(files=files_input, output_dir=plot_dir, compartment='Infected', name='infectives_total', title='Total infectives') From d8b321f1d19c1cd8b19c6c07b2bea42b5046db3b Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:25:51 +0100 Subject: [PATCH 45/49] mini optimizations and small changes for timing runs --- cpp/examples/graph_extended.cpp | 6 ++-- cpp/examples/graph_timing.cpp | 35 +++++++++++++++---- cpp/examples/ode_seir_mobility_improved.cpp | 12 +++---- cpp/examples/ode_seir_mobility_timing.cpp | 4 ++- cpp/memilio/compartments/simulation.h | 17 ++++++++- cpp/models/ode_seir_mobility_improved/model.h | 14 ++++---- 6 files changed, 61 insertions(+), 27 deletions(-) diff --git a/cpp/examples/graph_extended.cpp b/cpp/examples/graph_extended.cpp index 311994f33e..0fe7db0272 100644 --- a/cpp/examples/graph_extended.cpp +++ b/cpp/examples/graph_extended.cpp @@ -170,6 +170,7 @@ set_synthetic_population_data(mio::oseir::Parameters& params) mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double dt) { + mio::set_log_level(mio::LogLevel::off); // global parameters bool synthetic_population = false; const int num_age_groups = 6; @@ -252,9 +253,6 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double }); auto save_result_status = save_result(result, county_ids, num_age_groups, "graph_result_nrw.h5"); - result_graph.nodes()[0].property.get_result().print_table(); - result_graph.nodes()[1].property.get_result().print_table(); - result_graph.nodes()[2].property.get_result().print_table(); return mio::success(); } @@ -262,7 +260,7 @@ mio::IOResult run(const fs::path& data_dir, double t0, double tmax, double int main() { const auto t0 = 0.; - const auto tmax = 15.; + const auto tmax = 50.; const auto dt = 0.5; //time step of mobility, daily mobility every second step const std::string& data_dir = ""; diff --git a/cpp/examples/graph_timing.cpp b/cpp/examples/graph_timing.cpp index 3f6d2ae172..8759125419 100644 --- a/cpp/examples/graph_timing.cpp +++ b/cpp/examples/graph_timing.cpp @@ -103,7 +103,7 @@ void set_parameters_and_population(mio::Graph>>, mio::MobilityEdge<>> params_graph; + + set_parameters_and_population(params_graph, number_regions); + + auto sim = mio::make_mobility_sim(t0, dt, std::move(params_graph)); + sim.advance(tmax); + + auto result_graph = std::move(sim).get_graph(); + + int num_steps = 0; + for (auto&& node : result_graph.nodes()) { + num_steps += node.property.get_result().get_num_time_points() - 1; + } + + return num_steps; +} + int main(int argc, char** argv) { + mio::set_log_level(mio::LogLevel::off); const ScalarType tmax = 20; size_t warm_up = 10; size_t num_runs = 100; @@ -135,15 +157,16 @@ int main(int argc, char** argv) std::cout << "{ \"Regions\": " << num_regions << ", " << std::endl; // Warm up runs. + int num_steps = 0; for (size_t i = 0; i < warm_up; i++) { - double warm_up_time = simulate(num_regions, tmax); - mio::unused(warm_up_time); + num_steps = simulate_steps(num_regions, tmax); } + std::cout << "\"Steps\": " << num_steps / num_regions << "," << std::endl; // Runs with timing. ScalarType total = 0; for (size_t i = 0; i < num_runs; i++) { - double run_time = simulate(num_regions, tmax); + double run_time = simulate_runtime(num_regions, tmax); total += run_time; } std::cout << "\"Time\": " << total / num_runs << "\n}," << std::endl; diff --git a/cpp/examples/ode_seir_mobility_improved.cpp b/cpp/examples/ode_seir_mobility_improved.cpp index 7a2577b517..a07e4ac53d 100644 --- a/cpp/examples/ode_seir_mobility_improved.cpp +++ b/cpp/examples/ode_seir_mobility_improved.cpp @@ -242,12 +242,10 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0.; - ScalarType tmax = 15.; + ScalarType tmax = 50.; ScalarType dt = 0.1; - ScalarType number_regions = 53; - std::vector region_ids(number_regions); - iota(region_ids.begin(), region_ids.end(), 1); + ScalarType number_regions = 53; ScalarType number_age_groups = 6; bool synthetic_population = false; if (number_age_groups != 6) { @@ -279,8 +277,8 @@ int main() // result_from_sim.print_table(); auto save_result_status = - mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_nrw.h5"); + mio::save_result({result_from_sim}, {1}, number_regions * number_age_groups, "ode_result_nrw.h5"); - // auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); - // std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; + auto basic_reproduction_number = model.get_reproduction_number(0, result_from_sim).value(); + std::cout << "\nbasis reproduction number: " << basic_reproduction_number << "\n"; } diff --git a/cpp/examples/ode_seir_mobility_timing.cpp b/cpp/examples/ode_seir_mobility_timing.cpp index 17c080884e..bd1052420f 100644 --- a/cpp/examples/ode_seir_mobility_timing.cpp +++ b/cpp/examples/ode_seir_mobility_timing.cpp @@ -151,11 +151,13 @@ void simulate(size_t num_warm_up_runs, size_t num_runs, size_t number_regions, S for (size_t i = 0; i < num_warm_up_runs; i++) { simulate(t0, tmax, dt, model, integrator); } + auto result = simulate(t0, tmax, dt, model, integrator); + std::cout << "\"Steps\": " << result.get_num_time_points() << "," << std::endl; // Runs with timing. ScalarType total = 0; for (size_t i = 0; i < num_runs; i++) { - double runtime = simulate(t0, tmax, dt, model, integrator); + double runtime = simulate_runtime(t0, tmax, dt, model, integrator); total += runtime; } std::cout << "\"Time\": " << total / num_runs << "\n}," << std::endl; diff --git a/cpp/memilio/compartments/simulation.h b/cpp/memilio/compartments/simulation.h index 23a627d7f9..b887744430 100644 --- a/cpp/memilio/compartments/simulation.h +++ b/cpp/memilio/compartments/simulation.h @@ -215,7 +215,22 @@ using is_compartment_model_simulation = * @tparam Sim A Simulation that can simulate the model. */ template > -double simulate(FP t0, FP tmax, FP dt, Model const& model, std::shared_ptr> integrator = nullptr) +TimeSeries simulate(FP t0, FP tmax, FP dt, Model const& model, + std::shared_ptr> integrator = nullptr) +{ + model.check_constraints(); + Sim sim(model, t0, dt); + if (integrator) { + sim.set_integrator(integrator); + } + sim.advance(tmax); + return sim.get_result(); +} + +/*Same function, used for timing*/ +template > +double simulate_runtime(FP t0, FP tmax, FP dt, Model const& model, + std::shared_ptr> integrator = nullptr) { model.check_constraints(); Sim sim(model, t0, dt); diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index 470266fe5e..dea693d579 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -70,24 +70,22 @@ class Model : public FlowModel>().get_cont_freq_mat().get_matrix_at(t)(age_i, age_j) * params.template get>()[AgeGroup(age_i)]; - flow_SE_helper += - pop[population.get_flat_index({Region(region_n), AgeGroup(age_j), InfectionState::Infected})] * - Nj_inv; + flow_SE_helper += pop[Ijn] * Nj_inv; for (size_t region_m = 0; region_m < (size_t)n_regions; region_m++) { flow_SE_helper += commuting_strengths(region_n, region_m) * infectives_per_region(region_m) / m_population_after_commuting[{Region(region_m), AgeGroup(age_j)}]; From 7dc7e44309dd47f232d5573b893bd996a0641d5b Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:27:35 +0100 Subject: [PATCH 46/49] add simulations for paper model and comparison of basic reproduction number --- cpp/examples/ode_seir.cpp | 183 ++++++++++++++++--- cpp/examples/ode_seir_mobility.cpp | 282 +++++++++++++++++++++-------- 2 files changed, 365 insertions(+), 100 deletions(-) diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index c4e0bd0269..48049ab24d 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -26,40 +26,175 @@ #include "memilio/utils/time_series.h" #include "memilio/utils/time_series.h" +#include "memilio/io/io.h" +#include "memilio/io/result_io.h" +#include "memilio/io/epi_data.h" + +/** + * Set epidemiological parameters of Sars-CoV-2 for a immune-naive + * population and wild type variant. + * @param params Object that the parameters will be added to. + * @returns Currently generates no errors. + */ +mio::IOResult set_covid_parameters(mio::oseir::Parameters& params, bool synthetic_population) +{ + params.template set>(3.335); + if (!synthetic_population) { + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; + } + else { + params.template set>(8.097612257); + + params.template set>(0.07333); + } + + printf("Setting epidemiological parameters successful.\n"); + return mio::success(); +} + +/** + * indices of contact matrix corresponding to locations where contacts occur. + */ +enum class ContactLocation +{ + Home = 0, + School, + Work, + Other, + Count, +}; + +static const std::map contact_locations = {{ContactLocation::Home, "home"}, + {ContactLocation::School, "school_pf_eig"}, + {ContactLocation::Work, "work"}, + {ContactLocation::Other, "other"}}; + +/** + * Set contact matrices. + * Reads contact matrices from files in the data directory. + * @param data_dir data directory. + * @param params Object that the contact matrices will be added to. + * @returns any io errors that happen during reading of the files. + */ +mio::IOResult set_contact_matrices(const fs::path& data_dir, mio::oseir::Parameters& params, + bool synthetic_population) +{ + if (!synthetic_population) { + //TODO: io error handling + auto contact_matrices = mio::ContactMatrixGroup(contact_locations.size(), size_t(params.get_num_groups())); + for (auto&& contact_location : contact_locations) { + BOOST_OUTCOME_TRY(auto&& baseline, + mio::read_mobility_plain( + (data_dir / "contacts" / ("baseline_" + contact_location.second + ".txt")).string())); + BOOST_OUTCOME_TRY(auto&& minimum, + mio::read_mobility_plain( + (data_dir / "contacts" / ("minimum_" + contact_location.second + ".txt")).string())); + contact_matrices[size_t(contact_location.first)].get_baseline() = baseline; + contact_matrices[size_t(contact_location.first)].get_minimum() = minimum; + } + params.get>() = mio::UncertainContactMatrix(contact_matrices); + } + else { + mio::ContactMatrixGroup& contact_matrix = params.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(7.95 / (size_t)params.get_num_groups()); + } + + printf("Setting contact matrices successful.\n"); + return mio::success(); +} + +template +mio::IOResult set_population_data(mio::oseir::Model& model, const fs::path& data_dir) +{ + BOOST_OUTCOME_TRY( + auto&& node_ids, + mio::get_node_ids((data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true, + true)); + + BOOST_OUTCOME_TRY(const auto&& population_data, + mio::read_population_data( + (data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true)); + + for (auto&& entry : population_data) { + auto it = std::find_if(node_ids.begin(), node_ids.end(), [&entry](auto r) { + return r == 0 || + (entry.county_id && mio::regions::StateId(r) == mio::regions::get_state_id(int(*entry.county_id))) || + (entry.county_id && mio::regions::CountyId(r) == *entry.county_id) || + (entry.district_id && mio::regions::DistrictId(r) == *entry.district_id); + }); + if (it != node_ids.end()) { + for (size_t age = 0; age < (size_t)model.parameters.get_num_groups(); age++) { + model.populations[{mio::AgeGroup(age), mio::oseir::InfectionState::Susceptible}] += + entry.population[mio::AgeGroup(age)]; + } + } + } + + printf("Setting population data successful.\n"); + return mio::success(); +} +template +mio::IOResult set_parameters_and_population(mio::oseir::Model& model, const fs::path& data_dir, + bool synthetic_population) +{ + auto& populations = model.populations; + auto& parameters = model.parameters; + + size_t number_age_groups = (size_t)parameters.get_num_groups(); + + if (synthetic_population) { + printf("Data is not compatible, using synthetic population instead.\n"); + for (size_t j = 0; j < number_age_groups; j++) { + model.populations[{mio::AgeGroup(j), mio::oseir::InfectionState::Exposed}] = 100; + model.populations[{mio::AgeGroup(j), mio::oseir::InfectionState::Susceptible}] = 999900; + } + } + else { + BOOST_OUTCOME_TRY(set_population_data(model, data_dir)); + populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] -= 100; + populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] += 100; + } + + BOOST_OUTCOME_TRY(set_contact_matrices(data_dir, parameters, synthetic_population)) + + BOOST_OUTCOME_TRY(set_covid_parameters(parameters, synthetic_population)); + + return mio::success(); +} int main() { mio::set_log_level(mio::LogLevel::debug); - ScalarType t0 = 0; - ScalarType tmax = 0.2; + ScalarType t0 = 0.; + ScalarType tmax = 50.; ScalarType dt = 0.1; - mio::log_info("Simulating ODE SEIR; t={} ... {} with dt = {}.", t0, tmax, dt); - - int number_agegroups = 6; - mio::oseir::Model model(number_agegroups); - - ScalarType total_population = 286922; - for (int i = 0; i < number_agegroups; i++) { - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Exposed}] = 10; - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Infected}] = 0; - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Recovered}] = 0; - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Susceptible}] = - total_population - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Exposed}] - - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Infected}] - - model.populations[{mio::AgeGroup(i), mio::oseir::InfectionState::Recovered}]; + ScalarType number_age_groups = 6; + bool synthetic_population = false; + if (number_age_groups != 6) { + synthetic_population = true; } - model.parameters.set>(3.); - model.parameters.set>(5.); - model.parameters.set>(1.); + mio::log_info("Simulating ODE SEIR; t={} ... {} with dt = {}.", t0, tmax, dt); + + const std::string& data_dir = ""; - mio::ContactMatrixGroup& contact_matrix = model.parameters.get>(); - contact_matrix[0].get_baseline().setConstant(7.95); - // contact_matrix[0].add_damping(0.7, mio::SimulationTime(30.)); + mio::oseir::Model model(number_age_groups); + auto result_prepare_simulation = set_parameters_and_population(model, data_dir, synthetic_population); - model.check_constraints(); std::shared_ptr> integrator = std::make_shared>(); auto seir = simulate(t0, tmax, dt, model, integrator); @@ -67,6 +202,6 @@ int main() auto reproduction_numbers = model.get_reproduction_numbers(seir); std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; - seir.print_table({"S", "E", "I", "R"}); + // seir.print_table({"S", "E", "I", "R"}); // std::cout << "\nnumber total: " << seir.get_last_value().sum() << "\n"; } diff --git a/cpp/examples/ode_seir_mobility.cpp b/cpp/examples/ode_seir_mobility.cpp index 4cdab666b7..f77b795935 100644 --- a/cpp/examples/ode_seir_mobility.cpp +++ b/cpp/examples/ode_seir_mobility.cpp @@ -9,28 +9,201 @@ #include "models/ode_seir_mobility/parameters.h" #include "models/ode_seir_mobility/regions.h" #include "memilio/io/io.h" -#include "memilio/io/result_io.h" +#include "memilio/io/epi_data.h" + +/** + * Set epidemiological parameters of Sars-CoV-2 for a immune-naive + * population and wild type variant. + * @param params Object that the parameters will be added to. + * @returns Currently generates no errors. + */ +mio::IOResult set_covid_parameters(mio::oseirmobility::Parameters& params, bool synthetic_population) +{ + params.template set>(3.335); + if (!synthetic_population) { + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; + } + else { + params.template set>(8.097612257); + + params.template set>(0.07333); + } + + printf("Setting epidemiological parameters successful.\n"); + return mio::success(); +} + +/** + * indices of contact matrix corresponding to locations where contacts occur. + */ +enum class ContactLocation +{ + Home = 0, + School, + Work, + Other, + Count, +}; + +static const std::map contact_locations = {{ContactLocation::Home, "home"}, + {ContactLocation::School, "school_pf_eig"}, + {ContactLocation::Work, "work"}, + {ContactLocation::Other, "other"}}; + +/** + * Set contact matrices. + * Reads contact matrices from files in the data directory. + * @param data_dir data directory. + * @param params Object that the contact matrices will be added to. + * @returns any io errors that happen during reading of the files. + */ +mio::IOResult set_contact_matrices(const fs::path& data_dir, mio::oseirmobility::Parameters& params, + bool synthetic_population) +{ + if (!synthetic_population) { + //TODO: io error handling + auto contact_matrices = mio::ContactMatrixGroup(contact_locations.size(), size_t(params.get_num_agegroups())); + for (auto&& contact_location : contact_locations) { + BOOST_OUTCOME_TRY(auto&& baseline, + mio::read_mobility_plain( + (data_dir / "contacts" / ("baseline_" + contact_location.second + ".txt")).string())); + BOOST_OUTCOME_TRY(auto&& minimum, + mio::read_mobility_plain( + (data_dir / "contacts" / ("minimum_" + contact_location.second + ".txt")).string())); + contact_matrices[size_t(contact_location.first)].get_baseline() = baseline; + contact_matrices[size_t(contact_location.first)].get_minimum() = minimum; + } + params.get>() = + mio::UncertainContactMatrix(contact_matrices); + } + else { + mio::ContactMatrixGroup& contact_matrix = + params.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(7.95 / (size_t)params.get_num_agegroups()); + } + + printf("Setting contact matrices successful.\n"); + return mio::success(); +} + +template +mio::IOResult set_population_data(mio::oseirmobility::Model& model, const fs::path& data_dir) +{ + BOOST_OUTCOME_TRY( + auto&& node_ids, + mio::get_node_ids((data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true, + true)); + + BOOST_OUTCOME_TRY(const auto&& population_data, + mio::read_population_data( + (data_dir / "pydata" / "Germany" / "county_current_population_nrw.json").string(), true)); + + for (auto&& entry : population_data) { + auto it = std::find_if(node_ids.begin(), node_ids.end(), [&entry](auto r) { + return r == 0 || + (entry.county_id && mio::regions::StateId(r) == mio::regions::get_state_id(int(*entry.county_id))) || + (entry.county_id && mio::regions::CountyId(r) == *entry.county_id) || + (entry.district_id && mio::regions::DistrictId(r) == *entry.district_id); + }); + if (it != node_ids.end()) { + auto region_idx = size_t(it - node_ids.begin()); + for (size_t age = 0; age < (size_t)model.parameters.get_num_agegroups(); age++) { + model.populations[{mio::oseirmobility::Region(region_idx), mio::AgeGroup(age), + mio::oseirmobility::InfectionState::Susceptible}] = + entry.population[mio::AgeGroup(age)]; + } + } + } + + printf("Setting population data successful.\n"); + return mio::success(); +} template -mio::IOResult set_mobility_weights(const std::string& mobility_data, mio::oseirmobility::Model& model, - size_t number_regions) +mio::IOResult set_mobility_weights(mio::oseirmobility::Model& model, const fs::path& data_dir) { - // mobility between nodes - BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, - mio::read_mobility_plain(mobility_data + "/mobility" + "/commuter_migration_test.txt")); - if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || - mobility_data_commuter.cols() != Eigen::Index(number_regions)) { - return mio::failure(mio::StatusCode::InvalidValue, - "Mobility matrices do not have the correct size. You may need to run " - "transformMobilitydata.py from pycode memilio epidata package."); + size_t number_regions = (size_t)model.parameters.get_num_regions(); + if (number_regions == 1) { + model.parameters.template get>() + .get_cont_freq_mat()[0] + .get_baseline() + .setConstant(1.0); + + return mio::success(); + } + else { + // mobility between nodes + BOOST_OUTCOME_TRY(auto&& mobility_data_commuter, + mio::read_mobility_plain((data_dir / "mobility" / "commuter_mobility_nrw.txt").string())); + if (mobility_data_commuter.rows() != Eigen::Index(number_regions) || + mobility_data_commuter.cols() != Eigen::Index(number_regions)) { + return mio::failure(mio::StatusCode::InvalidValue, + "Mobility matrices do not have the correct size. You may need to run " + "transformMobilitydata.py from pycode memilio epidata package."); + } + + for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { + auto population_i = model.populations.get_group_total(mio::oseirmobility::Region(county_idx_i)); + mobility_data_commuter.row(county_idx_i) /= population_i; + } + model.parameters.template get>() + .get_cont_freq_mat()[0] + .get_baseline() = mobility_data_commuter; + + printf("Setting mobility weights successful.\n"); + return mio::success(); } +} - for (size_t county_idx_i = 0; county_idx_i < number_regions; ++county_idx_i) { - auto population_i = model.populations.get_group_total(mio::oseirmobility::Region(county_idx_i)); - mobility_data_commuter.row(county_idx_i) /= population_i; +template +mio::IOResult set_parameters_and_population(mio::oseirmobility::Model& model, const fs::path& data_dir, + bool synthetic_population) +{ + auto& populations = model.populations; + auto& parameters = model.parameters; + + size_t number_regions = (size_t)parameters.get_num_regions(); + size_t number_age_groups = (size_t)parameters.get_num_agegroups(); + + if (synthetic_population) { + printf("Data is not compatible, using synthetic population instead.\n"); + for (size_t j = 0; j < number_age_groups; j++) { + model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(j), + mio::oseirmobility::InfectionState::Exposed}] = 100; + model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(j), + mio::oseirmobility::InfectionState::Susceptible}] = 999900; + for (size_t i = 1; i < number_regions; i++) { + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(j), + mio::oseirmobility::InfectionState::Exposed}] = 0; + model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(j), + mio::oseirmobility::InfectionState::Susceptible}] = 1000000; + } + } + } + else { + BOOST_OUTCOME_TRY(set_population_data(model, data_dir)); + populations[{mio::oseirmobility::Region(27), mio::AgeGroup(0), + mio::oseirmobility::InfectionState::Susceptible}] -= 100; + populations[{mio::oseirmobility::Region(27), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Exposed}] += + 100; } - model.parameters.template get>().get_cont_freq_mat()[0].get_baseline() = - mobility_data_commuter; + BOOST_OUTCOME_TRY(set_mobility_weights(model, data_dir)); + + BOOST_OUTCOME_TRY(set_contact_matrices(data_dir, parameters, synthetic_population)) + + BOOST_OUTCOME_TRY(set_covid_parameters(parameters, synthetic_population)); return mio::success(); } @@ -40,80 +213,37 @@ int main() mio::set_log_level(mio::LogLevel::debug); ScalarType t0 = 0.; - ScalarType tmax = 15.; + ScalarType tmax = 50.; ScalarType dt = 0.1; - ScalarType number_regions = 2; + ScalarType number_regions = 53; std::vector region_ids(number_regions); iota(region_ids.begin(), region_ids.end(), 1); - ScalarType number_age_groups = 1; + ScalarType number_age_groups = 6; + bool synthetic_population = false; + if (number_age_groups != 6) { + synthetic_population = true; + } mio::log_info("Simulating SIR; t={} ... {} with dt = {}.", t0, tmax, dt); - const std::string& mobility_data = ""; + const std::string& data_dir = ""; mio::oseirmobility::Model model(number_regions, number_age_groups); - model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(0), mio::oseirmobility::InfectionState::Exposed}] = - 10; - model.populations[{mio::oseirmobility::Region(0), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Susceptible}] = 9990; - for (int i = 1; i < number_regions; i++) { - model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Exposed}] = 0; - model.populations[{mio::oseirmobility::Region(i), mio::AgeGroup(0), - mio::oseirmobility::InfectionState::Susceptible}] = 10000; - } - - model.parameters.set>(1.); + auto result_prepare_simulation = set_parameters_and_population(model, data_dir, synthetic_population); - model.parameters.set>(3.); - model.parameters.set>(5.); + // using DefaultIntegratorCore = + // mio::ControlledStepperWrapper; - 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.5, mio::SimulationTime(5)); - - auto result_preprocess = set_mobility_weights(mobility_data, model, number_regions); - - using DefaultIntegratorCore = - mio::ControlledStepperWrapper; - - std::shared_ptr> integrator = std::make_shared(); + std::shared_ptr> integrator = std::make_shared>(); model.check_constraints(); auto result_from_sim = simulate(t0, tmax, dt, model, integrator); - auto save_result_status = - mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_standard.h5"); - - auto reproduction_numbers = model.get_reproduction_numbers(result_from_sim); - std::cout << "\nbasis reproduction number: " << reproduction_numbers[0] << "\n"; - - // bool print_to_terminal = true; - - // result_from_sim.print_table(); - - // if (print_to_terminal) { - - // std::vector vars = {"S", "E", "I", "R"}; - // printf("\n # t"); - // for (size_t i = 0; i < (size_t)model.parameters.get_num_regions(); i++) { - // for (size_t k = 0; k < (size_t)mio::oseirmobility::InfectionState::Count; k++) { - // printf(" %s_%d", vars[k].c_str(), (int)i); - // } - // } - - // auto num_points = static_cast(sir.get_num_time_points()); - // for (size_t i = 0; i < num_points; i++) { - // printf("\n%.14f ", sir.get_time(i)); - // for (size_t k = 0; k < (size_t)model.parameters.get_num_regions(); k++) { - // for (size_t j = 0; j < (size_t)mio::oseirmobility::InfectionState::Count; j++) { - // printf(" %.14f", sir.get_value(i)[j + (size_t)mio::oseirmobility::InfectionState::Count * (int)k]); - // } - // } - // } - // printf("\n"); - // } + // auto save_result_status = + // mio::save_result({result_from_sim}, region_ids, number_regions * number_age_groups, "ode_result_standard2.h5"); + + auto basic_reproduction_number = model.get_reproduction_number(0, result_from_sim).value(); + std::cout << "\nbasis reproduction number: " << basic_reproduction_number << "\n"; } From 90a8d3e15e4966ade09a372eb0871e4d75e3454c Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:29:18 +0100 Subject: [PATCH 47/49] small changes in plots --- tools/plot_results_mobility.py | 49 +++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/tools/plot_results_mobility.py b/tools/plot_results_mobility.py index 99854994ae..21a0523d75 100644 --- a/tools/plot_results_mobility.py +++ b/tools/plot_results_mobility.py @@ -96,7 +96,7 @@ def plot_map_nrw(data: pd.DataFrame, # Use n+2 many columns (1: legend + 2: empty space + 3-n: data sets) and # n+2 rows where the top row is used for a potential title, the second row # for the content and all other rows have height zero. - height_ratios = [0.05, 1, 0] + height_ratios = [0.25, 1, 0] if len(data_columns) > 1: height_ratios = height_ratios + [ 0.0 for i in range(len(data_columns)-1)] @@ -135,7 +135,7 @@ def plot_map_nrw(data: pd.DataFrame, map_data.plot(data_columns[i], ax=ax, legend=False, vmin=scale_colors[0], vmax=scale_colors[1]) - ax.set_title(legend[i], fontsize=12) + ax.set_title(legend[i], fontsize=10) ax.set_axis_off() sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) @@ -148,9 +148,9 @@ def plot_map_nrw(data: pd.DataFrame, plt.savefig(os.path.join(output_path, fig_name + '.png'), dpi=dpi) plt.close() -def plot_maps(files, output_dir, name=''): +def plot_maps(files, output_dir, legend, name=''): - for date in range(10, 50, 10): + for date in range(10, 21, 10): dfs_all = extract_nrw_data_and_combine(files=files, date=date) min_val = dfs_all[dfs_all.columns[1:]].min().min() @@ -158,7 +158,7 @@ def plot_maps(files, output_dir, name=''): plot_map_nrw( dfs_all, scale_colors=[min_val, max_val], - legend=['', ''], + legend=legend, title='NRW - Simulation Day '+str(date), plot_colorbar=True, output_path=output_dir, fig_name=name+str(date), dpi=900, @@ -173,7 +173,8 @@ def extract_nrw_data_and_combine(files, date): i = 0 for file in files.values(): - if(i == 0): # result file of equation-based model has to be first + model_type = os.path.basename(file).split('_')[0] + if model_type == 'ode': # result file of equation-based model has to be first df = pm.extract_data( file, region_spec=None, column=None, date=date, filters={'Group': filter_age, 'InfectionState': [2]}, @@ -187,7 +188,6 @@ def extract_nrw_data_and_combine(files, date): ids = geoger.get_county_ids() ids = [id for id in ids if str(id).startswith('5')] - # ids = [5111, 5112, 5113] if len(ids) != len(df): raise gd.DataError("Data is not compatible with number of NRW counties.") @@ -229,26 +229,27 @@ def extract_nrw_data_and_combine(files, date): return dfs_all -def plot_total_compartment(files, output_dir, compartment = 'Infected', name='', title=''): +def plot_total_compartment(files, output_dir, legend, compartment = 'Infected', name='', title=''): - i = 0 + file_idx = 0 for file in files.values(): + model_type = os.path.basename(file).split('_')[0] # Load data. h5file = h5py.File(file + '.h5', 'r') - if i == 0: + if model_type=='ode': dates = h5file['1']['Time'][:] data = h5file['1']['Total'][:,compartments[compartment]] - plt.plot(dates, data, label='Equation-based model') + plt.plot(dates, data, label=legend[file_idx]) else: df = pd.DataFrame() regions = list(h5file.keys()) for i in range(len(regions)): df['Region'+str(i)] = h5file[regions[i]]['Total'][:, compartments[compartment]] df['Total'] = df.sum(axis=1) - df['Time'] = h5file['5111']['Time'][:] # hardcoded - plt.plot(df['Time'], df['Total'], label='Graph-based model') + df['Time'] = h5file[regions[0]]['Time'][:] # hardcoded + plt.plot(df['Time'], df['Total'], label=legend[file_idx], linestyle='dashed') - i = i+1 + file_idx = file_idx+1 plt.title(title) plt.legend() @@ -258,11 +259,23 @@ def plot_total_compartment(files, output_dir, compartment = 'Infected', name='', if __name__ == '__main__': - files_input = {'Data set 1': 'cpp/build/ode_result_nrw', - 'Data set 2': 'cpp/build/graph_result_nrw'} # Result file of equation-based model has to be first + files_input = {'Data set 1': 'cpp/build/ode_result_timing', + 'Data set 3': 'cpp/build/graph_result_timing'}#, + # 'Data set 2': 'cpp/build/ode_result_standard2'} + files_compare_solver = {'Data set 1': 'cpp/build/ode_result_nrw_euler', + 'Data set 2': 'cpp/build/ode_result_nrw_adaptive', + 'Data set 3': 'cpp/build/graph_result_nrw_euler', + 'Data set 4': 'cpp/build/graph_result_nrw_adaptive'} file_format = 'h5' + models = ['ODE Metapopulation model', + 'Graph-based hybrid ODE model', + 'ODE Metapopulation model (Wang)'] + plot_dir = os.path.join(os.path.dirname(__file__), '../Plots') - plot_maps(files=files_input,output_dir=plot_dir, name='NRWPlotDay') - plot_total_compartment(files=files_input, output_dir=plot_dir, compartment='Infected', name='infectives_total', title='Total infectives') + plot_maps(files={'Ode': 'cpp/build/graph_result_timing'}, output_dir=plot_dir, legend=['ODE'], name='TimingTest') + plot_total_compartment(files={'Ode': 'cpp/build/graph_result_timing'}, output_dir=plot_dir, legend=['Graph'], + compartment='Infected', name='timing_test', title='Total infectives') + # plot_total_compartment(files=files_input, output_dir=plot_dir, legend=['ODE', 'Graph'], + # compartment='Infected', name='infectives_total', title='Total infectives') From c78c996242577805e2429177faa7a63c13a9b9b4 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:15:16 +0100 Subject: [PATCH 48/49] fix runtime maybe --- cpp/models/ode_seir_mobility_improved/model.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cpp/models/ode_seir_mobility_improved/model.h b/cpp/models/ode_seir_mobility_improved/model.h index dea693d579..061be62017 100644 --- a/cpp/models/ode_seir_mobility_improved/model.h +++ b/cpp/models/ode_seir_mobility_improved/model.h @@ -59,15 +59,18 @@ class Model : public FlowModel n_regions = reduce_index>(params.get_num_regions()); for (size_t age_i = 0; age_i < (size_t)n_age_groups; age_i++) { for (size_t age_j = 0; age_j < (size_t)n_age_groups; age_j++) { - Eigen::VectorXd infectives_per_region = Eigen::VectorXd::Zero((size_t)n_regions); + Eigen::VectorXd infectious_share_per_region = Eigen::VectorXd::Zero((size_t)n_regions); for (size_t region_n = 0; region_n < (size_t)n_regions; region_n++) { for (size_t region_m = 0; region_m < (size_t)n_regions; region_m++) { - infectives_per_region(region_n) += + infectious_share_per_region(region_n) += commuting_strengths(region_m, region_n) * pop[population.get_flat_index( {Region(region_m), AgeGroup(age_j), InfectionState::Infected})]; } + infectious_share_per_region(region_n) /= + m_population_after_commuting[{Region(region_n), AgeGroup(age_j)}]; } + Eigen::VectorXd infections_due_commuting = commuting_strengths * infectious_share_per_region; for (size_t region_n = 0; region_n < (size_t)n_regions; region_n++) { FP flow_SE_helper = 0; const size_t Ejn = @@ -85,11 +88,7 @@ class Model : public FlowModel>().get_cont_freq_mat().get_matrix_at(t)(age_i, age_j) * params.template get>()[AgeGroup(age_i)]; - flow_SE_helper += pop[Ijn] * Nj_inv; - for (size_t region_m = 0; region_m < (size_t)n_regions; region_m++) { - flow_SE_helper += commuting_strengths(region_n, region_m) * infectives_per_region(region_m) / - m_population_after_commuting[{Region(region_m), AgeGroup(age_j)}]; - } + flow_SE_helper += pop[Ijn] * Nj_inv + infections_due_commuting(region_n); flows[Base::template get_flat_flow_index( {Region(region_n), AgeGroup(age_i)})] += flow_SE_helper * coeffStoI * From cd056b5c7d2c429036e3d5919b9c1450423e79e2 Mon Sep 17 00:00:00 2001 From: Carlotta Gerstein <100771374+charlie0614@users.noreply.github.com> Date: Tue, 31 Dec 2024 19:37:16 +0100 Subject: [PATCH 49/49] adjust timing examples for single age group --- cpp/examples/graph_timing.cpp | 66 ++++++++++++-------- cpp/examples/ode_seir_mobility_timing.cpp | 75 ++++++++++++++--------- 2 files changed, 88 insertions(+), 53 deletions(-) diff --git a/cpp/examples/graph_timing.cpp b/cpp/examples/graph_timing.cpp index 8759125419..2041a9768c 100644 --- a/cpp/examples/graph_timing.cpp +++ b/cpp/examples/graph_timing.cpp @@ -7,14 +7,23 @@ #include +bool age_groups = true; + void set_contact_matrices(mio::oseir::Parameters& params) { - Eigen::MatrixXd contact_matrix_eigen(6, 6); - contact_matrix_eigen << 3.9547, 1.1002, 2.9472, 2.05, 0.3733, 0.0445, 0.3327, 3.5892, 1.236, 1.9208, 0.2681, 0.0161, - 0.246, 0.7124, 5.6518, 3.2939, 0.2043, 0.0109, 0.1742, 0.8897, 3.3124, 4.5406, 0.4262, 0.0214, 0.0458, 0.1939, - 0.5782, 1.3825, 1.473, 0.0704, 0.1083, 0.1448, 0.4728, 0.9767, 0.6266, 0.1724; - mio::ContactMatrixGroup& contact_matrix = params.template get>().get_cont_freq_mat(); - contact_matrix[0].get_baseline() = contact_matrix_eigen; + if (age_groups) { + Eigen::MatrixXd contact_matrix_eigen(6, 6); + contact_matrix_eigen << 3.9547, 1.1002, 2.9472, 2.05, 0.3733, 0.0445, 0.3327, 3.5892, 1.236, 1.9208, 0.2681, + 0.0161, 0.246, 0.7124, 5.6518, 3.2939, 0.2043, 0.0109, 0.1742, 0.8897, 3.3124, 4.5406, 0.4262, 0.0214, + 0.0458, 0.1939, 0.5782, 1.3825, 1.473, 0.0704, 0.1083, 0.1448, 0.4728, 0.9767, 0.6266, 0.1724; + mio::ContactMatrixGroup& contact_matrix = + params.template get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline() = contact_matrix_eigen; + } + else { + mio::ContactMatrixGroup& contact_matrix = params.get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(7.95); + } } /** @@ -27,19 +36,25 @@ void set_covid_parameters(mio::oseir::Parameters& params) { params.template set>(3.335); - params.get>()[mio::AgeGroup(0)] = 8.0096875; - params.get>()[mio::AgeGroup(1)] = 8.0096875; - params.get>()[mio::AgeGroup(2)] = 8.2182; - params.get>()[mio::AgeGroup(3)] = 8.1158; - params.get>()[mio::AgeGroup(4)] = 8.033; - params.get>()[mio::AgeGroup(5)] = 7.985; - - params.get>()[mio::AgeGroup(0)] = 0.03; - params.get>()[mio::AgeGroup(1)] = 0.06; - params.get>()[mio::AgeGroup(2)] = 0.06; - params.get>()[mio::AgeGroup(3)] = 0.06; - params.get>()[mio::AgeGroup(4)] = 0.09; - params.get>()[mio::AgeGroup(5)] = 0.175; + if (age_groups) { + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; + } + else { + params.get>()[mio::AgeGroup(0)] = 0.07333; + params.get>()[mio::AgeGroup(0)] = 8.097612257; + } } void set_population_data(mio::oseir::Parameters& params, @@ -60,10 +75,10 @@ void set_population_data(mio::oseir::Parameters& params, node.parameters = params; node.populations = population; } - for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { - nodes[0].populations[{i, mio::oseir::InfectionState::Exposed}] = 100; - nodes[0].populations[{i, mio::oseir::InfectionState::Susceptible}] = 999900; - } + // for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) { + nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Exposed}] += 100; + nodes[0].populations[{mio::AgeGroup(0), mio::oseir::InfectionState::Susceptible}] -= 100; + // } for (size_t node_idx = 0; node_idx < nodes.size(); ++node_idx) { params_graph.add_node(node_idx, nodes[node_idx]); @@ -74,7 +89,10 @@ void set_parameters_and_population(mio::Graph>& params_graph, size_t number_regions) { - const int num_age_groups = 6; + int num_age_groups = 1; + if (age_groups) { + num_age_groups = 6; + } mio::oseir::Parameters params(num_age_groups); diff --git a/cpp/examples/ode_seir_mobility_timing.cpp b/cpp/examples/ode_seir_mobility_timing.cpp index bd1052420f..9fd9c0823f 100644 --- a/cpp/examples/ode_seir_mobility_timing.cpp +++ b/cpp/examples/ode_seir_mobility_timing.cpp @@ -28,16 +28,25 @@ #include +bool age_groups = true; + template void set_contact_matrix(mio::oseirmobilityimproved::Model& model) { - Eigen::MatrixXd contact_matrix_eigen(6, 6); - contact_matrix_eigen << 3.9547, 1.1002, 2.9472, 2.05, 0.3733, 0.0445, 0.3327, 3.5892, 1.236, 1.9208, 0.2681, 0.0161, - 0.246, 0.7124, 5.6518, 3.2939, 0.2043, 0.0109, 0.1742, 0.8897, 3.3124, 4.5406, 0.4262, 0.0214, 0.0458, 0.1939, - 0.5782, 1.3825, 1.473, 0.0704, 0.1083, 0.1448, 0.4728, 0.9767, 0.6266, 0.1724; - mio::ContactMatrixGroup& contact_matrix = - model.parameters.template get>().get_cont_freq_mat(); - contact_matrix[0].get_baseline() = contact_matrix_eigen; + if (age_groups) { + Eigen::MatrixXd contact_matrix_eigen(6, 6); + contact_matrix_eigen << 3.9547, 1.1002, 2.9472, 2.05, 0.3733, 0.0445, 0.3327, 3.5892, 1.236, 1.9208, 0.2681, + 0.0161, 0.246, 0.7124, 5.6518, 3.2939, 0.2043, 0.0109, 0.1742, 0.8897, 3.3124, 4.5406, 0.4262, 0.0214, + 0.0458, 0.1939, 0.5782, 1.3825, 1.473, 0.0704, 0.1083, 0.1448, 0.4728, 0.9767, 0.6266, 0.1724; + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline() = contact_matrix_eigen; + } + { + mio::ContactMatrixGroup& contact_matrix = + model.parameters.template get>().get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(7.95); + } } /** @@ -50,19 +59,25 @@ void set_covid_parameters(mio::oseirmobilityimproved::Parameters& params { params.template set>(3.335); - params.get>()[mio::AgeGroup(0)] = 8.0096875; - params.get>()[mio::AgeGroup(1)] = 8.0096875; - params.get>()[mio::AgeGroup(2)] = 8.2182; - params.get>()[mio::AgeGroup(3)] = 8.1158; - params.get>()[mio::AgeGroup(4)] = 8.033; - params.get>()[mio::AgeGroup(5)] = 7.985; - - params.get>()[mio::AgeGroup(0)] = 0.03; - params.get>()[mio::AgeGroup(1)] = 0.06; - params.get>()[mio::AgeGroup(2)] = 0.06; - params.get>()[mio::AgeGroup(3)] = 0.06; - params.get>()[mio::AgeGroup(4)] = 0.09; - params.get>()[mio::AgeGroup(5)] = 0.175; + if (age_groups) { + params.get>()[mio::AgeGroup(0)] = 8.0096875; + params.get>()[mio::AgeGroup(1)] = 8.0096875; + params.get>()[mio::AgeGroup(2)] = 8.2182; + params.get>()[mio::AgeGroup(3)] = 8.1158; + params.get>()[mio::AgeGroup(4)] = 8.033; + params.get>()[mio::AgeGroup(5)] = 7.985; + + params.get>()[mio::AgeGroup(0)] = 0.03; + params.get>()[mio::AgeGroup(1)] = 0.06; + params.get>()[mio::AgeGroup(2)] = 0.06; + params.get>()[mio::AgeGroup(3)] = 0.06; + params.get>()[mio::AgeGroup(4)] = 0.09; + params.get>()[mio::AgeGroup(5)] = 0.175; + } + else { + params.get>()[mio::AgeGroup(0)] = 0.07333; + params.get>()[mio::AgeGroup(0)] = 8.097612257; + } } template @@ -91,17 +106,15 @@ void set_parameters_and_population(mio::oseirmobilityimproved::Model& model) size_t number_regions = (size_t)parameters.get_num_regions(); size_t number_age_groups = (size_t)parameters.get_num_agegroups(); for (size_t j = 0; j < number_age_groups; j++) { - model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 100; - model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 999900; - for (size_t i = 1; i < number_regions; i++) { - model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Exposed}] = 0; + for (size_t i = 0; i < number_regions; i++) { model.populations[{mio::oseirmobilityimproved::Region(i), mio::AgeGroup(j), - mio::oseirmobilityimproved::InfectionState::Susceptible}] = 1000000; + mio::oseirmobilityimproved::InfectionState::Susceptible}] = 10000; } } + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Exposed}] += 100; + model.populations[{mio::oseirmobilityimproved::Region(0), mio::AgeGroup(0), + mio::oseirmobilityimproved::InfectionState::Susceptible}] -= 100; set_mobility_weights(model); set_contact_matrix(model); @@ -133,9 +146,13 @@ void set_parameters_and_population(mio::oseirmobilityimproved::Model& model) void simulate(size_t num_warm_up_runs, size_t num_runs, size_t number_regions, ScalarType tmax) { + mio::set_log_level(mio::LogLevel::off); ScalarType t0 = 0.; ScalarType dt = 0.1; - ScalarType number_age_groups = 6; + ScalarType number_age_groups = 1; + if (age_groups) { + number_age_groups = 6; + } mio::oseirmobilityimproved::Model model(number_regions, number_age_groups); set_parameters_and_population(model);