diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 0a16c654d3..b0219176e0 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -76,7 +76,7 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list mio::UniformIntDistribution::get_instance()(prng, 1, int(mio::abm::InfectionState::Count) - 1)); auto infection = mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, person.get_age(), model.parameters, mio::abm::TimePoint(0), state); - person.add_new_infection(std::move(infection)); + person.add_new_infection(std::move(infection), prng, mio::abm::TimePoint(0), model.parameters); } //equal chance of (moderate) mask refusal and (moderate) mask eagerness diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index b07737f924..b683a1a293 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -116,6 +116,10 @@ add_executable(abm_minimal_example abm_minimal.cpp) target_link_libraries(abm_minimal_example PRIVATE memilio abm) target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(abm_aims_halle abm_aims_halle.cpp) +target_link_libraries(abm_aims_halle PRIVATE memilio abm) +target_compile_options(abm_aims_halle PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + if(MEMILIO_HAS_HDF5) add_executable(abm_parameter_study_example abm_parameter_study.cpp) target_link_libraries(abm_parameter_study_example PRIVATE memilio abm) diff --git a/cpp/examples/abm_aims_halle.cpp b/cpp/examples/abm_aims_halle.cpp new file mode 100644 index 0000000000..c8d0ef1214 --- /dev/null +++ b/cpp/examples/abm_aims_halle.cpp @@ -0,0 +1,1012 @@ +/* +* Copyright (C) 2020-2025 MEmilio +* +* Authors: Rene Schmieding, Sascha Korf +* +* 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 "abm/result_simulation.h" +#include "abm/household.h" +#include "abm/lockdown_rules.h" +#include "abm/model.h" +#include "abm/time.h" +#include "abm/protection_event.h" + +#include "boost/filesystem.hpp" +#include "boost/algorithm/string/split.hpp" +#include "boost/algorithm/string/classification.hpp" + +#include "memilio/compartments/parameter_studies.h" +#include "memilio/data/analyze_result.h" +#include "memilio/io/io.h" +#include "memilio/io/directories.h" +#include "memilio/io/result_io.h" +#include "memilio/utils/logging.h" +#include "memilio/utils/miompi.h" +#include "memilio/utils/random_number_generator.h" +#include "memilio/utils/stl_util.h" + +#include + +namespace fs = std::filesystem; +constexpr size_t num_age_groups = 6; + +const auto age_group_0_to_4 = mio::AgeGroup(0); +const auto age_group_5_to_14 = mio::AgeGroup(1); +const auto age_group_15_to_34 = mio::AgeGroup(2); +const auto age_group_35_to_59 = mio::AgeGroup(3); +const auto age_group_60_to_79 = mio::AgeGroup(4); +const auto age_group_80_plus = mio::AgeGroup(5); + +std::pair get_my_and_sigma(std::pair mean_and_std) +{ + auto mean = mean_and_std.first; + auto stddev = mean_and_std.second; + double my = log(mean * mean / sqrt(mean * mean + stddev * stddev)); + double sigma = sqrt(log(1 + stddev * stddev / (mean * mean))); + return {my, sigma}; +} + +void set_parameters(mio::abm::Parameters& params) +{ + // Set the Time parameters for the infection same for every age group for now + + auto my_and_sigma_exposed = get_my_and_sigma({4.5, 1.5}); + params.get() = + mio::ParameterDistributionLogNormal(my_and_sigma_exposed.first, my_and_sigma_exposed.second); + + auto my_and_sigma_no_symptoms_to_symptoms = get_my_and_sigma({1.1, 0.9}); + params.get() = mio::ParameterDistributionLogNormal( + my_and_sigma_no_symptoms_to_symptoms.first, my_and_sigma_no_symptoms_to_symptoms.second); + + auto my_and_sigma_no_symptoms_to_recovered = get_my_and_sigma({8.0, 2.0}); + params.get() = mio::ParameterDistributionLogNormal( + my_and_sigma_no_symptoms_to_recovered.first, my_and_sigma_no_symptoms_to_recovered.second); + + auto my_and_sigma_symptoms_to_severe = get_my_and_sigma({6.6, 4.9}); + params.get() = mio::ParameterDistributionLogNormal( + my_and_sigma_symptoms_to_severe.first, my_and_sigma_symptoms_to_severe.second); + + auto my_and_sigma_symptoms_to_recovered = get_my_and_sigma({18.1, 6.3}); + params.get() = mio::ParameterDistributionLogNormal( + my_and_sigma_symptoms_to_recovered.first, my_and_sigma_symptoms_to_recovered.second); + + auto my_and_sigma_severe_to_critical = get_my_and_sigma({1.5, 2.0}); + params.get() = mio::ParameterDistributionLogNormal( + my_and_sigma_severe_to_critical.first, my_and_sigma_severe_to_critical.second); + + auto my_and_sigma_severe_to_recovered = get_my_and_sigma({18.1, 6.3}); + params.get() = mio::ParameterDistributionLogNormal( + my_and_sigma_severe_to_recovered.first, my_and_sigma_severe_to_recovered.second); + + auto my_and_sigma_severe_to_dead = get_my_and_sigma({10.7, 4.8}); + params.get() = + mio::ParameterDistributionLogNormal(my_and_sigma_severe_to_dead.first, my_and_sigma_severe_to_dead.second); + + auto my_and_sigma_critical_to_dead = get_my_and_sigma({10.7, 4.8}); + params.get() = + mio::ParameterDistributionLogNormal(my_and_sigma_critical_to_dead.first, my_and_sigma_critical_to_dead.second); + + auto my_and_sigma_critical_to_recovered = get_my_and_sigma({18.1, 6.3}); + params.get() = mio::ParameterDistributionLogNormal( + my_and_sigma_critical_to_recovered.first, my_and_sigma_critical_to_recovered.second); + + //Set testing parameters + auto pcr_test_values = mio::abm::TestParameters{0.9, 0.995, mio::abm::hours(24), mio::abm::TestType::PCR}; + auto antigen_test_values = + mio::abm::TestParameters{0.9, 0.9999, mio::abm::minutes(15), mio::abm::TestType::Antigen}; + auto generic_test_values = mio::abm::TestParameters{0.7, 0.95, mio::abm::minutes(30), mio::abm::TestType::Generic}; + + params.get()[mio::abm::TestType::PCR] = pcr_test_values; + params.get()[mio::abm::TestType::Antigen] = antigen_test_values; + params.get()[mio::abm::TestType::Generic] = generic_test_values; + + // Set percentage parameters + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.50; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = 0.55; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = + 0.60; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = + 0.70; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = + 0.83; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.90; + + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.02; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = 0.03; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = 0.04; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = 0.07; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.17; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.24; + + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.1; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = 0.11; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = 0.12; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = 0.14; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.33; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.62; + + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.12; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = 0.13; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = 0.15; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = 0.26; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.40; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.48; + + // Set infection parameters + // Set protection level against an severe infection. + params.get() = + mio::TimeSeriesFunctor{mio::TimeSeriesFunctorType::LinearInterpolation, {{0, 0.8}, {150, 0.8}}}; + params.get() = + mio::TimeSeriesFunctor{mio::TimeSeriesFunctorType::LinearInterpolation, {{0, 0.0}, {150, 0.0}}}; + + // Set PAIS parameters + // PAISProbability is 0.0 by default. Set only values that are different from 0.0 here. + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34, + mio::abm::Sex::Female, mio::abm::VaccinationClass::Zero}] = 0.108; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34, mio::abm::Sex::Male, + mio::abm::VaccinationClass::Zero}] = 0.051; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59, + mio::abm::Sex::Female, mio::abm::VaccinationClass::Zero}] = 0.136; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59, mio::abm::Sex::Male, + mio::abm::VaccinationClass::Zero}] = 0.105; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79, + mio::abm::Sex::Female, mio::abm::VaccinationClass::Zero}] = 0.188; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79, mio::abm::Sex::Male, + mio::abm::VaccinationClass::Zero}] = 0.108; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus, mio::abm::Sex::Female, + mio::abm::VaccinationClass::Zero}] = 0.192; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus, mio::abm::Sex::Male, + mio::abm::VaccinationClass::Zero}] = 0.175; + + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34, + mio::abm::Sex::Female, mio::abm::VaccinationClass::OneOrTwo}] = 0.167; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34, mio::abm::Sex::Male, + mio::abm::VaccinationClass::OneOrTwo}] = 0.098; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59, + mio::abm::Sex::Female, mio::abm::VaccinationClass::OneOrTwo}] = 0.189; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59, mio::abm::Sex::Male, + mio::abm::VaccinationClass::OneOrTwo}] = 0.133; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79, + mio::abm::Sex::Female, mio::abm::VaccinationClass::OneOrTwo}] = 0.166; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79, mio::abm::Sex::Male, + mio::abm::VaccinationClass::OneOrTwo}] = 0.109; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus, mio::abm::Sex::Female, + mio::abm::VaccinationClass::OneOrTwo}] = 0.137; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus, mio::abm::Sex::Male, + mio::abm::VaccinationClass::OneOrTwo}] = 0.078; + + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34, + mio::abm::Sex::Female, mio::abm::VaccinationClass::ThreeOrMore}] = 0.156; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34, mio::abm::Sex::Male, + mio::abm::VaccinationClass::ThreeOrMore}] = 0.107; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59, + mio::abm::Sex::Female, mio::abm::VaccinationClass::ThreeOrMore}] = 0.193; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59, mio::abm::Sex::Male, + mio::abm::VaccinationClass::ThreeOrMore}] = 0.122; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79, + mio::abm::Sex::Female, mio::abm::VaccinationClass::ThreeOrMore}] = 0.163; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79, mio::abm::Sex::Male, + mio::abm::VaccinationClass::ThreeOrMore}] = 0.106; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus, mio::abm::Sex::Female, + mio::abm::VaccinationClass::ThreeOrMore}] = 0.154; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus, mio::abm::Sex::Male, + mio::abm::VaccinationClass::ThreeOrMore}] = 0.101; + + params.get()[{mio::abm::VirusVariant::Wildtype, + mio::abm::VaccinationClass::Zero}] = 0.267; + params.get()[{mio::abm::VirusVariant::Wildtype, + mio::abm::VaccinationClass::OneOrTwo}] = 0.247; + params.get()[{mio::abm::VirusVariant::Wildtype, + mio::abm::VaccinationClass::ThreeOrMore}] = 0.217; + params.get()[{mio::abm::VirusVariant::Wildtype, + mio::abm::VaccinationClass::Zero}] = 0.038; + params.get()[{mio::abm::VirusVariant::Wildtype, + mio::abm::VaccinationClass::OneOrTwo}] = 0.116; + params.get()[{mio::abm::VirusVariant::Wildtype, + mio::abm::VaccinationClass::ThreeOrMore}] = 0.118; + + Eigen::MatrixXd pais_transition_matrix = Eigen::MatrixXd::Zero( + static_cast(mio::abm::PAISState::Count), static_cast(mio::abm::PAISState::Count)); + pais_transition_matrix(static_cast(mio::abm::PAISState::Healthy), + static_cast(mio::abm::PAISState::Healthy)) = 0.9925839491519293; + pais_transition_matrix(static_cast(mio::abm::PAISState::Healthy), + static_cast(mio::abm::PAISState::Medium)) = 0.00026973522588106033; + pais_transition_matrix(static_cast(mio::abm::PAISState::Healthy), + static_cast(mio::abm::PAISState::Severe)) = 0.007146315622189754; + pais_transition_matrix(static_cast(mio::abm::PAISState::Medium), + static_cast(mio::abm::PAISState::Healthy)) = 1.879727103567679e-5; + pais_transition_matrix(static_cast(mio::abm::PAISState::Medium), + static_cast(mio::abm::PAISState::Medium)) = 1.1042796363187963e-9; + pais_transition_matrix(static_cast(mio::abm::PAISState::Medium), + static_cast(mio::abm::PAISState::Severe)) = 0.9999812016246846; + pais_transition_matrix(static_cast(mio::abm::PAISState::Severe), + static_cast(mio::abm::PAISState::Healthy)) = 0.23940186497365926; + pais_transition_matrix(static_cast(mio::abm::PAISState::Severe), + static_cast(mio::abm::PAISState::Medium)) = 0.6049572234530622; + pais_transition_matrix(static_cast(mio::abm::PAISState::Severe), + static_cast(mio::abm::PAISState::Severe)) = 0.15564091157327845; + params.get() = pais_transition_matrix; + + //Set other parameters + params.get() = 0.0; + params.get()[{mio::abm::VirusVariant::Wildtype}] = 1.6; + //params.get() = 0.5; + //params.get({mio::abm::MaskType::None}) = 0.0; + params.get() = mio::abm::days(10); + params.get() = 0.5; +} + +// set location specific parameters +void set_local_parameters(mio::abm::Model& model) +{ + const int n_age_groups = (int)model.parameters.get_num_groups(); + + // setting this up in matrix-form would be much nicer, + // but we somehow can't construct Eigen object with initializer lists + /* baseline_home + 0.4413 0.4504 1.2383 0.8033 0.0494 0.0017 + 0.0485 0.7616 0.6532 1.1614 0.0256 0.0013 + 0.1800 0.1795 0.8806 0.6413 0.0429 0.0032 + 0.0495 0.2639 0.5189 0.8277 0.0679 0.0014 + 0.0087 0.0394 0.1417 0.3834 0.7064 0.0447 + 0.0292 0.0648 0.1248 0.4179 0.3497 0.1544 + */ + mio::ContactMatrix contacts_home(static_cast(n_age_groups)); + contacts_home.get_baseline()(age_group_0_to_4.get(), age_group_0_to_4.get()) = 0.4413; + contacts_home.get_baseline()(age_group_0_to_4.get(), age_group_5_to_14.get()) = 0.0504; + contacts_home.get_baseline()(age_group_0_to_4.get(), age_group_15_to_34.get()) = 1.2383; + contacts_home.get_baseline()(age_group_0_to_4.get(), age_group_35_to_59.get()) = 0.8033; + contacts_home.get_baseline()(age_group_0_to_4.get(), age_group_60_to_79.get()) = 0.0494; + contacts_home.get_baseline()(age_group_0_to_4.get(), age_group_80_plus.get()) = 0.0017; + contacts_home.get_baseline()(age_group_5_to_14.get(), age_group_0_to_4.get()) = 0.0485; + contacts_home.get_baseline()(age_group_5_to_14.get(), age_group_5_to_14.get()) = 0.7616; + contacts_home.get_baseline()(age_group_5_to_14.get(), age_group_15_to_34.get()) = 0.6532; + contacts_home.get_baseline()(age_group_5_to_14.get(), age_group_35_to_59.get()) = 1.1614; + contacts_home.get_baseline()(age_group_5_to_14.get(), age_group_60_to_79.get()) = 0.0256; + contacts_home.get_baseline()(age_group_5_to_14.get(), age_group_80_plus.get()) = 0.0013; + contacts_home.get_baseline()(age_group_15_to_34.get(), age_group_0_to_4.get()) = 0.1800; + contacts_home.get_baseline()(age_group_15_to_34.get(), age_group_5_to_14.get()) = 0.1795; + contacts_home.get_baseline()(age_group_15_to_34.get(), age_group_15_to_34.get()) = 0.8806; + contacts_home.get_baseline()(age_group_15_to_34.get(), age_group_35_to_59.get()) = 0.6413; + contacts_home.get_baseline()(age_group_15_to_34.get(), age_group_60_to_79.get()) = 0.0429; + contacts_home.get_baseline()(age_group_15_to_34.get(), age_group_80_plus.get()) = 0.0032; + contacts_home.get_baseline()(age_group_35_to_59.get(), age_group_0_to_4.get()) = 0.0495; + contacts_home.get_baseline()(age_group_35_to_59.get(), age_group_5_to_14.get()) = 0.2639; + contacts_home.get_baseline()(age_group_35_to_59.get(), age_group_15_to_34.get()) = 0.5189; + contacts_home.get_baseline()(age_group_35_to_59.get(), age_group_35_to_59.get()) = 0.8277; + contacts_home.get_baseline()(age_group_35_to_59.get(), age_group_60_to_79.get()) = 0.0679; + contacts_home.get_baseline()(age_group_35_to_59.get(), age_group_80_plus.get()) = 0.0014; + contacts_home.get_baseline()(age_group_60_to_79.get(), age_group_0_to_4.get()) = 0.0087; + contacts_home.get_baseline()(age_group_60_to_79.get(), age_group_5_to_14.get()) = 0.0394; + contacts_home.get_baseline()(age_group_60_to_79.get(), age_group_15_to_34.get()) = 0.1417; + contacts_home.get_baseline()(age_group_60_to_79.get(), age_group_35_to_59.get()) = 0.3834; + contacts_home.get_baseline()(age_group_60_to_79.get(), age_group_60_to_79.get()) = 0.7064; + contacts_home.get_baseline()(age_group_60_to_79.get(), age_group_80_plus.get()) = 0.0447; + contacts_home.get_baseline()(age_group_80_plus.get(), age_group_0_to_4.get()) = 0.0292; + contacts_home.get_baseline()(age_group_80_plus.get(), age_group_5_to_14.get()) = 0.0648; + contacts_home.get_baseline()(age_group_80_plus.get(), age_group_15_to_34.get()) = 0.1248; + contacts_home.get_baseline()(age_group_80_plus.get(), age_group_35_to_59.get()) = 0.4179; + contacts_home.get_baseline()(age_group_80_plus.get(), age_group_60_to_79.get()) = 0.3497; + contacts_home.get_baseline()(age_group_80_plus.get(), age_group_80_plus.get()) = 0.1544; + + /* baseline_school + 1.1165 0.2741 0.2235 0.1028 0.0007 0.0000 + 0.1627 1.9412 0.2431 0.1780 0.0130 0.0000 + 0.0148 0.1646 1.1266 0.0923 0.0074 0.0000 + 0.0367 0.1843 0.3265 0.0502 0.0021 0.0005 + 0.0004 0.0370 0.0115 0.0014 0.0039 0.0000 + 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 + */ + mio::ContactMatrix contacts_school(static_cast(n_age_groups)); + contacts_school.get_baseline()(age_group_0_to_4.get(), age_group_0_to_4.get()) = 1.1165; + contacts_school.get_baseline()(age_group_0_to_4.get(), age_group_5_to_14.get()) = 0.2741; + contacts_school.get_baseline()(age_group_0_to_4.get(), age_group_15_to_34.get()) = 0.2235; + contacts_school.get_baseline()(age_group_0_to_4.get(), age_group_35_to_59.get()) = 0.1028; + contacts_school.get_baseline()(age_group_0_to_4.get(), age_group_60_to_79.get()) = 0.0007; + contacts_school.get_baseline()(age_group_0_to_4.get(), age_group_80_plus.get()) = 0.0000; + contacts_school.get_baseline()(age_group_5_to_14.get(), age_group_0_to_4.get()) = 0.1627; + contacts_school.get_baseline()(age_group_5_to_14.get(), age_group_5_to_14.get()) = 1.9412; + contacts_school.get_baseline()(age_group_5_to_14.get(), age_group_15_to_34.get()) = 0.2431; + contacts_school.get_baseline()(age_group_5_to_14.get(), age_group_35_to_59.get()) = 0.1780; + contacts_school.get_baseline()(age_group_5_to_14.get(), age_group_60_to_79.get()) = 0.0130; + contacts_school.get_baseline()(age_group_5_to_14.get(), age_group_80_plus.get()) = 0.0000; + contacts_school.get_baseline()(age_group_15_to_34.get(), age_group_0_to_4.get()) = 0.0148; + contacts_school.get_baseline()(age_group_15_to_34.get(), age_group_5_to_14.get()) = 0.1646; + contacts_school.get_baseline()(age_group_15_to_34.get(), age_group_15_to_34.get()) = 1.1266; + contacts_school.get_baseline()(age_group_15_to_34.get(), age_group_35_to_59.get()) = 0.0923; + contacts_school.get_baseline()(age_group_15_to_34.get(), age_group_60_to_79.get()) = 0.0074; + contacts_school.get_baseline()(age_group_15_to_34.get(), age_group_80_plus.get()) = 0.0000; + contacts_school.get_baseline()(age_group_35_to_59.get(), age_group_0_to_4.get()) = 0.0367; + contacts_school.get_baseline()(age_group_35_to_59.get(), age_group_5_to_14.get()) = 0.1843; + contacts_school.get_baseline()(age_group_35_to_59.get(), age_group_15_to_34.get()) = 0.3265; + contacts_school.get_baseline()(age_group_35_to_59.get(), age_group_35_to_59.get()) = 0.0502; + contacts_school.get_baseline()(age_group_35_to_59.get(), age_group_60_to_79.get()) = 0.0021; + contacts_school.get_baseline()(age_group_35_to_59.get(), age_group_80_plus.get()) = 0.0005; + contacts_school.get_baseline()(age_group_60_to_79.get(), age_group_0_to_4.get()) = 0.0004; + contacts_school.get_baseline()(age_group_60_to_79.get(), age_group_5_to_14.get()) = 0.0370; + contacts_school.get_baseline()(age_group_60_to_79.get(), age_group_15_to_34.get()) = 0.0115; + contacts_school.get_baseline()(age_group_60_to_79.get(), age_group_35_to_59.get()) = 0.0014; + contacts_school.get_baseline()(age_group_60_to_79.get(), age_group_60_to_79.get()) = 0.0039; + contacts_school.get_baseline()(age_group_60_to_79.get(), age_group_80_plus.get()) = 0.0000; + contacts_school.get_baseline()(age_group_80_plus.get(), age_group_0_to_4.get()) = 0.0000; + contacts_school.get_baseline()(age_group_80_plus.get(), age_group_5_to_14.get()) = 0.0000; + contacts_school.get_baseline()(age_group_80_plus.get(), age_group_15_to_34.get()) = 0.0000; + contacts_school.get_baseline()(age_group_80_plus.get(), age_group_35_to_59.get()) = 0.0000; + contacts_school.get_baseline()(age_group_80_plus.get(), age_group_60_to_79.get()) = 0.0000; + contacts_school.get_baseline()(age_group_80_plus.get(), age_group_80_plus.get()) = 0.0000; + + /* baseline_work + 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 + 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 + 0.0000 0.0127 1.7570 1.6050 0.0133 0.0000 + 0.0000 0.0020 1.0311 2.3166 0.0098 0.0000 + 0.0000 0.0002 0.0194 0.0325 0.0003 0.0000 + 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 + */ + mio::ContactMatrix contacts_work(static_cast(n_age_groups)); + contacts_work.get_baseline()(age_group_0_to_4.get(), age_group_0_to_4.get()) = 0.0000; + contacts_work.get_baseline()(age_group_0_to_4.get(), age_group_5_to_14.get()) = 0.0000; + contacts_work.get_baseline()(age_group_0_to_4.get(), age_group_15_to_34.get()) = 0.0000; + contacts_work.get_baseline()(age_group_0_to_4.get(), age_group_35_to_59.get()) = 0.0000; + contacts_work.get_baseline()(age_group_0_to_4.get(), age_group_60_to_79.get()) = 0.0000; + contacts_work.get_baseline()(age_group_0_to_4.get(), age_group_80_plus.get()) = 0.0000; + contacts_work.get_baseline()(age_group_5_to_14.get(), age_group_0_to_4.get()) = 0.0000; + contacts_work.get_baseline()(age_group_5_to_14.get(), age_group_5_to_14.get()) = 0.0000; + contacts_work.get_baseline()(age_group_5_to_14.get(), age_group_15_to_34.get()) = 0.0000; + contacts_work.get_baseline()(age_group_5_to_14.get(), age_group_35_to_59.get()) = 0.0000; + contacts_work.get_baseline()(age_group_5_to_14.get(), age_group_60_to_79.get()) = 0.0000; + contacts_work.get_baseline()(age_group_5_to_14.get(), age_group_80_plus.get()) = 0.0000; + contacts_work.get_baseline()(age_group_15_to_34.get(), age_group_0_to_4.get()) = 0.0000; + contacts_work.get_baseline()(age_group_15_to_34.get(), age_group_5_to_14.get()) = 0.0127; + contacts_work.get_baseline()(age_group_15_to_34.get(), age_group_15_to_34.get()) = 1.7570; + contacts_work.get_baseline()(age_group_15_to_34.get(), age_group_35_to_59.get()) = 1.6050; + contacts_work.get_baseline()(age_group_15_to_34.get(), age_group_60_to_79.get()) = 0.0133; + contacts_work.get_baseline()(age_group_15_to_34.get(), age_group_80_plus.get()) = 0.0000; + contacts_work.get_baseline()(age_group_35_to_59.get(), age_group_0_to_4.get()) = 0.0000; + contacts_work.get_baseline()(age_group_35_to_59.get(), age_group_5_to_14.get()) = 0.0020; + contacts_work.get_baseline()(age_group_35_to_59.get(), age_group_15_to_34.get()) = 1.0311; + contacts_work.get_baseline()(age_group_35_to_59.get(), age_group_35_to_59.get()) = 2.3166; + contacts_work.get_baseline()(age_group_35_to_59.get(), age_group_60_to_79.get()) = 0.0098; + contacts_work.get_baseline()(age_group_35_to_59.get(), age_group_80_plus.get()) = 0.0000; + contacts_work.get_baseline()(age_group_60_to_79.get(), age_group_0_to_4.get()) = 0.0000; + contacts_work.get_baseline()(age_group_60_to_79.get(), age_group_5_to_14.get()) = 0.0002; + contacts_work.get_baseline()(age_group_60_to_79.get(), age_group_15_to_34.get()) = 0.0194; + contacts_work.get_baseline()(age_group_60_to_79.get(), age_group_35_to_59.get()) = 0.0325; + contacts_work.get_baseline()(age_group_60_to_79.get(), age_group_60_to_79.get()) = 0.0003; + contacts_work.get_baseline()(age_group_60_to_79.get(), age_group_80_plus.get()) = 0.0000; + contacts_work.get_baseline()(age_group_80_plus.get(), age_group_0_to_4.get()) = 0.0000; + contacts_work.get_baseline()(age_group_80_plus.get(), age_group_5_to_14.get()) = 0.0000; + contacts_work.get_baseline()(age_group_80_plus.get(), age_group_15_to_34.get()) = 0.0000; + contacts_work.get_baseline()(age_group_80_plus.get(), age_group_35_to_59.get()) = 0.0000; + contacts_work.get_baseline()(age_group_80_plus.get(), age_group_60_to_79.get()) = 0.0000; + contacts_work.get_baseline()(age_group_80_plus.get(), age_group_80_plus.get()) = 0.0000; + + /* baseline_other + 0.5170 0.3997 0.7957 0.9958 0.3239 0.0428 + 0.0632 0.9121 0.3254 0.4731 0.2355 0.0148 + 0.0336 0.1604 1.7529 0.8622 0.1440 0.0077 + 0.0204 0.1444 0.5738 1.2127 0.3433 0.0178 + 0.0371 0.0393 0.4171 0.9666 0.7495 0.0257 + 0.0791 0.0800 0.3480 0.5588 0.2769 0.0180 + */ + mio::ContactMatrix contacts_other(static_cast(n_age_groups)); + contacts_other.get_baseline()(age_group_0_to_4.get(), age_group_0_to_4.get()) = 0.5170; + contacts_other.get_baseline()(age_group_0_to_4.get(), age_group_5_to_14.get()) = 0.3997; + contacts_other.get_baseline()(age_group_0_to_4.get(), age_group_15_to_34.get()) = 0.7957; + contacts_other.get_baseline()(age_group_0_to_4.get(), age_group_35_to_59.get()) = 0.9958; + contacts_other.get_baseline()(age_group_0_to_4.get(), age_group_60_to_79.get()) = 0.3239; + contacts_other.get_baseline()(age_group_0_to_4.get(), age_group_80_plus.get()) = 0.0428; + contacts_other.get_baseline()(age_group_5_to_14.get(), age_group_0_to_4.get()) = 0.0632; + contacts_other.get_baseline()(age_group_5_to_14.get(), age_group_5_to_14.get()) = 0.9121; + contacts_other.get_baseline()(age_group_5_to_14.get(), age_group_15_to_34.get()) = 0.3254; + contacts_other.get_baseline()(age_group_5_to_14.get(), age_group_35_to_59.get()) = 0.4731; + contacts_other.get_baseline()(age_group_5_to_14.get(), age_group_60_to_79.get()) = 0.2355; + contacts_other.get_baseline()(age_group_5_to_14.get(), age_group_80_plus.get()) = 0.0148; + contacts_other.get_baseline()(age_group_15_to_34.get(), age_group_0_to_4.get()) = 0.0336; + contacts_other.get_baseline()(age_group_15_to_34.get(), age_group_5_to_14.get()) = 0.1604; + contacts_other.get_baseline()(age_group_15_to_34.get(), age_group_15_to_34.get()) = 1.7529; + contacts_other.get_baseline()(age_group_15_to_34.get(), age_group_35_to_59.get()) = 0.8622; + contacts_other.get_baseline()(age_group_15_to_34.get(), age_group_60_to_79.get()) = 0.1440; + contacts_other.get_baseline()(age_group_15_to_34.get(), age_group_80_plus.get()) = 0.0077; + contacts_other.get_baseline()(age_group_35_to_59.get(), age_group_0_to_4.get()) = 0.0204; + contacts_other.get_baseline()(age_group_35_to_59.get(), age_group_5_to_14.get()) = 0.1444; + contacts_other.get_baseline()(age_group_35_to_59.get(), age_group_15_to_34.get()) = 0.5738; + contacts_other.get_baseline()(age_group_35_to_59.get(), age_group_35_to_59.get()) = 1.2127; + contacts_other.get_baseline()(age_group_35_to_59.get(), age_group_60_to_79.get()) = 0.3433; + contacts_other.get_baseline()(age_group_35_to_59.get(), age_group_80_plus.get()) = 0.0178; + contacts_other.get_baseline()(age_group_60_to_79.get(), age_group_0_to_4.get()) = 0.0371; + contacts_other.get_baseline()(age_group_60_to_79.get(), age_group_5_to_14.get()) = 0.0393; + contacts_other.get_baseline()(age_group_60_to_79.get(), age_group_15_to_34.get()) = 0.4171; + contacts_other.get_baseline()(age_group_60_to_79.get(), age_group_35_to_59.get()) = 0.9666; + contacts_other.get_baseline()(age_group_60_to_79.get(), age_group_60_to_79.get()) = 0.7495; + contacts_other.get_baseline()(age_group_60_to_79.get(), age_group_80_plus.get()) = 0.0257; + contacts_other.get_baseline()(age_group_80_plus.get(), age_group_0_to_4.get()) = 0.0791; + contacts_other.get_baseline()(age_group_80_plus.get(), age_group_5_to_14.get()) = 0.0800; + contacts_other.get_baseline()(age_group_80_plus.get(), age_group_15_to_34.get()) = 0.3480; + contacts_other.get_baseline()(age_group_80_plus.get(), age_group_35_to_59.get()) = 0.5588; + contacts_other.get_baseline()(age_group_80_plus.get(), age_group_60_to_79.get()) = 0.2769; + contacts_other.get_baseline()(age_group_80_plus.get(), age_group_80_plus.get()) = 0.0180; + + mio::ContactMatrix contacts_random(static_cast(n_age_groups)); + + for (auto& loc : model.get_locations()) { + switch (loc.get_type()) { + case mio::abm::LocationType::Home: + loc.get_infection_parameters().get() = contacts_home; + loc.get_infection_parameters().get().get_baseline() *= 1.6; //15 hours + break; + case mio::abm::LocationType::School: + loc.get_infection_parameters().get() = contacts_school; + loc.get_infection_parameters().get().get_baseline() *= 12.0; //2 hours + break; + case mio::abm::LocationType::Work: + loc.get_infection_parameters().get() = contacts_work; + loc.get_infection_parameters().get().get_baseline() *= 8.0; // 3 hours + break; + case mio::abm::LocationType::SocialEvent: + loc.get_infection_parameters().get() = contacts_other; + loc.get_infection_parameters().get().get_baseline() *= 1.2; + loc.get_infection_parameters().get().get_baseline() *= 8.0; // 3 hours + break; + case mio::abm::LocationType::BasicsShop: + loc.get_infection_parameters().get() = contacts_other; + loc.get_infection_parameters().get().get_baseline() *= 0.8; + loc.get_infection_parameters().get().get_baseline() *= 12.0; // 2 hours + break; + default: + loc.get_infection_parameters().get() = contacts_random; + break; + } + } +} + +// map age groups from file to model age groups +mio::AgeGroup get_age_group_from_string(const std::string& age_group_string) +{ + if (age_group_string == "18-20") { + return age_group_15_to_34; + } + if (age_group_string == "20-29") { + return age_group_15_to_34; + } + else if (age_group_string == "30-39") { + // randomly 15-34 or 35-59 + auto rng = mio::RandomNumberGenerator(); + if (mio::UniformDistribution::get_instance()(rng, 0.0, 1.0) < 0.5) { + return age_group_15_to_34; + } + return age_group_35_to_59; + } + else if (age_group_string == "40-49") { + return age_group_35_to_59; + } + else if (age_group_string == "50-59") { + return age_group_35_to_59; + } + else if (age_group_string == "60-69") { + return age_group_60_to_79; + } + else if (age_group_string == "70+") { + // randomly 60-79 or 80+ + auto rng = mio::RandomNumberGenerator(); + if (mio::UniformDistribution::get_instance()(rng, 0.0, 1.0) < 0.5) { + return age_group_60_to_79; + } + return age_group_80_plus; + } + else { + mio::log_error("Invalid age group string."); + return age_group_0_to_4; // default value + } +} + +// convert date string to TimePoint +mio::abm::TimePoint get_time_point_from_string(const std::string& date_string) +{ + // split the string into year, month and day and convert to time_t + std::vector values; + boost::split(values, date_string, boost::is_any_of("-")); + struct tm datetime{}; + datetime.tm_year = std::stoi(values[0]) - 1900; + datetime.tm_mon = std::stoi(values[1]) - 1; + datetime.tm_mday = std::stoi(values[2]); + datetime.tm_isdst = -1; + time_t timestamp = mktime(&datetime); + return mio::abm::TimePoint((int)timestamp); +} + +// add infection and vaccination dates to vectors +void add_infection_dates_and_vaccinations(std::vector>& infections, + std::vector>& vaccinations, + const std::vector& values, + const std::map& index, mio::abm::TimePoint start_date) +{ + std::vector infection_dates; + if (values[index.at("ih_infection_1_date")] != "NA") { + mio::abm::TimePoint infection_date = get_time_point_from_string(values[index.at("ih_infection_1_date")]); + if (infection_date < start_date) { + infection_dates.push_back(infection_date); + } + } + if (values[index.at("ih_infection_2_date")] != "NA") { + mio::abm::TimePoint infection_date = get_time_point_from_string(values[index.at("ih_infection_2_date")]); + if (infection_date < start_date) { + infection_dates.push_back(infection_date); + } + } + if (values[index.at("ih_infection_3_date")] != "NA") { + mio::abm::TimePoint infection_date = get_time_point_from_string(values[index.at("ih_infection_3_date")]); + if (infection_date < start_date) { + infection_dates.push_back(infection_date); + } + } + if (values[index.at("ih_infection_4_date")] != "NA") { + mio::abm::TimePoint infection_date = get_time_point_from_string(values[index.at("ih_infection_4_date")]); + if (infection_date < start_date) { + infection_dates.push_back(infection_date); + } + } + infections.push_back(infection_dates); + + std::vector vaccination_dates; + if (values[index.at("ih_vaccine_1_date")] != "NA") { + mio::abm::TimePoint vaccination_date = get_time_point_from_string(values[index.at("ih_vaccine_1_date")]); + if (vaccination_date < start_date) { + vaccination_dates.push_back(vaccination_date); + } + } + if (values[index.at("ih_vaccine_2_date")] != "NA") { + mio::abm::TimePoint vaccination_date = get_time_point_from_string(values[index.at("ih_vaccine_2_date")]); + if (vaccination_date < start_date) { + vaccination_dates.push_back(vaccination_date); + } + } + if (values[index.at("ih_vaccine_3_date")] != "NA") { + mio::abm::TimePoint vaccination_date = get_time_point_from_string(values[index.at("ih_vaccine_3_date")]); + if (vaccination_date < start_date) { + vaccination_dates.push_back(vaccination_date); + } + } + vaccinations.push_back(vaccination_dates); +} + +void add_infection(mio::abm::Model& model, mio::abm::PersonId pid, const mio::abm::TimePoint infection_date) +{ + mio::abm::PersonalRandomNumberGenerator prng(model.get_rng(), model.get_person(pid)); + model.get_person(pid).add_new_infection(mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, + model.get_person(pid).get_age(), model.parameters, + infection_date), + prng, infection_date, model.parameters); +} + +// add vaccination by date +void add_vaccination(mio::abm::Model& model, mio::abm::PersonId pid, const mio::abm::TimePoint vaccination_date) +{ + model.get_person(pid).add_new_vaccination(mio::abm::ProtectionType::GenericVaccine, vaccination_date); +} + +// add infection by date +void init_infections_and_vaccinations(mio::abm::Model& model, mio::abm::PersonId pid, + const std::vector& infection_dates, + const std::vector& vaccination_dates) +{ + for (const auto& infection_date : infection_dates) { + add_infection(model, pid, infection_date); + } + for (const auto& vaccination_date : vaccination_dates) { + add_vaccination(model, pid, vaccination_date); + } +} + +/** @brief Initializes infections and vaccinations in the model based on the data file. Infections and vaccinations are sampled for each person in the model from the given data, depending on their age group. + * @param[in] model The ABM model in which infections and vaccinations should be initialized. + * @param[in] start_date The start date of the simulation. Infections and vaccinations that happened after this date are not initialized in the model. + */ +void initialize_infections_and_vaccinations(mio::abm::Model& model, const std::string& filename, + const mio::abm::TimePoint start_date) +{ + if (!fs::exists(fs::path(filename))) { + mio::log_error("Cannot read in data. File does not exist."); + } + // File pointer + std::fstream fin; + + // Open an existing file + fin.open(filename, std::ios::in); + std::vector row; + std::vector row_string; + std::string line; + + // Read the Titles from the Data file + std::getline(fin, line); + line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); + std::vector titles; + boost::split(titles, line, boost::is_any_of(",")); + uint32_t count_of_titles = 0; + std::map index = {}; + for (auto const& title : titles) { + index.insert({title, count_of_titles}); + row_string.push_back(title); + count_of_titles++; + } + + mio::CustomIndexArray>, mio::AgeGroup> infections( + mio::AgeGroup(num_age_groups), std::vector>{}); + mio::CustomIndexArray>, mio::AgeGroup> vaccinations( + mio::AgeGroup(num_age_groups), std::vector>{}); + + // Read the data from the file and save sampled infections and vaccinations + while (std::getline(fin, line)) { + line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); + std::vector values; + boost::split(values, line, boost::is_any_of(",")); + mio::AgeGroup age_group = get_age_group_from_string(values[index["age_group"]]); + + add_infection_dates_and_vaccinations(infections[age_group], vaccinations[age_group], values, index, start_date); + } + + // Distribute persons into age groups + mio::CustomIndexArray, mio::AgeGroup> persons_by_age_group( + mio::AgeGroup(num_age_groups), std::vector{}); + + for (auto& person : model.get_persons()) { + persons_by_age_group[person.get_age()].push_back(person.get_id()); + } + + // Randomly sample from the infections and vaccinations for each person in the model + for (auto age_group : mio::AgeGroup(num_age_groups)) { + if (persons_by_age_group[age_group].size() < infections[age_group].size()) { + mio::log_warning("Fewer persons in age group {} than given in the sample file.", age_group.get()); + } + if (infections[age_group].size() == 0) { + continue; // no infections/vaccinations in this age group from data + } + for (mio::abm::PersonId pid : persons_by_age_group[age_group]) { + // randomly select a sample + auto rng = mio::RandomNumberGenerator(); + auto random_sample_index = mio::DiscreteDistribution::get_instance()( + rng, std::vector(infections[age_group].size(), 1.0 / infections[age_group].size())); + init_infections_and_vaccinations(model, pid, infections[age_group][random_sample_index], + vaccinations[age_group][random_sample_index]); + } + } +} + +/// An ABM setup taken from abm_minimal.cpp. +mio::abm::Model make_model(const std::string& infections_vaccinations_file, const mio::abm::TimePoint start_date, + const mio::RandomNumberGenerator& rng) +{ + // Create the model with 6 age groups. + const int model_id = 15502; // Halle county id + auto model = mio::abm::Model(num_age_groups, model_id); + model.get_rng() = rng; + + // Set the age groups that can go to school; here this is AgeGroup(1) (i.e. 5-14) + model.parameters.get() = false; + model.parameters.get()[age_group_5_to_14] = true; + // Set the age groups that can go to work; here these are AgeGroup(2) and AgeGroup(3) (i.e. 15-34 and 35-59) + model.parameters.get().set_multiple({age_group_15_to_34, age_group_35_to_59}, true); + + set_parameters(model.parameters); + // Check if the parameters satisfy their constraints. + model.parameters.check_constraints(); + + //std::vector age_distribution{1000, 1000, 1000, 1000, 1000, 1000}; // artificial age distribution + std::vector age_distribution_male{4788, 10890, 29272, 35381, 21757, 7503}; // Halle age distribution male + std::vector age_distribution_female{4439, 9571, 28188, + 34020, 28597, 12361}; // Halle age distribution female + const size_t total_population = + std::accumulate(age_distribution_male.begin(), age_distribution_male.end(), (size_t)0) + + std::accumulate(age_distribution_female.begin(), age_distribution_female.end(), (size_t)0); + + auto random_household_member = mio::abm::HouseholdMember(num_age_groups); + random_household_member.set_age_weight(age_group_0_to_4, (int)age_distribution_male[age_group_0_to_4.get()] + + (int)age_distribution_female[age_group_0_to_4.get()]); + random_household_member.set_age_weight(age_group_5_to_14, + (int)age_distribution_male[age_group_5_to_14.get()] + + (int)age_distribution_female[age_group_5_to_14.get()]); + random_household_member.set_age_weight(age_group_15_to_34, + (int)age_distribution_male[age_group_15_to_34.get()] + + (int)age_distribution_female[age_group_15_to_34.get()]); + random_household_member.set_age_weight(age_group_35_to_59, + (int)age_distribution_male[age_group_35_to_59.get()] + + (int)age_distribution_female[age_group_35_to_59.get()]); + random_household_member.set_age_weight(age_group_60_to_79, + (int)age_distribution_male[age_group_60_to_79.get()] + + (int)age_distribution_female[age_group_60_to_79.get()]); + random_household_member.set_age_weight(age_group_80_plus, + (int)age_distribution_male[age_group_80_plus.get()] + + (int)age_distribution_female[age_group_80_plus.get()]); + + // Add households with people drawn randomly from the age distribution. + size_t persons_added = 0; + + while (total_population > persons_added) { + auto household = mio::abm::Household(); + // random household size between 1 and 6 (weighted) + auto hh_size = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), + std::vector{40, 20, 20, 10, 5, 5}); + household.add_members(random_household_member, (int)hh_size); + add_household_to_model(model, household); + persons_added += hh_size; + } + + // Add one social event with 5 maximum contacts. + // Maximum contacts limit the number of people that a person can infect while being at this location. + auto event = model.add_location(mio::abm::LocationType::SocialEvent); + model.get_location(event).get_infection_parameters().set(5); + // Add hospital and ICU with 5 maximum contacs. + auto hospital = model.add_location(mio::abm::LocationType::Hospital); + model.get_location(hospital).get_infection_parameters().set(5); + auto icu = model.add_location(mio::abm::LocationType::ICU); + model.get_location(icu).get_infection_parameters().set(5); + // Add one supermarket, maximum constacts are assumed to be 20. + auto shop = model.add_location(mio::abm::LocationType::BasicsShop); + model.get_location(shop).get_infection_parameters().set(20); + // At every school, the maximum contacts are 20. + auto school = model.add_location(mio::abm::LocationType::School); + model.get_location(school).get_infection_parameters().set(20); + // At every workplace, maximum contacts are 20. + auto work = model.add_location(mio::abm::LocationType::Work); + model.get_location(work).get_infection_parameters().set(20); + + // People can get tested at work (and do this with 0.01 probability) from day 0 to day 20. + auto validity_period = mio::abm::days(2); + auto probability = 0.01; + auto start_date_test = start_date; + auto end_date_test = start_date + mio::abm::days(20); + auto test_type = mio::abm::TestType::Antigen; + auto test_parameters = model.parameters.get()[test_type]; + auto testing_criteria_work = mio::abm::TestingCriteria(); + auto testing_scheme_work = mio::abm::TestingScheme(testing_criteria_work, validity_period, start_date_test, + end_date_test, test_parameters, probability); + model.get_testing_strategy().add_scheme(mio::abm::LocationType::Work, testing_scheme_work); + + // People test at home with 0.01 probability. + probability = 0.01; + end_date_test = start_date + mio::abm::days(100); + auto testing_scheme_home = mio::abm::TestingScheme(testing_criteria_work, validity_period, start_date_test, + end_date_test, test_parameters, probability); + model.get_testing_strategy().add_scheme(mio::abm::LocationType::Home, testing_scheme_home); + + // Add infections and vaccinations. + initialize_infections_and_vaccinations(model, infections_vaccinations_file, start_date); + + // OLD: + // Assign infection state to each person. + // The infection states are chosen randomly with the following discrete distribution + /* + std::vector infection_distribution{0.99, 0.002, 0.002, 0.002, 0.002, 0.002, 0.0, 0.0}; + for (auto& person : model.get_persons()) { + mio::abm::InfectionState infection_state = mio::abm::InfectionState( + mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), infection_distribution)); + auto person_rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) { + person.add_new_infection(mio::abm::Infection(person_rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + model.parameters, start_date, infection_state), + person_rng, start_date, model.parameters); + } + } + + // Assign vaccinations to each person. The number of vaccinations is chosen randomly with the following discrete distribution + std::vector vaccination_distribution{0.5, 0.4, 0.1}; + // The date of the last vaccination is randomly chosen within a year before the start date of the simulation and six weeks back in case of two vaccinations. + for (auto& person : model.get_persons()) { + auto person_rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + size_t vaccination_count = + mio::DiscreteDistribution::get_instance()(person_rng, vaccination_distribution); + if (vaccination_count > 0) { + auto last_vaccination_date = + start_date - mio::abm::days(365) + + mio::abm::days(mio::UniformDistribution::get_instance()(person_rng) * 365); + if (vaccination_count > 1) { + auto second_last_vaccination_date = + last_vaccination_date - mio::abm::days(42) + + mio::abm::days(mio::UniformDistribution::get_instance()(person_rng) * 42); + person.add_new_vaccination(mio::abm::ProtectionType::GenericVaccine, second_last_vaccination_date); + } + person.add_new_vaccination(mio::abm::ProtectionType::GenericVaccine, last_vaccination_date); + } + } + */ + + // Assign locations to the people + for (auto& person : model.get_persons()) { + const auto id = person.get_id(); + //assign shop and event + model.assign_location(id, event); + model.assign_location(id, shop); + //assign hospital and ICU + model.assign_location(id, hospital); + model.assign_location(id, icu); + //assign work/school to people depending on their age + if (person.get_age() == age_group_5_to_14) { + model.assign_location(id, school); + } + if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { + model.assign_location(id, work); + } + } + + // During the lockdown, social events are closed for 90% of people. + auto t_lockdown = start_date + mio::abm::days(10); + mio::abm::close_social_events(t_lockdown, 0.9, model.parameters); + + set_local_parameters(model); + + return model; +} + +/** + * @brief Logger to log the recovered persons for each age group. + */ +struct LogRecovered : mio::LogAlways { + using Type = std::pair; + + static Type log(const mio::abm::Simulation<>& sim) + { + Type recovered{}; + Eigen::VectorXd age_group_counts = + Eigen::VectorXd::Zero(Eigen::Index(sim.get_model().parameters.get_num_groups())); + for (auto& person : sim.get_model().get_persons()) { + if (person.get_infection_state(sim.get_time()) == mio::abm::InfectionState::Recovered) { + age_group_counts[person.get_age().get()] += 1; + } + } + recovered.first = sim.get_time(); + recovered.second = age_group_counts; + return recovered; + } +}; + +/** + * @brief Logger to log the number of persons for each age group that had PAIS. + */ +struct LogActivePAIS : mio::LogAlways { + using Type = std::pair; + + static Type log(const mio::abm::Simulation<>& sim) + { + Type active_pais{}; + Eigen::VectorXd age_group_counts = + Eigen::VectorXd::Zero(Eigen::Index(sim.get_model().parameters.get_num_groups())); + for (auto& person : sim.get_model().get_persons()) { + if (person.has_active_pais(sim.get_time())) { + age_group_counts[person.get_age().get()] += 1; + } + } + active_pais.first = sim.get_time(); + active_pais.second = age_group_counts; + return active_pais; + } +}; + +/** + * @brief Logger to log the recovered persons for each age group that were detected. + */ +struct LogRecoveredDetected : mio::LogAlways { + using Type = std::pair; + + static Type log(const mio::abm::Simulation<>& sim) + { + Type recovered_detected{}; + Eigen::VectorXd age_group_counts = + Eigen::VectorXd::Zero(Eigen::Index(sim.get_model().parameters.get_num_groups())); + for (auto& person : sim.get_model().get_persons()) { + if (person.get_infection_state(sim.get_time()) == mio::abm::InfectionState::Recovered && + person.get_infection().is_detected()) { + age_group_counts[person.get_age().get()] += 1; + } + } + recovered_detected.first = sim.get_time(); + recovered_detected.second = age_group_counts; + return recovered_detected; + } +}; + +/** + * @brief Logger to log vaccination counts for each age group for all agents in the simulation. + */ +struct LogVaccinationCounts : mio::LogAlways { + using Type = std::pair; + /** + * @brief Log the vaccination counts for each age group for all agents in the simulation. + * @param[in] sim The simulation of the ABM. + * @return A pair of the TimePoint and the TimeSeries of the vaccination counts for each age group for all agents in the simulation. + * The TimeSeries is a CustomIndexArray with Index mio::AgeGroup and a value of a tuple, where each tuple contains the following information: + * -# The count of people that have 0 vaccinations. + * -# The count of people that have 1 vaccination. + * -# The count of people that have 2 or more vaccinations. + */ + static Type log(const mio::abm::Simulation<>& sim) + { + Type vaccination_counts{}; + Eigen::VectorXd age_group_counts = Eigen::VectorXd::Zero( + Eigen::Index(sim.get_model().parameters.get_num_groups() * + static_cast(mio::abm::VaccinationClass::Count))); // 3 values per age group + + for (auto& person : sim.get_model().get_persons()) { + auto age_group = person.get_age().get(); + auto n_vaccinations = + static_cast(mio::abm::get_vaccination_class(person.get_vaccinations().size())); + auto index = age_group + n_vaccinations * sim.get_model().parameters.get_num_groups(); + age_group_counts[index] += 1; + } + vaccination_counts.first = sim.get_time(); + vaccination_counts.second = age_group_counts; + return vaccination_counts; + } +}; + +/** + * @brief Main function to run the ABM simulation. + */ +int main() +{ + // mio::mpi::init(); + + // mio::set_log_level(mio::LogLevel::warn); + + // Set start and end time for the simulation. + auto t0 = get_time_point_from_string("2022-01-01"); // Start date is 2022-01-01 00:00:00.000 + + auto tmax = t0 + mio::abm::days(20); + + const std::string infections_vaccinations_file = + mio::path_join(mio::base_dir(), "AIMS_Halle_Data/260304_infections_vaccines_halle.csv"); + auto sim = + mio::abm::Simulation(t0, std::move(make_model(infections_vaccinations_file, t0, mio::thread_local_rng()))); + + // Create a history object to store the time series of the number of recovered (detected) persons and the number of vaccinations for each age group. + mio::History historyRecoveredDetected{ + Eigen::Index(sim.get_model().parameters.get_num_groups())}; + mio::History historyRecovered{ + Eigen::Index(sim.get_model().parameters.get_num_groups())}; + mio::History historyVaccinations{ + Eigen::Index(sim.get_model().parameters.get_num_groups() * 3)}; + mio::History historyActivePAIS{ + Eigen::Index(sim.get_model().parameters.get_num_groups())}; + + // Run the simulation until tmax with the history object. + sim.advance(tmax, historyRecovered, historyVaccinations, historyActivePAIS); + + const std::string result_dir = mio::path_join(mio::base_dir(), "AIMS_Halle_Example_Results"); + if (!mio::create_directory(result_dir)) { + mio::log_error("Could not create result directory \"{}\".", result_dir); + return 1; + } + + // The amount of recovered detected persons are written into the file "recovered.txt" as a table with 7 columns. + // The first column is Time. The other columns correspond to the amount of people within each AgeGroup at this Time. + std::ofstream outfile(mio::path_join(result_dir, "recovered.txt")); + std::get<0>(historyRecovered.get_log()).print_table(outfile, {}, 16, 1, ','); + std::cout << "Results written to recovered.txt" << std::endl; + + // The amount of vaccinated persons are written into the file "vaccinations.txt" as a table with 19 columns. + // The first column is Time. The other columns correspond to the number of people within each AgeGroup and with the number of Vaccinations (0/1/2) at this Time. + std::ofstream outfile2(mio::path_join(result_dir, "vaccinations.txt")); + std::get<0>(historyVaccinations.get_log()).print_table(outfile2, {}, 16, 1, ','); + std::cout << "Results written to vaccinations.txt" << std::endl; + + // The amount of active PAIS persons are written into the file "active_pais.txt" as a table with 7 columns. + std::ofstream outfile3(mio::path_join(result_dir, "active_pais.txt")); + std::get<0>(historyActivePAIS.get_log()).print_table(outfile3, {}, 16, 1, ','); + std::cout << "Results written to active_pais.txt" << std::endl; + + // mio::mpi::finalize(); + + return 0; +} diff --git a/cpp/examples/abm_aims_visual.py b/cpp/examples/abm_aims_visual.py new file mode 100644 index 0000000000..1078d85c81 --- /dev/null +++ b/cpp/examples/abm_aims_visual.py @@ -0,0 +1,13 @@ +import matplotlib.pyplot as plt +import pandas as pd + +df = pd.read_csv("active_pais.txt", index_col=0, parse_dates=True) + +plt.figure(figsize=(10, 6)) +for column in df.columns: + plt.plot(df.index, df[column], label=column) +plt.legend(["0-4", "5-14", "15-34", "35-59", "60-79", "80+"], title="Age Groups") +plt.title("Active PAIS Over Time by Age Group") +plt.xlabel("Time in Days since 01.01.1970") +plt.ylabel("Number of Individuals") +plt.show() \ No newline at end of file diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 4e1c3bf3e2..787f044204 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -151,7 +151,8 @@ int main() (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); if (infection_state != mio::abm::InfectionState::Susceptible) person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model.parameters, start_date, infection_state)); + model.parameters, start_date, infection_state), + rng, start_date, model.parameters); } // Assign locations to the people diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index d5fbad0413..0d6c43451a 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -124,7 +124,8 @@ int main() auto rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model.parameters, start_date, infection_state)); + model.parameters, start_date, infection_state), + rng, start_date, model.parameters); } } diff --git a/cpp/examples/abm_parameter_study.cpp b/cpp/examples/abm_parameter_study.cpp index 313663ef20..97514ac90b 100644 --- a/cpp/examples/abm_parameter_study.cpp +++ b/cpp/examples/abm_parameter_study.cpp @@ -135,7 +135,8 @@ mio::abm::Model make_model(const mio::RandomNumberGenerator& rng) auto person_rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(person_rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model.parameters, start_date, infection_state)); + model.parameters, start_date, infection_state), + person_rng, start_date, model.parameters); } } diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 9eb1caf65a..be45c46119 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -204,7 +204,8 @@ int main() auto rng = mio::abm::PersonalRandomNumberGenerator(model1.get_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model1.parameters, start_date, infection_state)); + model1.parameters, start_date, infection_state), + rng, start_date, model1.parameters); } person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_m1, model1.get_id()); person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_m1, model1.get_id()); @@ -234,7 +235,8 @@ int main() auto rng = mio::abm::PersonalRandomNumberGenerator(model2.get_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model2.parameters, start_date, infection_state)); + model2.parameters, start_date, infection_state), + rng, start_date, model2.parameters); } person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_m2, model2.get_id()); person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_m2, model2.get_id()); diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index ae04b68b2d..a1992e72d5 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -35,6 +35,9 @@ add_library(abm mask.h mask.cpp common_abm_loggers.h + sex.h + pais.h + pais.cpp ) target_link_libraries(abm PUBLIC memilio) target_include_directories(abm PUBLIC diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 0d91c69c24..570ec55cb3 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -178,7 +178,6 @@ struct LogInfectionState : mio::LogAlways { Eigen::VectorX sum = Eigen::VectorX::Zero(Eigen::Index(mio::abm::InfectionState::Count)); auto curr_time = sim.get_time(); - PRAGMA_OMP(for) for (auto& location : sim.get_model().get_locations()) { for (uint32_t inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { sum[inf_state] += sim.get_model().get_subpopulation(location.get_id(), curr_time, @@ -189,6 +188,30 @@ struct LogInfectionState : mio::LogAlways { } }; +/** + * @brief Looger to log the TimeSeries of the number of Person%s that have an active PAIS. + */ +struct LogPAIS : mio::LogAlways { + using Type = std::pair; + /** + * @brief Log the TimeSeries of the number of Person%s that have an active PAIS. + * @param[in] sim The simulation of the abm. + * @return A pair of the TimePoint and the TimeSeries of the number of Person%s that have an active PAIS. + */ + static Type log(const mio::abm::Simulation<>& sim) + { + ScalarType sum = 0; + auto curr_time = sim.get_time(); + for (auto& person : sim.get_model().get_persons()) { + auto person_id = person.get_id(); + if (sim.get_model().get_person(person_id).has_active_pais(curr_time)) { + sum++; + } + } + return std::make_pair(curr_time, sum); + } +}; + /** * @brief This is like the DataWriterToMemory, but it only logs time series data. * @tparam Loggers The loggers that are used to log data. The loggers must return a touple with a TimePoint and a value. diff --git a/cpp/models/abm/infection.cpp b/cpp/models/abm/infection.cpp index 0cd8e869f1..d9b4fa9d62 100644 --- a/cpp/models/abm/infection.cpp +++ b/cpp/models/abm/infection.cpp @@ -144,6 +144,26 @@ InfectionState Infection::get_infection_state(TimePoint t) const return std::prev(it)->second; } +std::pair Infection::get_highest_infection_state() const +{ + if (m_infection_course.back().second == InfectionState::Dead) { + return m_infection_course.back(); + } + else { + return m_infection_course[m_infection_course.size() - 2]; + } +} + +TimePoint Infection::get_infection_state_start_date(InfectionState state) const +{ + for (const auto& [time_point, inf_state] : m_infection_course) { + if (inf_state == state) { + return time_point; + } + } + return TimePoint(-1); // invalid TimePoint +} + void Infection::set_detected() { m_detected = true; diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index bf25631094..b6d9e73f71 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -144,6 +144,22 @@ class Infection */ InfectionState get_infection_state(TimePoint t) const; + /** + * @brief Get the highest #InfectionState of the Infection and the TimePoint when that state is reached. + * The highest #InfectionState is the state with the most severe symptoms that is reached during the Infection. + * For example, if a Person goes through the states InfectedNoSymptoms -> InfectedSymptoms -> Recovered, the highest #InfectionState is InfectedSymptoms. + * If a Person goes through the states InfectedNoSymptoms -> InfectedSymptoms -> InfectedSevere -> Recovered, the highest #InfectionState is InfectedSevere. + * @return A pair of the highest #InfectionState and the TimePoint when that state is reached. + */ + std::pair get_highest_infection_state() const; + + /** + * @brief Get the start date of a specific #InfectionState. + * @param[in] state #InfectionState for which the start date is queried. + * @return The start date of the given #InfectionState. If the Person does not reach that state during the Infection, an invalid TimePoint is returned. + */ + TimePoint get_infection_state_start_date(InfectionState state) const; + /** * @brief Set the Infection to detected. */ diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index a886edde98..bdb10f5cbe 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -52,10 +52,10 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) return id; } -PersonId Model::add_person(const LocationId id, AgeGroup age) +PersonId Model::add_person(const LocationId id, AgeGroup age, Sex sex) { PersonId person_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, person_id)); + return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, sex, person_id)); } PersonId Model::add_person(Person&& person) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index ee9077932f..5f90df009d 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -20,6 +20,7 @@ #ifndef MIO_ABM_MODEL_H #define MIO_ABM_MODEL_H +#include "abm/sex.h" #include "abm/infection_state.h" #include "abm/model_functions.h" #include "abm/location_type.h" @@ -203,9 +204,10 @@ class Model * @brief Add a Person to the Model. * @param[in] id The LocationID of the initial Location of the Person. * @param[in] age AgeGroup of the person. + * @param[in] sex Sex of the person. * @return Id of the newly created Person. */ - PersonId add_person(const LocationId id, AgeGroup age); + PersonId add_person(const LocationId id, AgeGroup age, Sex sex = Sex::Male); /** * @brief Adds a copy of a given Person to the Model. diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index 7529590521..d024e909d6 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -100,10 +100,10 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const random_transition(personal_rng, VirusVariant::Count, dt, local_indiv_expected_trans); // use VirusVariant::Count for no virus submission if (virus != VirusVariant::Count) { - person.add_new_infection(Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2, - mio::abm::InfectionState::Exposed, - person.get_latest_protection(t + dt / 2), - false)); // Starting time in second order approximation + person.add_new_infection( + Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2, + mio::abm::InfectionState::Exposed, person.get_latest_protection(t + dt / 2), false), + personal_rng, t + dt / 2, global_parameters); // Starting time in second order approximation } } } diff --git a/cpp/models/abm/pais.cpp b/cpp/models/abm/pais.cpp new file mode 100644 index 0000000000..655548a158 --- /dev/null +++ b/cpp/models/abm/pais.cpp @@ -0,0 +1,115 @@ +/* +* Copyright (C) 2020-2026 MEmilio +* +* Authors: David Kerkmann +* +* 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 "abm/pais.h" +#include "abm/person.h" +#include "abm/random_events.h" + +namespace mio +{ +namespace abm +{ + +void PAIS::update_severity(const Parameters& params, PersonalRandomNumberGenerator& rng, TimePoint t, TimeSpan dt) +{ + if (severity.empty() || t > severity.back().first) { + return; // only update if the last update was before t + } + std::pair transmission_probs[static_cast(PAISState::Count)]; + + for (auto&& v : enum_members()) { + transmission_probs[static_cast(v)] = { + v, params.get()(static_cast(severity.back().second), + static_cast(v))}; + } + auto severity_new = random_transition(rng, severity.back().second, dt, transmission_probs); + if (severity_new != severity.back().second) { + this->severity.push_back({t, severity_new}); // only update if there is a change in severity + } +} + +void PAIS::init_or_refresh(const Parameters& params, Person& p, PersonalRandomNumberGenerator& rng, + const Infection& inf, TimePoint t) +{ + // get highest InfectionState of the new infection + auto highest_state = inf.get_highest_infection_state(); + if (highest_state.second != InfectionState::Dead) { + // if the Person already had an active PAIS and gets a reinfection, refresh the PAIS status + if (get_severity(t) != PAISState::Count) { + add_new_severity(t, highest_state); + } + else { + // base probability of developing PAIS based on age, sex, virus variant and number of vaccinations + ScalarType pais_prob = params.get()[{inf.get_virus_variant(), p.get_age(), p.get_sex(), + get_vaccination_class(p.get_vaccinations().size())}]; + // increase probability of developing PAIS if the Person had a severe acute infection or worse + if (highest_state.second == InfectionState::InfectedSevere || + highest_state.second == InfectionState::InfectedCritical) { + pais_prob *= params.get()[{ + inf.get_virus_variant(), get_vaccination_class(p.get_vaccinations().size())}]; + } + // reduce probability of developing PAIS if the Person has not had PAIS after an earlier infection + if (!p.get_infections().empty() && get_severity(t) == PAISState::Count) { + pais_prob *= params.get()[{ + inf.get_virus_variant(), get_vaccination_class(p.get_vaccinations().size())}]; + } + + auto& uniform_dist = UniformDistribution::get_instance(); + if (uniform_dist(rng) < pais_prob) { + TimePoint time_recovered = inf.get_infection_state_start_date(InfectionState::Recovered); + add_new_severity(time_recovered, highest_state); + } + } + } +} + +void PAIS::add_new_severity(TimePoint t, std::pair highest_state) +{ + PAISState severity_new; + if (highest_state.second == InfectionState::InfectedSevere || + highest_state.second == InfectionState::InfectedCritical) { + severity_new = PAISState::Severe; + } + else { + severity_new = PAISState::Medium; + } + if (severity.empty() || (t > severity.back().first && severity_new != severity.back().second)) { + this->severity.push_back({t, severity_new}); + } +} + +PAISState PAIS::get_severity(TimePoint t) const +{ + if (severity.empty()) { + return PAISState::Count; + } + if (t < severity[0].first) { + return PAISState::Count; + } + + auto it = std::upper_bound(severity.begin(), severity.end(), t, + [](const TimePoint& s, const std::pair& state) { + return state.first > s; + }); + return std::prev(it)->second; +} + +} // namespace abm +} // namespace mio diff --git a/cpp/models/abm/pais.h b/cpp/models/abm/pais.h new file mode 100644 index 0000000000..d39ceaa8ce --- /dev/null +++ b/cpp/models/abm/pais.h @@ -0,0 +1,90 @@ +/* +* Copyright (C) 2020-2026 MEmilio +* +* Authors: David Kerkmann +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef MIO_ABM_PAIS_H +#define MIO_ABM_PAIS_H + +#include "abm/pais_state.h" +#include "memilio/io/default_serialize.h" +#include "abm/time.h" +#include "abm/personal_rng.h" +#include "abm/infection.h" +#include "abm/parameters.h" + +#include + +namespace mio +{ +namespace abm +{ + +class Person; // forward declaration to avoid circular dependency between Person and PAIS + +/** + * @brief Represents a PAIS (Post-Acute Infection Syndrome). + */ +struct PAIS { + std::vector> severity{}; ///< Time series of the severity of the PAIS. + + /** + * @brief Update the severity of the PAIS. + * @param[in] params The Parameters of the Simulation. + * @param[in] rng Personal RandomNumberGenerator. + * @param[in] t TimePoint of querry. Usually the current time of the Simulation. + * @param[in] dt The time step size of the Simulation. + */ + void update_severity(const Parameters& params, PersonalRandomNumberGenerator& rng, TimePoint t, TimeSpan dt); + + /** + * @brief Initialize or refresh the PAIS status based on a new infection. + * @param[in] params The Parameters of the Simulation. + * @param[in] p The Person with the new Infection. + * @param[in] rng Personal RandomNumberGenerator. + * @param[in] inf The new Infection. + * @param[in] t TimePoint of querry. Usually the current time of the Simulation. + */ + void init_or_refresh(const Parameters& params, Person& p, PersonalRandomNumberGenerator& rng, const Infection& inf, + TimePoint t); + + /** + * @brief Add a new severity state to the PAIS based on the highest InfectionState of the infection. + * If the highest InfectionState is InfectedSevere or InfectedCritical, the new severity state is Severe, otherwise it is Medium. + * The new severity state is only added if it is different from the current severity state and if the last update was before t. + * @param[in] t TimePoint of querry. Usually the current time of the Simulation. + * @param[in] highest_state The highest InfectionState of the infection. + */ + void add_new_severity(TimePoint t, std::pair highest_state); + + /** + * @brief Get the severity of the PAIS at a given time. + * @param[in] t TimePoint of querry. Usually the current time of the Simulation. + * @return The severity of the PAIS at time t. + */ + PAISState get_severity(TimePoint t) const; + + auto default_serialize() + { + return Members("PAIS").add("severity", severity); + } +}; + +} // namespace abm +} // namespace mio + +#endif diff --git a/cpp/models/abm/pais_state.h b/cpp/models/abm/pais_state.h new file mode 100644 index 0000000000..febaed7626 --- /dev/null +++ b/cpp/models/abm/pais_state.h @@ -0,0 +1,46 @@ +/* +* Copyright (C) 2020-2026 MEmilio +* +* Authors: David Kerkmann +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef MIO_ABM_PAIS_STATE_H +#define MIO_ABM_PAIS_STATE_H + +#include + +namespace mio +{ +namespace abm +{ + +/** + * PAIS states in ABM. + * can be used as 0-based index +*/ +enum class PAISState : std::uint32_t +{ + Healthy, + Medium, + Severe, + + Count // last!! +}; + +} // namespace abm +} // namespace mio + +#endif diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index 1933d36722..8a19c6b72d 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -22,9 +22,10 @@ #include "abm/mask_type.h" #include "abm/time.h" +#include "abm/sex.h" #include "abm/infection_state.h" #include "abm/virus_variant.h" -#include "abm/protection_event.h" +#include "abm/pais_state.h" #include "abm/protection_event.h" #include "abm/test_type.h" #include "memilio/config.h" @@ -459,6 +460,71 @@ struct HighViralLoadProtectionFactor { } }; +/** + * @brief Personal probability to obtain post-acute infection symptoms (PAIS), which depends on #ProtectionType, + * #AgeGroup, #Sex and #VirusVariant and the amount of previous vaccinations. Its value is between 0 and 1. + * The role of previous infections and the strength of the acute infection are considered separately. + */ +struct PAISProbability { + using Type = CustomIndexArray; + static Type get_default(AgeGroup size) + { + return Type({VirusVariant::Count, size, Sex::Count, VaccinationClass::Count}, 0.0); + } + static std::string name() + { + return "PAISProbability"; + } +}; + +/** + * @brief Multiplicative factor to determine the probability of developing post-acute infection symptoms (PAIS) based on the severity of the acute infection. + */ +struct PAISProbabilitySeverityFactor { + using Type = CustomIndexArray; + static Type get_default(AgeGroup /*size*/) + { + return Type({VirusVariant::Count, VaccinationClass::Count}, 1.0); + } + static std::string name() + { + return "PAISProbabilitySeverityFactor"; + } +}; + +/** + * @brief Multiplicative factor to determine the probability of developing post-acute infection symptoms (PAIS) if the Person has not had PAIS after the first infection. + */ +struct PAISProtectionAtSecondInfection { + using Type = CustomIndexArray; + static Type get_default(AgeGroup /*size*/) + { + return Type({VirusVariant::Count, VaccinationClass::Count}, 1.0); + } + static std::string name() + { + return "PAISProtectionAtSecondInfection"; + } +}; + +/** + * @brief Transition Matrix between PAIS states. + * Each value give the probability that a Person with a certain PAISState transitions to another PAISState within a day. + * The first index is the from state and the second index is the to state. + */ +struct PAISTransitionMatrix { + using Type = Eigen::MatrixXd; + static Type get_default(AgeGroup /*size*/) + { + return Eigen::MatrixXd::Identity(static_cast(PAISState::Count), + static_cast(PAISState::Count)); + } + static std::string name() + { + return "PAISTransitionMatrix"; + } +}; + /** * @brief Parameters that describe the reliability of a test. */ @@ -706,7 +772,8 @@ using ParametersBase = AerosolTransmissionRates, LockdownDate, QuarantineDuration, QuarantineEffectiveness, SocialEventRate, BasicShoppingRate, WorkRatio, SchoolRatio, GotoWorkTimeMinimum, GotoWorkTimeMaximum, GotoSchoolTimeMinimum, GotoSchoolTimeMaximum, AgeGroupGotoSchool, AgeGroupGotoWork, - InfectionProtectionFactor, SeverityProtectionFactor, HighViralLoadProtectionFactor, TestData>; + InfectionProtectionFactor, SeverityProtectionFactor, HighViralLoadProtectionFactor, PAISProbability, + PAISProbabilitySeverityFactor, PAISProtectionAtSecondInfection, PAISTransitionMatrix, TestData>; /** * @brief Maximum number of Person%s an infectious Person can infect at the respective Location. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 19a66aec99..8a50d46d4e 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -34,13 +34,14 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id) + int location_model_id, AgeGroup age, Sex sex, PersonId person_id) : m_location(location_id) , m_location_type(location_type) , m_location_model_id(location_model_id) , m_assigned_locations((uint32_t)LocationType::Count, LocationId::invalid_id()) , m_home_isolation_start(TimePoint(-(std::numeric_limits::max() / 2))) , m_age(age) + , m_sex(sex) , m_time_at_location(0) , m_mask(Mask(MaskType::None, TimePoint(-(std::numeric_limits::max() / 2)))) , m_compliance((uint32_t)InterventionType::Count, 1.) @@ -75,6 +76,14 @@ bool Person::is_infected(TimePoint t) const return true; } +bool Person::has_active_pais(TimePoint t) const +{ + if (m_pais.get_severity(t) == PAISState::Healthy || m_pais.get_severity(t) == PAISState::Count) { + return false; + } + return true; +} + InfectionState Person::get_infection_state(TimePoint t) const { if (m_infections.empty()) { @@ -85,8 +94,10 @@ InfectionState Person::get_infection_state(TimePoint t) const } } -void Person::add_new_infection(Infection&& inf) +void Person::add_new_infection(Infection&& inf, PersonalRandomNumberGenerator& rng, TimePoint t, + const Parameters& params) { + m_pais.init_or_refresh(params, *this, rng, inf, t); // initialize or refresh PAIS status based on the new infection m_infections.push_back(std::move(inf)); } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index e1cea60230..a3cc8c40f8 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -20,8 +20,10 @@ #ifndef MIO_ABM_PERSON_H #define MIO_ABM_PERSON_H +#include "abm/sex.h" #include "abm/infection.h" #include "abm/infection_state.h" +#include "abm/pais.h" #include "abm/location_id.h" #include "abm/location_type.h" #include "abm/parameters.h" @@ -58,7 +60,8 @@ class Person * */ explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id = PersonId::invalid_ID()); + int location_model_id, AgeGroup age, Sex sex = Sex::Male, + PersonId person_id = PersonId::invalid_ID()); explicit Person(const Person& other, PersonId person_id); @@ -77,6 +80,22 @@ class Person Infection& get_infection(); const Infection& get_infection() const; + /** + * @brief Get all infections of the Person. + * @return A vector with all infections the Person had. + * @{ + */ + std::vector& get_infections() + { + return m_infections; + } + + const std::vector& get_infections() const + { + return m_infections; + } + /** @} */ + /** * @brief Get all vaccinations of the Person. * @return A vector with all vaccinations. @@ -100,6 +119,13 @@ class Person */ bool is_infected(TimePoint t) const; + /** + * @brief Returns if the Person has an active PAIS at the TimePoint. + * @param[in] t TimePoint of querry. Usually the current time of the Simulation. + * @return True if the Person has an active PAIS at the TimePoint. + */ + bool has_active_pais(TimePoint t) const; + /** * @brief Get the InfectionState of the Person at a specific TimePoint. * @param[in] t TimePoint of querry. Usually the current time of the Simulation. @@ -110,8 +136,11 @@ class Person /** * @brief Adds a new Infection to the list of Infection%s. * @param[in] inf The new Infection. + * @param[in] rng PersonalRandomNumberGenerator. + * @param[in] t TimePoint of querry. Usually the current time of the Simulation. + * @param[in] params The Parameters of the Simulation. */ - void add_new_infection(Infection&& inf); + void add_new_infection(Infection&& inf, PersonalRandomNumberGenerator& rng, TimePoint t, const Parameters& params); /** * @brief Get the AgeGroup of this Person. @@ -122,6 +151,15 @@ class Person return m_age; } + /** + * @brief Get the Sex of this Person. + * @return Sex of the Person + */ + Sex get_sex() const + { + return m_sex; + } + /** * @brief Get the current Location of the Person. * @return Current Location of the Person. @@ -351,6 +389,13 @@ class Person */ void set_mask(MaskType type, TimePoint t); + /** + * @brief Get the antibody level of the Person at a given time. + * @param[in] t TimePoint of check. + * @returns Antibody level of the Person at the given TimePoint. + */ + ScalarType get_antibody_level(TimePoint t) const; + /** * @brief Get the multiplicative factor on how likely an #Infection is due to the immune system. * @param[in] t TimePoint of check. @@ -414,6 +459,7 @@ class Person .add("infections", m_infections) .add("home_isolation_start", m_home_isolation_start) .add("age_group", m_age) + .add("sex", m_sex) .add("time_at_location", m_time_at_location) .add("rnd_workgroup", m_random_workgroup) .add("rnd_schoolgroup", m_random_schoolgroup) @@ -452,8 +498,10 @@ class Person Person always visits the same Home or School etc. */ std::vector m_vaccinations; ///< Vector with all vaccinations the Person has received. std::vector m_infections; ///< Vector with all Infection%s the Person had. + PAIS m_pais; ///< The PAIS of the Person (PAISState::Count if no PAIS). TimePoint m_home_isolation_start; ///< TimePoint when the Person started isolation at home. AgeGroup m_age; ///< AgeGroup the Person belongs to. + Sex m_sex; ///< Sex of the Person. TimeSpan m_time_at_location; ///< Time the Person has spent at its current Location so far. ScalarType m_random_workgroup; ///< Value to determine if the Person goes to work or works from home during lockdown. @@ -481,7 +529,7 @@ struct DefaultFactory { static abm::Person create() { return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::LocationId(), 0, AgeGroup(0), - abm::PersonId()); + abm::Sex::Male, abm::PersonId()); } }; diff --git a/cpp/models/abm/protection_event.h b/cpp/models/abm/protection_event.h index 809f920ccd..24a4545ce7 100644 --- a/cpp/models/abm/protection_event.h +++ b/cpp/models/abm/protection_event.h @@ -30,6 +30,35 @@ namespace mio namespace abm { +/** + * @brief #Vaccination classes + * can be used as 0-based index + */ +enum class VaccinationClass : std::uint32_t +{ + Zero, + OneOrTwo, + ThreeOrMore, + Count //last!! +}; + +/** + * @brief Determine VaccinationClass based on the number of vaccinations. + * @param num_vaccinations Number of vaccinations received. + */ +inline VaccinationClass get_vaccination_class(size_t num_vaccinations) +{ + if (num_vaccinations == 0) { + return VaccinationClass::Zero; + } + else if (num_vaccinations <= 2) { + return VaccinationClass::OneOrTwo; + } + else { + return VaccinationClass::ThreeOrMore; + } +} + /** * @brief #ProtectionType in ABM. * can be used as 0-based index diff --git a/cpp/models/abm/sex.h b/cpp/models/abm/sex.h new file mode 100644 index 0000000000..8255f339ee --- /dev/null +++ b/cpp/models/abm/sex.h @@ -0,0 +1,44 @@ +/* +* Copyright (C) 2020-2026 MEmilio +* +* Authors: David Kerkmann +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef SEX_H +#define SEX_H + +#include + +namespace mio +{ +namespace abm +{ + +/** + * @brief The sex of a Person in the ABM. + * can be used as 0-based index +*/ +enum class Sex : std::uint32_t +{ + Male, + Female, + Count // last!! +}; + +} // namespace abm +} // namespace mio + +#endif diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index bf9af22c29..5e6341af16 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -23,23 +23,24 @@ #include "memilio/utils/random_number_generator.h" mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age, - mio::abm::InfectionState infection_state, mio::abm::TimePoint t, + mio::abm::Sex sex, mio::abm::InfectionState infection_state, mio::abm::TimePoint t, mio::abm::Parameters params, mio::abm::PersonId id) { assert(age.get() < params.get_num_groups()); - mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, id); + mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, sex, id); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( - mio::abm::Infection(rng_p, static_cast(0), age, params, t, infection_state)); + mio::abm::Infection(rng_p, static_cast(0), age, params, t, infection_state), rng_p, + t, params); } return p; } mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, - mio::abm::InfectionState infection_state, mio::abm::TimePoint t) + mio::abm::Sex sex, mio::abm::InfectionState infection_state, mio::abm::TimePoint t) { - return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, + return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, sex, infection_state, t, model.parameters, static_cast(model.get_persons().size()))); } diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 808e8dc173..476bd7d9eb 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -94,7 +94,7 @@ struct ScopedMockDistribution { * @brief Create a Person without a Model object. Intended for simple use in tests. */ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, - mio::AgeGroup age = age_group_15_to_34, + mio::AgeGroup age = age_group_15_to_34, mio::abm::Sex sex = mio::abm::Sex::Male, mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0), mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), @@ -104,7 +104,7 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc * @brief Add a Person to the Model. Intended for simple use in tests. */ mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, - mio::AgeGroup age = age_group_15_to_34, + mio::AgeGroup age = age_group_15_to_34, mio::abm::Sex sex = mio::abm::Sex::Male, mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0)); diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index fe7926ff06..19533f1761 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -438,13 +438,16 @@ TEST_F(TestModel, evolveMobilityTrips) auto rng_p1 = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), p1); p1.add_new_infection(mio::abm::Infection(rng_p1, static_cast(0), p1.get_age(), - model.parameters, t, mio::abm::InfectionState::InfectedNoSymptoms)); + model.parameters, t, mio::abm::InfectionState::InfectedNoSymptoms), + rng_p1, t, model.parameters); auto rng_p3 = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), p3); p3.add_new_infection(mio::abm::Infection(rng_p3, static_cast(0), p3.get_age(), - model.parameters, t, mio::abm::InfectionState::InfectedSevere)); + model.parameters, t, mio::abm::InfectionState::InfectedSevere), + rng_p3, t, model.parameters); auto rng_p4 = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), p4); p4.add_new_infection(mio::abm::Infection(rng_p4, static_cast(0), p4.get_age(), - model.parameters, t, mio::abm::InfectionState::Recovered)); + model.parameters, t, mio::abm::InfectionState::Recovered), + rng_p4, t, model.parameters); // For any other uniform distribution calls in model.evolve EXPECT_CALL(mock_uniform_dist2.get_mock(), invoke).WillRepeatedly(Return(1.)); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 9bc2cabc40..167acf7885 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -350,7 +350,8 @@ TEST_F(TestPerson, getLatestProtection) t = mio::abm::TimePoint(40 * 24 * 60 * 60); person.add_new_infection(mio::abm::Infection(prng, static_cast(0), age_group_15_to_34, - params, t, mio::abm::InfectionState::Exposed)); + params, t, mio::abm::InfectionState::Exposed), + prng, t, params); latest_protection = person.get_latest_protection(t); // Verify that the latest protection is a natural infection. EXPECT_EQ(latest_protection.type, mio::abm::ProtectionType::NaturalInfection); diff --git a/docs/source/cpp/graph_abm.rst b/docs/source/cpp/graph_abm.rst index fe277d346e..916a538113 100644 --- a/docs/source/cpp/graph_abm.rst +++ b/docs/source/cpp/graph_abm.rst @@ -100,10 +100,10 @@ Assigning infection states and locations to persons in all models can be done vi //Add infection to persons in home1 auto rng_child1 = mio::abm::PersonalRandomNumberGenerator(child1); child1.add_new_infection(mio::abm::Infection(rng_child1, mio::abm::VirusVariant::Wildtype, child1.get_age(), - model1.parameters, start_date, mio::abm::InfectionState::InfectedNoSymptoms)); + model1.parameters, start_date, mio::abm::InfectionState::InfectedNoSymptoms), rng_child1, start_date, model1.parameters); auto rng_adult1 = mio::abm::PersonalRandomNumberGenerator(adult1); adult1.add_new_infection(mio::abm::Infection(rng_adult1, mio::abm::VirusVariant::Wildtype, adult1.get_age(), - model1.parameters, start_date, mio::abm::InfectionState::Exposed)); + model1.parameters, start_date, mio::abm::InfectionState::Exposed), rng_adult1, start_date, model1.parameters); //Assign Event, Shop, Hospital and ICU to all persons, school only to the child and work to the adults //Event diff --git a/docs/source/cpp/mobility_based_abm.rst b/docs/source/cpp/mobility_based_abm.rst index a854494fcb..525d418679 100644 --- a/docs/source/cpp/mobility_based_abm.rst +++ b/docs/source/cpp/mobility_based_abm.rst @@ -307,7 +307,7 @@ For infections to happen during the simulation, we have to initialize people wit if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model.parameters, start_date, infection_state)); + model.parameters, start_date, infection_state), rng, start_date, model.parameters); } }