From f72f233ed27a233398a8070de9c8421a078c63ed Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 12:34:50 +0200 Subject: [PATCH 01/10] delete examples and add some things to the other example --- cpp/examples/abm_history_object.cpp | 37 +- cpp/examples/abm_maximal.cpp | 0 cpp/simulations/abm.cpp | 630 ---------------- cpp/simulations/abm_braunschweig.cpp | 1044 -------------------------- 4 files changed, 27 insertions(+), 1684 deletions(-) create mode 100644 cpp/examples/abm_maximal.cpp delete mode 100644 cpp/simulations/abm.cpp delete mode 100644 cpp/simulations/abm_braunschweig.cpp diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 70e8939f60..f228e56604 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -139,16 +139,33 @@ int main() test_parameters, probability); model.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::Work, testing_scheme_work); - // Assign infection state to each person. - // The infection states are chosen randomly. - auto persons = model.get_persons(); - for (auto& person : persons) { - auto rng = mio::abm::PersonalRandomNumberGenerator(person); - mio::abm::InfectionState infection_state = - (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)); + for (auto& person : model.get_persons()) { + auto prng = mio::abm::PersonalRandomNumberGenerator(person); + //some % of people are infected, large enough to have some infection activity without everyone dying + auto pct_infected = 0.05; + if (mio::UniformDistribution::get_instance()(prng, 0.0, 1.0) < pct_infected) { + auto state = mio::abm::InfectionState( + mio::UniformIntDistribution::get_instance()(prng, 1, int(mio::abm::InfectionState::Count) - 1)); + auto infection = mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, person.get_age(), + model.parameters, mio::abm::TimePoint(0), state); + person.add_new_infection(std::move(infection)); + } + + //equal chance of (moderate) mask refusal and (moderate) mask eagerness + auto pct_compliance_values = std::array{0.05 /*0*/, 0.2 /*0.25*/, 0.5 /*0.5*/, 0.2 /*0.75*/, 0.05 /*1*/}; + auto compliance_value = 0.25 * mio::DiscreteDistribution::get_instance()(prng, pct_compliance_values); + person.set_compliance(mio::abm::InterventionType::Mask, compliance_value); + } + + //masks at locations + for (auto& loc : model.get_locations()) { + //some % of locations require masks + //skip homes so persons always have a place to go, simulation might break otherwise + auto pct_require_mask = 0.2; + if (loc.get_type() != mio::abm::LocationType::Home && + mio::UniformDistribution::get_instance()(model.get_rng()) < pct_require_mask) { + loc.set_required_mask(mio::abm::MaskType::Community); + } } // Assign locations to the people diff --git a/cpp/examples/abm_maximal.cpp b/cpp/examples/abm_maximal.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp deleted file mode 100644 index ab0df1be70..0000000000 --- a/cpp/simulations/abm.cpp +++ /dev/null @@ -1,630 +0,0 @@ -/* -* Copyright (C) 2020-2025 MEmilio -* -* Authors: Daniel Abele, Khoa Nguyen, 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/analyze_result.h" -#include "abm/common_abm_loggers.h" -#include "abm/household.h" -#include "abm/lockdown_rules.h" -#include "memilio/config.h" -#include "memilio/utils/abstract_parameter_distribution.h" -#include "memilio/config.h" -#include "memilio/io/result_io.h" -#include "memilio/utils/parameter_distributions.h" -#include "memilio/utils/random_number_generator.h" -#include "memilio/utils/uncertain_value.h" - -namespace fs = boost::filesystem; - -// Assign the name to general age group. -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); - -/** - * Set a value and distribution of an UncertainValue. - * Assigns average of min and max as a value and UNIFORM(min, max) as a distribution. - * @param p uncertain value to set. - * @param min minimum of distribution. - * @param max minimum of distribution. - */ -void assign_uniform_distribution(mio::UncertainValue<>& p, ScalarType min, ScalarType max) -{ - p = mio::UncertainValue<>(0.5 * (max + min)); - p.set_distribution(mio::ParameterDistributionUniform(min, max)); -} - -/** - * Determine the infection state of a person at the beginning of the simulation. - * The infection states are chosen randomly. They are distributed according to the probabilites set in the example. - * @return random infection state - */ -mio::abm::InfectionState determine_infection_state(mio::abm::PersonalRandomNumberGenerator& rng, ScalarType exposed, - ScalarType infected_no_symptoms, ScalarType infected_symptoms, - ScalarType recovered) -{ - ScalarType susceptible = 1 - exposed - infected_no_symptoms - infected_symptoms - recovered; - std::vector weights = { - susceptible, exposed, infected_no_symptoms, infected_symptoms / 3, infected_symptoms / 3, - infected_symptoms / 3, recovered}; - if (weights.size() != (size_t)mio::abm::InfectionState::Count - 1) { - mio::log_error("Initialization in ABM wrong, please correct vector length."); - } - auto state = mio::DiscreteDistribution::get_instance()(rng, weights); - return (mio::abm::InfectionState)state; -} - -/** - * Calculates a vector in which each entry describes the amount of people living in the corresponding household. - * This is done with equal distribution and if the number of people is not divisible by number of households the last one gets the rest. E.g. number_of_people = 10, number_of_households = 3. Then the vector household_sizes = {3,3,4}. - * @param number_of_people The total amount of people to be distributed. - * @param number_of_households The total amount of households. - * @return A vector with the size of each household. - */ -std::vector last_household_gets_the_rest(int number_of_people, int number_of_households) -{ - std::vector household_sizes(number_of_households, 0); - int avarage_household_size_round_down = number_of_people / number_of_households; //int rounds down. - int people_left = number_of_people - - avarage_household_size_round_down * - number_of_households; // People left if everyone got the same rounded down amount of people. - for (auto i = 0; i < number_of_households - 1; i++) { - household_sizes.at(i) = avarage_household_size_round_down; - } - household_sizes.at(number_of_households - 1) = - avarage_household_size_round_down + people_left; // Last one gets the people which would've been left out. - return household_sizes; -} - -/** - * Constructs a household group which has a single member to represent them all, e.g. all people have the same age distribution. - * @param age_dist A vector with the amount of people in each age group - * @param number_of_people The total amount of people living in this household group. - * @param number_of_hh The number of households in this household group. - * @return householdGroup A Class Household Group. - */ -mio::abm::HouseholdGroup make_uniform_households(const mio::abm::HouseholdMember& member, int number_of_people, - int number_of_hh) -{ - - // The size of each household is calculated in a vector household_size_list. - auto households_size_list = last_household_gets_the_rest(number_of_people, number_of_hh); - - auto householdGroup = mio::abm::HouseholdGroup(); - for (auto& household_size : households_size_list) { - auto household = mio::abm::Household(); - household.add_members(member, household_size); // Add members according to the amount of people in the list. - householdGroup.add_households(household, 1); // Add the household to the household group. - - // assuming 22 square meters per person and 3 meters of room height - // see: https://doi.org/10.1371/journal.pone.0259037 - household.set_space_per_member(66); - } - return householdGroup; -} - -/** - * Constructs a household group with families. - * @param child Child Household Member. - * @param parent Parent Household Member. - * @param random Random Household Member. This is for the rest Group where no exact age distribution can be found. - * @param number_of_persons_in_household Amount of people in this household - * @param number_of_full_familes Amount of full families, e.g. two parents and (number_of_persons_in_household - 2) children. - * @param number_of_half_familes Amount of half families, e.g. one parent and (number_of_persons_in_household - 1) children. - * @param number_of_other_familes number_of_persons_in_household random persons. - * @return A Household group. - */ -mio::abm::HouseholdGroup make_homes_with_families(const mio::abm::HouseholdMember& child, - const mio::abm::HouseholdMember& parent, - const mio::abm::HouseholdMember& random, - int number_of_persons_in_household, int number_of_full_familes, - int number_of_half_familes, int number_of_other_familes) -{ - - auto private_household_group = mio::abm::HouseholdGroup(); - - // Add full families. - auto household_full = mio::abm::Household(); - household_full.add_members(child, number_of_persons_in_household - 2); - household_full.add_members(parent, 2); - private_household_group.add_households(household_full, number_of_full_familes); - - // Add half families. - auto household_half = mio::abm::Household(); - household_half.add_members(child, number_of_persons_in_household - 1); - household_half.add_members(parent, 1); - private_household_group.add_households(household_half, number_of_half_familes); - - // Add other families. - if (number_of_persons_in_household < 5) { - auto household_others = mio::abm::Household(); - household_others.add_members(random, number_of_persons_in_household); - private_household_group.add_households(household_others, number_of_other_familes); - } - else if (number_of_persons_in_household == 5) { - // For 5 and more people in one household we have to distribute the rest onto the left over households. - int people_left_size5 = 545; - - auto households_size_list = last_household_gets_the_rest(people_left_size5, number_of_other_familes); - - auto household_rest = mio::abm::HouseholdGroup(); - for (auto& household_size : households_size_list) { - auto household = mio::abm::Household(); - household.add_members(random, household_size); // Add members according to the amount of people in the list. - household_rest.add_households(household, 1); // Add the household to the household group. - } - } - return private_household_group; -} - -void create_model_from_statistical_data(mio::abm::Model& model) -{ - - /** The data is taken from - * https://www-genesis.destatis.de/genesis/online?operation=statistic&levelindex=0&levelid=1627908577036&code=12211#abreadcrumb - * All numbers are in 1000. - * Destatis divides the Households into community households and private households. - * Community Households are: Refugee, Disabled, Retirement and Others. We have an explicit age distribution, amount of households and amount of people for them but not the exact amount of people in each household. - * The private Households are divided with respect to the amount of people living in each household. For a one person household we have the exact age distribution. For the rest we have data about which kind of family lives in them. The different kinds of families are: A family with two parents and the rest are children, a family with one parent and the rest are children and "other" families with no exact data about their age. - */ - - // Refugee - auto refugee = mio::abm::HouseholdMember(num_age_groups); - refugee.set_age_weight(age_group_0_to_4, 25); - refugee.set_age_weight(age_group_5_to_14, 12); - refugee.set_age_weight(age_group_15_to_34, 25); - refugee.set_age_weight(age_group_35_to_59, 9); - refugee.set_age_weight(age_group_60_to_79, 1); - refugee.set_age_weight(age_group_80_plus, 1); - int refugee_number_of_people = 74; - int refugee_number_of_households = 12; - auto refugeeGroup = make_uniform_households(refugee, refugee_number_of_people, refugee_number_of_households); - - add_household_group_to_model(model, refugeeGroup); - - // Disabled - auto disabled = mio::abm::HouseholdMember(num_age_groups); - disabled.set_age_weight(age_group_0_to_4, 2); - disabled.set_age_weight(age_group_5_to_14, 6); - disabled.set_age_weight(age_group_15_to_34, 13); - disabled.set_age_weight(age_group_35_to_59, 42); - disabled.set_age_weight(age_group_60_to_79, 97); - disabled.set_age_weight(age_group_80_plus, 32); - int disabled_number_of_people = 194; - int disabled_number_of_households = 8; - - auto disabledGroup = make_uniform_households(disabled, disabled_number_of_people, disabled_number_of_households); - - add_household_group_to_model(model, disabledGroup); - - // Retirement - auto retired = mio::abm::HouseholdMember(num_age_groups); - retired.set_age_weight(age_group_15_to_34, 1); - retired.set_age_weight(age_group_35_to_59, 30); - retired.set_age_weight(age_group_60_to_79, 185); - retired.set_age_weight(age_group_80_plus, 530); - int retirement_number_of_people = 744; - int retirement_number_of_households = 16; - - auto retirementGroup = - make_uniform_households(retired, retirement_number_of_people, retirement_number_of_households); - - add_household_group_to_model(model, retirementGroup); - - // Others - auto other = mio::abm::HouseholdMember(num_age_groups); - other.set_age_weight(age_group_0_to_4, 30); - other.set_age_weight(age_group_5_to_14, 40); - other.set_age_weight(age_group_15_to_34, 72); - other.set_age_weight(age_group_35_to_59, 40); - other.set_age_weight(age_group_60_to_79, 30); - other.set_age_weight(age_group_80_plus, 10); - int others_number_of_people = 222; - int others_number_of_households = 20; - - auto otherGroup = make_uniform_households(other, others_number_of_people, others_number_of_households); - - add_household_group_to_model(model, otherGroup); - - // One Person Household (we have exact age data about this) - auto one_person_household_member = mio::abm::HouseholdMember(num_age_groups); - one_person_household_member.set_age_weight(age_group_15_to_34, 4364); - one_person_household_member.set_age_weight(age_group_35_to_59, 7283); - one_person_household_member.set_age_weight(age_group_60_to_79, 4100); - one_person_household_member.set_age_weight(age_group_80_plus, 1800); - int one_person_number_of_people = 15387; - int one_person_number_of_households = 15387; - - auto onePersonGroup = make_uniform_households(one_person_household_member, one_person_number_of_people, - one_person_number_of_households); - - add_household_group_to_model(model, onePersonGroup); - - // For more than 1 family households we need families. These are parents and children and randoms (which are distributed like the data we have for these households). - auto child = mio::abm::HouseholdMember(num_age_groups); // A child is 50/50% 0-4 or 5-14. - child.set_age_weight(age_group_0_to_4, 1); - child.set_age_weight(age_group_5_to_14, 1); - - auto parent = mio::abm::HouseholdMember(num_age_groups); // A child is 40/40/20% 15-34, 35-59 or 60-79. - parent.set_age_weight(age_group_15_to_34, 2); - parent.set_age_weight(age_group_35_to_59, 2); - parent.set_age_weight(age_group_60_to_79, 1); - - auto random = - mio::abm::HouseholdMember(num_age_groups); // Randoms are distributed according to the left over persons. - random.set_age_weight(age_group_0_to_4, 5000); - random.set_age_weight(age_group_5_to_14, 6000); - random.set_age_weight(age_group_15_to_34, 14943); - random.set_age_weight(age_group_35_to_59, 22259); - random.set_age_weight(age_group_60_to_79, 11998); - random.set_age_weight(age_group_80_plus, 5038); - - // Two person households - int two_person_full_families = 11850; - int two_person_half_families = 1765; - int two_person_other_families = 166; - auto twoPersonHouseholds = make_homes_with_families(child, parent, random, 2, two_person_full_families, - two_person_half_families, two_person_other_families); - add_household_group_to_model(model, twoPersonHouseholds); - - // Three person households - int three_person_full_families = 4155; - int three_person_half_families = 662; - int three_person_other_families = 175; - auto threePersonHouseholds = make_homes_with_families(child, parent, random, 3, three_person_full_families, - three_person_half_families, three_person_other_families); - add_household_group_to_model(model, threePersonHouseholds); - - // Four person households - int four_person_full_families = 3551; - int four_person_half_families = 110; - int four_person_other_families = 122; - auto fourPersonHouseholds = make_homes_with_families(child, parent, random, 4, four_person_full_families, - four_person_half_families, four_person_other_families); - add_household_group_to_model(model, fourPersonHouseholds); - - // Five plus person households - int fiveplus_person_full_families = 1245; - int fiveplus_person_half_families = 80; - int fiveplus_person_other_families = 82; - auto fivePlusPersonHouseholds = - make_homes_with_families(child, parent, random, 5, fiveplus_person_full_families, fiveplus_person_half_families, - fiveplus_person_other_families); - add_household_group_to_model(model, fivePlusPersonHouseholds); -} - -/** - * Add locations to the model and assign locations to the people. - */ -void create_assign_locations(mio::abm::Model& model) -{ - // Add one social event with 100 maximum contacts. - // Maximum contacs limit the number of people that a person can infect while being at this location. - // A high percentage of people (50-100%) have to get tested in the 2 days before the event - // For the capacity we assume an area of 1.25 m^2 per person (https://doi.org/10.1371/journal.pone.0259037) and a - // room height of 3 m - auto event = model.add_location(mio::abm::LocationType::SocialEvent); - model.get_location(event).get_infection_parameters().set(100); - model.get_location(event).set_capacity(100, 375); - - auto testing_criteria = mio::abm::TestingCriteria(); - auto validity_period = mio::abm::days(2); - auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(60); - - auto probability = mio::UncertainValue<>(); - assign_uniform_distribution(probability, 0.5, 1.0); - - auto test_params = model.parameters.get()[mio::abm::TestType::Antigen]; - auto testing_scheme = mio::abm::TestingScheme(testing_criteria, validity_period, start_date, end_date, test_params, - probability.draw_sample()); - - model.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::SocialEvent, testing_scheme); - - // Add hospital and ICU with 5 maximum contacs. - // For the number of agents in this example we assume a capacity of 584 persons (80 beds per 10000 residents in - // Germany (Statistisches Bundesamt, 2022) and a volume of 26242 m^3 - // (https://doi.org/10.1016/j.buildenv.2021.107926)) - // For the ICUs we assume a capacity of 30 agents and the same volume. - auto hospital = model.add_location(mio::abm::LocationType::Hospital); - model.get_location(hospital).get_infection_parameters().set(5); - model.get_location(hospital).set_capacity(584, 26242); - auto icu = model.add_location(mio::abm::LocationType::ICU); - model.get_location(icu).get_infection_parameters().set(5); - model.get_location(icu).set_capacity(30, 1350); - - // Add schools, workplaces and shops. - // At every school there are 600 students. The maximum contacs are 40. - // Students have to get tested once a week. - // We assume 2 m^2 per student (https://doi.org/10.1371/journal.pone.0259037) and a room height of 3 m. - // At every workplace work 100 people (needs to be varified), maximum contacts are 40. - // People can get tested at work (and do this with 0.5 probability). - // Per person we assume an area of 10 m^2 (https://doi.org/10.1371/journal.pone.0259037) and a room height of 3 m. - // Add one supermarked per 15.000 people, maximum constacts are assumed to be 20. - // A shop has a capacity of 240 persons (https://doi.org/10.1016/j.buildenv.2021.107926) - // and a volume of 7200 cubic meters (10 m^2 per person (https://doi.org/10.1371/journal.pone.0259037) and 3 m - // room height). - auto shop = model.add_location(mio::abm::LocationType::BasicsShop); - model.get_location(shop).get_infection_parameters().set(20); - model.get_location(shop).set_capacity(240, 7200); - - auto school = model.add_location(mio::abm::LocationType::School); - model.get_location(school).get_infection_parameters().set(40); - model.get_location(school).set_capacity(600, 3600); - - auto work = model.add_location(mio::abm::LocationType::Work); - model.get_location(work).get_infection_parameters().set(40); - model.get_location(work).set_capacity(100, 3000); - - int counter_event = 0; - int counter_school = 0; - int counter_work = 0; - int counter_shop = 0; - //Assign locations to the people - auto persons = model.get_persons(); - for (auto& person : persons) { - const auto id = person.get_id(); - //assign shop and event - model.assign_location(id, event); - counter_event++; - model.assign_location(id, shop); - counter_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); - counter_school++; - } - if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - model.assign_location(id, work); - counter_work++; - } - //add new school/work/shop if needed - if (counter_event == 1000) { - counter_event = 0; - event = model.add_location(mio::abm::LocationType::SocialEvent); - model.get_location(event).set_capacity(100, 375); - model.get_location(event).get_infection_parameters().set(100); - } - if (counter_school == 600) { - counter_school = 0; - school = model.add_location(mio::abm::LocationType::School); - model.get_location(school).get_infection_parameters().set(40); - model.get_location(school).set_capacity(600, 3600); - } - if (counter_work == 100) { - counter_work = 0; - work = model.add_location(mio::abm::LocationType::Work); - model.get_location(work).get_infection_parameters().set(40); - model.get_location(work).set_capacity(100, 3000); - } - if (counter_shop == 15000) { - counter_shop = 0; - shop = model.add_location(mio::abm::LocationType::BasicsShop); - model.get_location(shop).get_infection_parameters().set(20); - model.get_location(shop).set_capacity(240, 7200); - } - } - - // add the testing schemes for school and work - auto testing_criteria_school = mio::abm::TestingCriteria(); - validity_period = mio::abm::days(7); - auto testing_scheme_school = mio::abm::TestingScheme(testing_criteria_school, validity_period, start_date, end_date, - test_params, probability.draw_sample()); - model.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::School, testing_scheme_school); - - auto test_at_work = std::vector{mio::abm::LocationType::Work}; - auto testing_criteria_work = mio::abm::TestingCriteria(); - - assign_uniform_distribution(probability, 0.1, 0.5); - auto testing_scheme_work = mio::abm::TestingScheme(testing_criteria_work, validity_period, start_date, end_date, - test_params, probability.draw_sample()); - model.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::Work, testing_scheme_work); -} - -/** - * Assign an infection state to each person. - */ -void assign_infection_state(mio::abm::Model& model, mio::abm::TimePoint t, double exposed_prob, - double infected_no_symptoms_prob, double infected_symptoms_prob, double recovered_prob) -{ - auto persons = model.get_persons(); - for (auto& person : persons) { - auto rng = mio::abm::PersonalRandomNumberGenerator(person); - auto infection_state = determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, - infected_symptoms_prob, recovered_prob); - if (infection_state != mio::abm::InfectionState::Susceptible) { - person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model.parameters, t, infection_state, - person.get_latest_protection(), false)); - } - } -} - -void set_parameters(mio::abm::Parameters params) -{ - // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) - params.get()[age_group_5_to_14] = true; - // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 and 35-59) - params.get().set_multiple({age_group_15_to_34, age_group_35_to_59}, true); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); -} - -/** - * Create a sampled model with start time t0. - * @param t0 the start time of the simulation -*/ -mio::abm::Model create_sampled_model(const mio::abm::TimePoint& t0) -{ - // mio::thread_local_rng().seed( - // {123144124, 835345345, 123123123, 99123}); //set seeds, e.g., for debugging - printf("Parameter Sample Seeds: "); - for (auto s : mio::thread_local_rng().get_seeds()) { - printf("%u, ", s); - } - printf("\n"); - - // Assumed percentage of infection state at the beginning of the simulation. - ScalarType exposed_prob = 0.005, infected_no_symptoms_prob = 0.001, infected_symptoms_prob = 0.001, - recovered_prob = 0.0; - - //Set global infection parameters (similar to infection parameters in SECIR model) and initialize the model - auto model = mio::abm::Model(num_age_groups); - - set_parameters(model.parameters); - - // model.get_rng().seed( - // {23144124, 1835345345, 9343763, 9123}); //set seeds, e.g., for debugging - printf("ABM Simulation Seeds: "); - for (auto s : model.get_rng().get_seeds()) { - printf("%u, ", s); - } - printf("\n"); - - // Create the model object from statistical data. - create_model_from_statistical_data(model); - - // Assign an infection state to each person. - assign_infection_state(model, t0, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); - - // Add locations and assign locations to the people. - create_assign_locations(model); - - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(20); - - // During the lockdown, 25% of people work from home and schools are closed for 90% of students. - // Social events are very rare. - mio::abm::set_home_office(t_lockdown, 0.25, model.parameters); - mio::abm::set_school_closure(t_lockdown, 0.9, model.parameters); - mio::abm::close_social_events(t_lockdown, 0.9, model.parameters); - - return model; -} - -/** - * Run the ABM simulation. - * @param result_dir Directory where all results of the parameter study will be stored. - * @param num_runs Number of runs. - * @param save_single_runs [Default: true] Defines if single run results are written to the disk. - * @returns Any io error that occurs during reading or writing of files. - */ -mio::IOResult run(const fs::path& result_dir, size_t num_runs, bool save_single_runs = true) -{ - - auto t0 = mio::abm::TimePoint(0); // Start time per simulation - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(60); // End time per simulation - auto ensemble_results = std::vector>>{}; // Vector of collected results - ensemble_results.reserve(size_t(num_runs)); - auto ensemble_params = std::vector>{}; - ensemble_params.reserve(size_t(num_runs)); - auto run_idx = size_t(1); // The run index - - // Create the sampled simulation with start time t0 - auto model = create_sampled_model(t0); - ensemble_params.push_back(std::vector{model}); - - // Loop over a number of runs - while (run_idx <= num_runs) { - // Make a simulation using a copy from the original model - auto sim = mio::abm::Simulation(t0, mio::abm::Model(model)); - // Add a time series writer to the simulation - mio::History historyTimeSeries{ - Eigen::Index(mio::abm::InfectionState::Count)}; - // Advance the model to tmax - sim.advance(tmax, historyTimeSeries); - // Collect the results from the simulation - ensemble_results.push_back(std::vector>{std::get<0>(historyTimeSeries.get_log())}); - // Increase the run index - ++run_idx; - } - // Save all results to files - BOOST_OUTCOME_TRY(save_results(ensemble_results, ensemble_params, {0}, result_dir, save_single_runs)); - return mio::success(); -} - -int main(int argc, char** argv) -{ - - mio::set_log_level(mio::LogLevel::warn); - - std::string result_dir = "."; - size_t num_runs; - bool save_single_runs = true; - - if (argc == 2) { - num_runs = atoi(argv[1]); - printf("Number of run is %s.\n", argv[1]); - printf("Saving results to the current directory.\n"); - } - - else if (argc == 3) { - num_runs = atoi(argv[1]); - result_dir = argv[2]; - printf("Number of runs is %s.\n", argv[1]); - printf("Saving results to \"%s\".\n", result_dir.c_str()); - } - else { - printf("Usage:\n"); - printf("abm_example \n"); - printf("\tRun the simulation for time(s).\n"); - printf("\tStore the results in the current directory.\n"); - printf("abm_example \n"); - printf("\tRun the simulation for time(s).\n"); - printf("\tStore the results in .\n"); - return 0; - } - - auto result = run(result_dir, num_runs, save_single_runs); - if (!result) { - printf("%s\n", result.error().formatted_message().c_str()); - return -1; - } - return 0; -} diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp deleted file mode 100644 index f1758ada16..0000000000 --- a/cpp/simulations/abm_braunschweig.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -/* -* Copyright (C) 2020-2025 MEmilio -* -* Authors: Sascha Korf, 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 "abm/common_abm_loggers.h" -#include "abm/location_id.h" -#include "abm/lockdown_rules.h" -#include "abm/parameters.h" -#include "abm/parameters.h" -#include "abm/person.h" -#include "abm/person_id.h" -#include "abm/simulation.h" -#include "abm/model.h" -#include "memilio/utils/abstract_parameter_distribution.h" -#include "memilio/epidemiology/age_group.h" -#include "memilio/io/io.h" -#include "memilio/io/result_io.h" -#include "memilio/utils/parameter_distributions.h" -#include "memilio/utils/uncertain_value.h" -#include "boost/algorithm/string/split.hpp" -#include "boost/algorithm/string/classification.hpp" - -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -// Assign the name to general age group. -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); - -/** - * Set a value and distribution of an UncertainValue. - * Assigns average of min and max as a value and UNIFORM(min, max) as a distribution. - * @param p uncertain value to set. - * @param min minimum of distribution. - * @param max minimum of distribution. - */ -void assign_uniform_distribution(mio::UncertainValue<>& p, ScalarType min, ScalarType max) -{ - p = mio::UncertainValue<>(0.5 * (max + min)); - p.set_distribution(mio::ParameterDistributionUniform(min, max)); -} - -/** - * Determine the infection state of a person at the beginning of the simulation. - * The infection states are chosen randomly. They are distributed according to the probabilites set in the example. - * @return random infection state - */ -mio::abm::InfectionState determine_infection_state(mio::abm::PersonalRandomNumberGenerator& rng, ScalarType exposed, - ScalarType infected_no_symptoms, ScalarType infected_symptoms, - ScalarType recovered) -{ - ScalarType susceptible = 1 - exposed - infected_no_symptoms - infected_symptoms - recovered; - std::vector weights = { - susceptible, exposed, infected_no_symptoms, infected_symptoms / 3, infected_symptoms / 3, - infected_symptoms / 3, recovered}; - if (weights.size() != (size_t)mio::abm::InfectionState::Count - 1) { - mio::log_error("Initialization in ABM wrong, please correct vector length."); - } - auto state = mio::DiscreteDistribution::get_instance()(rng, weights); - return (mio::abm::InfectionState)state; -} - -/** - * Assign an infection state to each person. - */ -void assign_infection_state(mio::abm::Model& model, mio::abm::TimePoint t, double exposed_prob, - double infected_no_symptoms_prob, double infected_symptoms_prob, double recovered_prob) -{ - auto persons = model.get_persons(); - for (auto& person : persons) { - auto rng = mio::abm::PersonalRandomNumberGenerator(person); - auto infection_state = determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, - infected_symptoms_prob, recovered_prob); - if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model.parameters, t, infection_state)); - } -} -int stringToMinutes(const std::string& input) -{ - size_t colonPos = input.find(":"); - if (colonPos == std::string::npos) { - // Handle invalid input (no colon found) - return -1; // You can choose a suitable error code here. - } - - std::string xStr = input.substr(0, colonPos); - std::string yStr = input.substr(colonPos + 1); - - int x = std::stoi(xStr); - int y = std::stoi(yStr); - return x * 60 + y; -} - -int longLatToInt(const std::string& input) -{ - double y = std::stod(input) * 1e+5; //we want the 5 numbers after digit - return (int)y; -} -void split_line(std::string string, std::vector* row) -{ - std::vector strings; - - std::string x = ",,", y = ",-1,"; - size_t pos; - while ((pos = string.find(x)) != std::string::npos) { - string.replace(pos, 2, y); - } // Temporary fix to handle empty cells. - boost::split(strings, string, boost::is_any_of(",")); - std::transform(strings.begin(), strings.end(), std::back_inserter(*row), [&](std::string s) { - if (s.find(":") != std::string::npos) { - return stringToMinutes(s); - } - else if (s.find(".") != std::string::npos) { - return longLatToInt(s); - } - else { - return std::stoi(s); - } - }); -} - -mio::abm::LocationType get_location_type(uint32_t acitivity_end) -{ - mio::abm::LocationType type; - switch (acitivity_end) { - case 1: - type = mio::abm::LocationType::Work; - break; - case 2: - type = mio::abm::LocationType::School; - break; - case 3: - type = mio::abm::LocationType::BasicsShop; - break; - case 4: - type = mio::abm::LocationType::SocialEvent; // Freizeit - break; - case 5: - type = mio::abm::LocationType::BasicsShop; // Private Erledigung - break; - case 6: - type = mio::abm::LocationType::SocialEvent; // Sonstiges - break; - default: - type = mio::abm::LocationType::Home; - break; - } - return type; -} - -mio::AgeGroup determine_age_group(uint32_t age) -{ - if (age <= 4) { - return age_group_0_to_4; - } - else if (age <= 14) { - return age_group_5_to_14; - } - else if (age <= 34) { - return age_group_15_to_34; - } - else if (age <= 59) { - return age_group_35_to_59; - } - else if (age <= 79) { - return age_group_60_to_79; - } - else { - return age_group_80_plus; - } -} - -void create_model_from_data(mio::abm::Model& model, const std::string& filename, const mio::abm::TimePoint t0, - int max_number_persons) -{ - // Open File - const fs::path p = filename; - if (!fs::exists(p)) { - 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++; - } - - std::map locations = {}; - std::map pids_data_to_model = {}; - std::map person_ids = {}; - std::map> locations_before; - std::map> locations_after; - - // For the model we need: Hospitals, ICUs (for both we just create one for now), Homes for each unique householdID, One Person for each person_id with respective age and home_id. - - // We assume that no person goes to an hospital, altough e.g. "Sonstiges" could be a hospital - auto hospital = model.add_location(mio::abm::LocationType::Hospital); - model.get_location(hospital).get_infection_parameters().set(5); - model.get_location(hospital).set_capacity(std::numeric_limits::max(), - std::numeric_limits::max()); - auto icu = model.add_location(mio::abm::LocationType::ICU); - model.get_location(icu).get_infection_parameters().set(5); - model.get_location(icu).set_capacity(std::numeric_limits::max(), std::numeric_limits::max()); - - // First we determine the persons number and their starting locations - int number_of_persons = 0; - - while (std::getline(fin, line)) { - row.clear(); - - // read columns in this row - split_line(line, &row); - line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - - uint32_t person_id = row[index["puid"]]; - auto it_person_id = person_ids.find(person_id); - if (it_person_id == person_ids.end()) { - if (number_of_persons >= max_number_persons) - break; //This is okay because the data is sorted by person_id - person_ids.insert({person_id, number_of_persons}); - number_of_persons++; - } - - // The starting location of a person is the end location of the last trip he made, either on the same day or on - // the day before - uint32_t target_location_id = std::abs(row[index["loc_id_end"]]); - int trip_start = row[index["start_time"]]; - if (trip_start < t0.hour_of_day()) { - auto it_person = locations_before.find(person_id); - if (it_person == locations_before.end()) { - locations_before.insert({person_id, std::make_pair(target_location_id, trip_start)}); - } - else { - if (it_person->second.second <= trip_start) { - it_person->second.first = target_location_id; - it_person->second.second = trip_start; - } - } - } - else { - auto it_person = locations_after.find(person_id); - if (it_person == locations_after.end()) { - locations_after.insert({person_id, std::make_pair(target_location_id, trip_start)}); - } - else { - if (it_person->second.second <= trip_start) { - it_person->second.first = target_location_id; - it_person->second.second = trip_start; - } - } - } - } - - fin.clear(); - fin.seekg(0); - std::getline(fin, line); // Skip header row - - // Add all locations to the model - while (std::getline(fin, line)) { - row.clear(); - - // read columns in this row - split_line(line, &row); - line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - - uint32_t person_id = row[index["puid"]]; - if (person_ids.find(person_id) == person_ids.end()) - break; - - uint32_t home_id = row[index["huid"]]; - uint32_t target_location_id = std::abs(row[index["loc_id_end"]]); - uint32_t activity_end = row[index["activity_end"]]; - mio::abm::GeographicalLocation location_long_lat = {(double)row[index["lon_end"]] / 1e+5, - (double)row[index["lat_end"]] / 1e+5}; - mio::abm::LocationId home; - auto it_home = locations.find(home_id); - if (it_home == locations.end()) { - home = model.add_location(mio::abm::LocationType::Home, 1); - locations.insert({home_id, home}); - mio::abm::GeographicalLocation location_long_lat_home = {(double)row[index["lon_start"]] / 1e+5, - (double)row[index["lat_start"]] / 1e+5}; - model.get_location(home).set_geographical_location(location_long_lat_home); - } - else { - home = it_home->second; - } - - mio::abm::LocationId location; - auto it_location = locations.find( - target_location_id); // Check if location already exists also for home which have the same id (home_id = target_location_id) - if (it_location == locations.end()) { - location = model.add_location( - get_location_type(activity_end), - 1); // Assume one place has one activity, this may be untrue but not important for now(?) - locations.insert({target_location_id, location}); - model.get_location(location).set_geographical_location(location_long_lat); - } - } - fin.clear(); - fin.seekg(0); - std::getline(fin, line); // Skip header row - - // Add the persons and trips - while (std::getline(fin, line)) { - row.clear(); - - // read columns in this row - split_line(line, &row); - line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - - uint64_t person_data_id = row[index["puid"]]; - if (person_ids.find(person_data_id) == person_ids.end()) - break; - - uint32_t age = row[index["age"]]; - uint32_t home_id = row[index["huid"]]; - uint32_t target_location_id = std::abs(row[index["loc_id_end"]]); - uint32_t start_location_id = std::abs(row[index["loc_id_start"]]); - uint32_t trip_start = row[index["start_time"]]; - uint32_t transport_mode = row[index["travel_mode"]]; - uint32_t acticity_end = row[index["activity_end"]]; - - // Add the trip to the trip list person and location must exist at this point - auto target_location = locations.find(target_location_id)->second; - auto start_location = locations.find(start_location_id)->second; - - auto pid_itr = pids_data_to_model.find(person_data_id); - - if (pid_itr == pids_data_to_model.end()) { // person has not been added to model yet - auto it_first_location_id = locations_before.find(person_data_id); - if (it_first_location_id == locations_before.end()) { - it_first_location_id = locations_after.find(person_data_id); - } - auto first_location_id = it_first_location_id->second.first; - auto first_location = locations.find(first_location_id)->second; - auto person_model_id = model.add_person(first_location, determine_age_group(age)); - auto home = locations.find(home_id)->second; - model.assign_location(person_model_id, home); - model.assign_location(person_model_id, hospital); - model.assign_location(person_model_id, icu); - pid_itr = pids_data_to_model.insert_or_assign(person_data_id, person_model_id).first; - } - - model.assign_location( - pid_itr->second, - target_location); //This assumes that we only have in each tripchain only one location type for each person - if (locations.find(start_location_id) == locations.end()) { - // For trips where the start location is not known use Home instead - start_location = model.get_person(pid_itr->second).get_assigned_location(mio::abm::LocationType::Home); - } - model.get_trip_list().add_trip( - mio::abm::Trip(static_cast(pid_itr->first), - mio::abm::TimePoint(0) + mio::abm::minutes(trip_start), target_location, start_location, - mio::abm::TransportMode(transport_mode), mio::abm::LocationType(acticity_end))); - } - model.get_trip_list().use_weekday_trips_on_weekend(); -} - -void set_parameters(mio::abm::Parameters params) -{ - // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) - params.get()[age_group_5_to_14] = true; - // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 and 35-59) - params.get().set_multiple({age_group_15_to_34, age_group_35_to_59}, true); - params.set( - {{mio::abm::VirusVariant::Count, mio::AgeGroup(num_age_groups)}, mio::ParameterDistributionLogNormal(4., 1.)}); - - //0-4 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = - mio::ParameterDistributionLogNormal(4., 1.); - - //5-14 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_5_to_14}] = - mio::ParameterDistributionLogNormal(4., 1.); - - //15-34 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - mio::ParameterDistributionLogNormal(4., 1.); - - //35-59 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - mio::ParameterDistributionLogNormal(4., 1.); - - //60-79 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - mio::ParameterDistributionLogNormal(4., 1.); - - //80+ - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - mio::ParameterDistributionLogNormal(4., 1.); - - // Set each parameter for vaccinated people including personal infection and vaccine protection levels. - // Summary: https://doi.org/10.1038/s41577-021-00550-x, - - //0-4 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.161; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.001; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.015; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.001; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.001; - - // Protection of reinfection is the same for all age-groups, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5, https://doi.org/10.1038/s41591-021-01377-8 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_0_to_4, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.852}, - {180, 0.852}, - {210, 0.845}, - {240, 0.828}, - {270, 0.797}, - {300, 0.759}, - {330, 0.711}, - {360, 0.661}, - {390, 0.616}, - {420, 0.580}, - {450, 0.559}, - {450, 0.550}}}; - - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_0_to_4, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.91}, {60, 0.92}, {90, 0.88}, {120, 0.84}, {150, 0.81}, {180, 0.88}, {450, 0.5}}}; - - // Set up age-related severe protection levels, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_0_to_4, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.967}, - {30, 0.975}, - {60, 0.977}, - {90, 0.974}, - {120, 0.963}, - {150, 0.947}, - {180, 0.93}, - {210, 0.929}, - {240, 0.923}, - {270, 0.908}, - {300, 0.893}, - {330, 0.887}, - {360, 0.887}, - {450, 0.5}}}; - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_0_to_4, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.88}, {60, 0.91}, {90, 0.98}, {120, 0.94}, {150, 0.88}, {450, 0.5}}}; - - //5-14 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.161; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.001; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.015; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.001; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 0.001; - - // Protection of reinfection is the same for all age-groups, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5, https://doi.org/10.1038/s41591-021-01377-8 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_5_to_14, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.852}, - {180, 0.852}, - {210, 0.845}, - {240, 0.828}, - {270, 0.797}, - {300, 0.759}, - {330, 0.711}, - {360, 0.661}, - {390, 0.616}, - {420, 0.580}, - {450, 0.559}, - {450, 0.550}}}; - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_5_to_14, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.91}, {60, 0.92}, {90, 0.88}, {120, 0.84}, {150, 0.81}, {180, 0.88}, {450, 0.5}}}; - - // Set up age-related severe protection levels, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_5_to_14, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.967}, - {30, 0.975}, - {60, 0.977}, - {90, 0.974}, - {120, 0.963}, - {150, 0.947}, - {180, 0.93}, - {210, 0.929}, - {240, 0.923}, - {270, 0.908}, - {300, 0.893}, - {330, 0.887}, - {360, 0.887}, - {450, 0.5}}}; - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_5_to_14, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.88}, {60, 0.91}, {90, 0.98}, {120, 0.94}, {150, 0.88}, {450, 0.5}}}; - - //15-34 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = - 0.179; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = 0.001; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = 0.013; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = 0.021; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_15_to_34}] = 0.021; - - // Set up personal infection and vaccine protection levels, based on: https://doi.org/10.1038/s41577-021-00550-x, https://doi.org/10.1038/s41591-021-01377-8 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_15_to_34, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.852}, - {180, 0.852}, - {210, 0.845}, - {240, 0.828}, - {270, 0.797}, - {300, 0.759}, - {330, 0.711}, - {360, 0.661}, - {390, 0.616}, - {420, 0.580}, - {450, 0.559}, - {450, 0.550}}}; - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_15_to_34, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.89}, {60, 0.84}, {90, 0.78}, {120, 0.68}, {150, 0.57}, {180, 0.39}, {450, 0.1}}}; - // Set up age-related severe protection levels, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_15_to_34, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.967}, - {30, 0.975}, - {60, 0.977}, - {90, 0.974}, - {120, 0.963}, - {150, 0.947}, - {180, 0.93}, - {210, 0.929}, - {240, 0.923}, - {270, 0.908}, - {300, 0.893}, - {330, 0.887}, - {360, 0.887}, - {450, 0.5}}}; - // Information is from: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_15_to_34, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.88}, {60, 0.91}, {90, 0.98}, {120, 0.94}, {150, 0.88}, {180, 0.90}, {450, 0.5}}}; - - //35-59 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = - 0.179; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = 0.003; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = 0.02; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = 0.008; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_35_to_59}] = 0.008; - // Protection of reinfection is the same for all age-groups, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5, https://doi.org/10.1038/s41591-021-01377-8 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_35_to_59, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.852}, - {180, 0.852}, - {210, 0.845}, - {240, 0.828}, - {270, 0.797}, - {300, 0.759}, - {330, 0.711}, - {360, 0.661}, - {390, 0.616}, - {420, 0.580}, - {450, 0.559}, - {450, 0.550}}}; - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_35_to_59, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.89}, {60, 0.84}, {90, 0.78}, {120, 0.68}, {150, 0.57}, {180, 0.39}, {450, 0.1}}}; - // Set up age-related severe protection levels, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_35_to_59, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.967}, - {30, 0.975}, - {60, 0.977}, - {90, 0.974}, - {120, 0.963}, - {150, 0.947}, - {180, 0.93}, - {210, 0.929}, - {240, 0.923}, - {270, 0.908}, - {300, 0.893}, - {330, 0.887}, - {360, 0.887}, - {450, 0.5}}}; - // Information is from: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_35_to_59, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.88}, {60, 0.91}, {90, 0.98}, {120, 0.94}, {150, 0.88}, {180, 0.90}, {450, 0.5}}}; - //60-79 - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = - 0.179; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.009; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.035; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.023; - // Protection of reinfection is the same for all age-groups, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5, https://doi.org/10.1038/s41591-021-01377-8 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_60_to_79, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.852}, - {180, 0.852}, - {210, 0.845}, - {240, 0.828}, - {270, 0.797}, - {300, 0.759}, - {330, 0.711}, - {360, 0.661}, - {390, 0.616}, - {420, 0.580}, - {450, 0.559}, - {450, 0.550}}}; - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_60_to_79, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.87}, {60, 0.85}, {90, 0.78}, {120, 0.67}, {150, 0.61}, {180, 0.50}, {450, 0.1}}}; - // Set up personal severe protection levels. - // Protection of severe infection of age group 65 + is different from other age group, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_60_to_79, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.967}, - {30, 0.975}, - {60, 0.977}, - {90, 0.974}, - {120, 0.963}, - {150, 0.947}, - {180, 0.93}, - {210, 0.929}, - {240, 0.923}, - {270, 0.908}, - {300, 0.893}, - {330, 0.887}, - {360, 0.887}, - {360, 0.5}}}; - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_60_to_79, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.91}, {60, 0.86}, {90, 0.91}, {120, 0.94}, {150, 0.95}, {180, 0.90}, {450, 0.5}}}; - - //80+ - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = - 0.179; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.012; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.036; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.052; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_80_plus}] = 0.052; - // Protection of reinfection is the same for all age-groups, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5, https://doi.org/10.1038/s41591-021-01377-8 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_80_plus, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.852}, - {180, 0.852}, - {210, 0.845}, - {240, 0.828}, - {270, 0.797}, - {300, 0.759}, - {330, 0.711}, - {360, 0.661}, - {390, 0.616}, - {420, 0.580}, - {450, 0.559}, - {450, 0.550}}}; - // Information is from: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_80_plus, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.80}, {60, 0.79}, {90, 0.75}, {120, 0.56}, {150, 0.49}, {180, 0.43}, {450, 0.1}}}; - // Set up personal severe protection levels. - // Protection of severe infection of age group 65 + is different from other age group, based on: - // https://doi.org/10.1016/S0140-6736(22)02465-5 - params.get()[{mio::abm::ProtectionType::NaturalInfection, age_group_0_to_4, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.967}, - {30, 0.975}, - {60, 0.977}, - {90, 0.974}, - {120, 0.963}, - {150, 0.947}, - {180, 0.93}, - {210, 0.929}, - {240, 0.923}, - {270, 0.908}, - {300, 0.893}, - {330, 0.887}, - {360, 0.887}, - {360, 0.5}}}; - // Information is based on: https://doi.org/10.1016/S0140-6736(21)02183-8 - params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_80_plus, - mio::abm::VirusVariant::Wildtype}] = { - mio::TimeSeriesFunctorType::LinearInterpolation, - {{0, 0.5}, {30, 0.84}, {60, 0.88}, {90, 0.89}, {120, 0.86}, {150, 0.85}, {180, 0.83}, {450, 0.5}}}; -} - -/** - * Create a sampled simulation with start time t0. - * @param t0 The start time of the Simulation. - */ -mio::abm::Simulation<> create_sampled_simulation(const std::string& input_file, const mio::abm::TimePoint& t0, - int max_num_persons) -{ - // Assumed percentage of infection state at the beginning of the simulation. - ScalarType exposed_prob = 0.005, infected_no_symptoms_prob = 0.001, infected_symptoms_prob = 0.001, - recovered_prob = 0.0; - - //Set global infection parameters (similar to infection parameters in SECIR model) and initialize the model - auto model = mio::abm::Model(num_age_groups); - - set_parameters(model.parameters); - - // Create the model object from statistical data. - create_model_from_data(model, input_file, t0, max_num_persons); - model.use_mobility_rules(false); - - // Assign an infection state to each person. - assign_infection_state(model, t0, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); - - auto sim = mio::abm::Simulation(t0, std::move(model)); - return sim; -} - -template -void write_log_to_file_person_and_location_data(const T& history) -{ - auto logg = history.get_log(); - auto loc_id = std::get<0>(logg)[0]; - auto agent_id = std::get<1>(logg)[0]; - // Write lo to a text file. - std::ofstream myfile("locations_lookup.txt"); - myfile << "location_id, location_type, latitude, longitude\n"; - for (uint32_t loc_id_index = 0; loc_id_index < loc_id.size(); ++loc_id_index) { - auto id = std::get<0>(loc_id[loc_id_index]); - auto location_type = (int)std::get<1>(loc_id[loc_id_index]); - auto id_longitute = std::get<2>(loc_id[loc_id_index]).longitude; - auto id_latitude = std::get<2>(loc_id[loc_id_index]).latitude; - myfile << id << ", " << location_type << ", " << id_longitute << ", " << id_latitude << "\n"; - } - myfile.close(); - - std::ofstream myfile2("agents_lookup.txt"); - myfile2 << "agent_id, home_id, age\n"; - for (uint32_t agent_id_index = 0; agent_id_index < agent_id.size(); ++agent_id_index) { - auto id = std::get<0>(agent_id[agent_id_index]); - auto home_id = std::get<1>(agent_id[agent_id_index]); - auto age = std::get<2>(agent_id[agent_id_index]); - myfile2 << id << ", " << home_id << ", " << age << "\n"; - } - myfile2.close(); -} - -template -void write_log_to_file_trip_data(const T& history) -{ - - auto mobility_data = std::get<0>(history.get_log()); - std::ofstream myfile3("mobility_data.txt"); - myfile3 << "agent_id, trip_id, start_location, end_location, start_time, end_time, transport_mode, activity, " - "infection_state \n"; - int trips_id = 0; - for (uint32_t mobility_data_index = 2; mobility_data_index < mobility_data.size(); ++mobility_data_index) { - myfile3 << "timestep Nr.: " << mobility_data_index - 1 << "\n"; - for (uint32_t trip_index = 0; trip_index < mobility_data[mobility_data_index].size(); trip_index++) { - auto agent_id = std::get<0>(mobility_data[mobility_data_index][trip_index]); - - int start_index = mobility_data_index - 1; - using Type = std::tuple; - while (!std::binary_search(std::begin(mobility_data[start_index]), std::end(mobility_data[start_index]), - mobility_data[mobility_data_index][trip_index], - [](const Type& v1, const Type& v2) { - return std::get<0>(v1) < std::get<0>(v2); - })) { - start_index--; - } - auto start_location_iterator = - std::lower_bound(std::begin(mobility_data[start_index]), std::end(mobility_data[start_index]), - mobility_data[mobility_data_index][trip_index], [](const Type& v1, const Type& v2) { - return std::get<0>(v1) < std::get<0>(v2); - }); - auto start_location = (int)std::get<1>(*start_location_iterator).get(); - - auto end_location = (int)std::get<1>(mobility_data[mobility_data_index][trip_index]).get(); - - auto start_time = (int)std::get<2>(mobility_data[mobility_data_index][trip_index]).seconds(); - auto end_time = (int)std::get<2>(mobility_data[mobility_data_index][trip_index]).seconds(); - - auto transport_mode = (int)std::get<3>(mobility_data[mobility_data_index][trip_index]); - auto activity = (int)std::get<4>(mobility_data[mobility_data_index][trip_index]); - auto infection_state = (int)std::get<5>(mobility_data[mobility_data_index][trip_index]); - myfile3 << agent_id << ", " << trips_id << ", " << start_location << " , " << end_location << " , " - << start_time << " , " << end_time << " , " << transport_mode << " , " << activity << " , " - << infection_state << "\n"; - trips_id++; - } - } - myfile3.close(); -} - -mio::IOResult run(const std::string& input_file, const fs::path& result_dir, size_t num_runs, - bool save_single_runs = true) -{ - - auto t0 = mio::abm::TimePoint(0); // Start time per simulation - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(2); // End time per simulation - auto ensemble_results = std::vector>>{}; // Vector of collected results - ensemble_results.reserve(size_t(num_runs)); - auto run_idx = size_t(1); // The run index - auto save_result_result = mio::IOResult(mio::success()); // Variable informing over successful IO operations - auto max_num_persons = 1000; - - // Loop over a number of runs - while (run_idx <= num_runs) { - - // Create the sampled simulation with start time t0. - auto sim = create_sampled_simulation(input_file, t0, max_num_persons); - //output object - mio::History - historyPersonInf; - mio::History historyTimeSeries{ - Eigen::Index(mio::abm::InfectionState::Count)}; - mio::History historyPersonInfDelta; - // Collect the id of location in model. - std::vector loc_ids; - for (auto& location : sim.get_model().get_locations()) { - loc_ids.push_back(location.get_id().get()); - } - // Advance the model to tmax - sim.advance(tmax, historyPersonInf, historyTimeSeries, historyPersonInfDelta); - // TODO: update result of the simulation to be a vector of location result. - auto temp_sim_result = std::vector>{std::get<0>(historyTimeSeries.get_log())}; - // Push result of the simulation back to the result vector - ensemble_results.push_back(temp_sim_result); - // Option to save the current run result to file - if (save_result_result && save_single_runs) { - auto result_dir_run = result_dir / ("abm_result_run_" + std::to_string(run_idx) + ".h5"); - save_result_result = save_result(ensemble_results.back(), loc_ids, 1, result_dir_run.string()); - } - write_log_to_file_person_and_location_data(historyPersonInf); - write_log_to_file_trip_data(historyPersonInfDelta); - ++run_idx; - } - BOOST_OUTCOME_TRY(save_result_result); - return mio::success(); -} - -int main(int argc, char** argv) -{ - mio::set_log_level(mio::LogLevel::warn); - - std::string result_dir = "."; - std::string input_file = ""; - size_t num_runs; - bool save_single_runs = true; - - if (argc == 2) { - num_runs = atoi(argv[1]); - printf("Number of run is %s.\n", argv[1]); - printf("Saving results to the current directory.\n"); - } - - else if (argc == 3) { - num_runs = atoi(argv[1]); - result_dir = argv[2]; - printf("Number of run is %s.\n", argv[1]); - printf("Saving results to \"%s\".\n", result_dir.c_str()); - } - else { - printf("Usage:\n"); - printf("abm_example \n"); - printf("\tRun the simulation for time(s).\n"); - printf("\tStore the results in the current directory.\n"); - printf("abm_braunschweig \n"); - printf("\tRun the simulation for time(s).\n"); - printf("\tStore the results in .\n"); - printf("Running with number of runs = 1.\n"); - num_runs = 1; - } - - // mio::thread_local_rng().seed({...}); //set seeds, e.g., for debugging - //printf("Seeds: "); - //for (auto s : mio::thread_local_rng().get_seeds()) { - // printf("%u, ", s); - //} - //printf("\n"); - - auto result = run(input_file, result_dir, num_runs, save_single_runs); - if (!result) { - printf("%s\n", result.error().formatted_message().c_str()); - return -1; - } - return 0; -} From b373350b320cff8372c14c2241218eed4d9747a4 Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 12:43:35 +0200 Subject: [PATCH 02/10] Replace abm_history_example with abm_example in CMakeLists.txt and add abm.cpp implementation --- cpp/examples/CMakeLists.txt | 6 +++--- cpp/examples/{abm_history_object.cpp => abm.cpp} | 0 cpp/examples/abm_maximal.cpp | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename cpp/examples/{abm_history_object.cpp => abm.cpp} (100%) delete mode 100644 cpp/examples/abm_maximal.cpp diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index caae9380ce..62b748cd32 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -100,9 +100,9 @@ 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_history_example abm_history_object.cpp) -target_link_libraries(abm_history_example PRIVATE memilio abm) -target_compile_options(abm_history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(abm_example abm.cpp) +target_link_libraries(abm_example PRIVATE memilio abm) +target_compile_options(abm_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ide_seir_example ide_seir.cpp) target_link_libraries(ide_seir_example PRIVATE memilio ide_seir) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm.cpp similarity index 100% rename from cpp/examples/abm_history_object.cpp rename to cpp/examples/abm.cpp diff --git a/cpp/examples/abm_maximal.cpp b/cpp/examples/abm_maximal.cpp deleted file mode 100644 index e69de29bb2..0000000000 From ecae5eefa0b3677654bfa651f2f1012fa0d30586 Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 12:45:45 +0200 Subject: [PATCH 03/10] Remove abm_simulation and abm_braunschweig executables from CMakeLists.txt --- cpp/simulations/CMakeLists.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index a784b83ba5..155940f118 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -7,15 +7,7 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES}) target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - add_executable(abm_simulation abm.cpp) - target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES}) - target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - add_executable(munich_graph_sim munich_graph_sim) target_link_libraries(munich_graph_sim PRIVATE memilio ode_secir abm Boost::filesystem ${HDF5_C_LIBRARIES}) target_compile_options(munich_graph_sim PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - - add_executable(abm_braunschweig abm_braunschweig.cpp) - target_link_libraries(abm_braunschweig PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES}) - target_compile_options(abm_braunschweig PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() From 0571babae5f6092966d6ba191315d8c5951bcf6c Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 13:51:52 +0200 Subject: [PATCH 04/10] restrtucture abm example --- cpp/examples/abm.cpp | 685 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 560 insertions(+), 125 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index f228e56604..2fae1edb79 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2020-2025 MEmilio * -* Authors: Khoa Nguyen +* Authors: Daniel Abele, Khoa Nguyen, David Kerkmann * * Contact: Martin J. Kuehn * @@ -17,128 +17,435 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/analyze_result.h" +#include "abm/common_abm_loggers.h" #include "abm/household.h" #include "abm/lockdown_rules.h" -#include "abm/simulation.h" -#include "abm/model.h" -#include "abm/location_type.h" +#include "memilio/config.h" #include "memilio/utils/abstract_parameter_distribution.h" -#include "memilio/io/history.h" +#include "memilio/config.h" +#include "memilio/io/result_io.h" #include "memilio/utils/parameter_distributions.h" +#include "memilio/utils/random_number_generator.h" +#include "memilio/utils/uncertain_value.h" -#include -#include +namespace fs = boost::filesystem; -std::string convert_loc_id_to_string(std::tuple tuple_id) +// Assign the name to general age group. +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); + +/** + * Set a value and distribution of an UncertainValue. + * Assigns average of min and max as a value and UNIFORM(min, max) as a distribution. + * @param p uncertain value to set. + * @param min minimum of distribution. + * @param max minimum of distribution. + */ +void assign_uniform_distribution(mio::UncertainValue<>& p, ScalarType min, ScalarType max) { - return std::to_string(static_cast(std::get<0>(tuple_id))) + "_" + - std::to_string(std::get<1>(tuple_id)); + p = mio::UncertainValue<>(0.5 * (max + min)); + p.set_distribution(mio::ParameterDistributionUniform(min, max)); } -template -void write_log_to_file(const T& history) +/** + * Determine the infection state of a person at the beginning of the simulation. + * The infection states are chosen randomly. They are distributed according to the probabilites set in the example. + * @return random infection state + */ +mio::abm::InfectionState determine_infection_state(mio::abm::PersonalRandomNumberGenerator& rng, ScalarType exposed, + ScalarType infected_no_symptoms, ScalarType infected_symptoms, + ScalarType recovered) { - auto logg = history.get_log(); - // Write the results to a file. - auto loc_id = std::get<1>(logg); - auto time_points = std::get<0>(logg); - std::string input; - std::ofstream myfile("test_output.txt"); - myfile << "Locations as numbers:\n"; - for (auto&& id : loc_id[0]) { - myfile << convert_loc_id_to_string(id) << "\n"; + ScalarType susceptible = 1 - exposed - infected_no_symptoms - infected_symptoms - recovered; + std::vector weights = { + susceptible, exposed, infected_no_symptoms, infected_symptoms / 3, infected_symptoms / 3, + infected_symptoms / 3, recovered}; + if (weights.size() != (size_t)mio::abm::InfectionState::Count - 1) { + mio::log_error("Initialization in ABM wrong, please correct vector length."); } - myfile << "Timepoints:\n"; + auto state = mio::DiscreteDistribution::get_instance()(rng, weights); + return (mio::abm::InfectionState)state; +} - for (auto&& t : time_points) { - input += std::to_string(t) + " "; +/** + * Calculates a vector in which each entry describes the amount of people living in the corresponding household. + * This is done with equal distribution and if the number of people is not divisible by number of households the last one gets the rest. E.g. number_of_people = 10, number_of_households = 3. Then the vector household_sizes = {3,3,4}. + * @param number_of_people The total amount of people to be distributed. + * @param number_of_households The total amount of households. + * @return A vector with the size of each household. + */ +std::vector last_household_gets_the_rest(int number_of_people, int number_of_households) +{ + std::vector household_sizes(number_of_households, 0); + int avarage_household_size_round_down = number_of_people / number_of_households; //int rounds down. + int people_left = number_of_people - + avarage_household_size_round_down * + number_of_households; // People left if everyone got the same rounded down amount of people. + for (auto i = 0; i < number_of_households - 1; i++) { + household_sizes.at(i) = avarage_household_size_round_down; } - myfile << input << "\n"; + household_sizes.at(number_of_households - 1) = + avarage_household_size_round_down + people_left; // Last one gets the people which would've been left out. + return household_sizes; +} + +/** + * Constructs a household group which has a single member to represent them all, e.g. all people have the same age distribution. + * @param age_dist A vector with the amount of people in each age group + * @param number_of_people The total amount of people living in this household group. + * @param number_of_hh The number of households in this household group. + * @return householdGroup A Class Household Group. + */ +mio::abm::HouseholdGroup make_uniform_households(const mio::abm::HouseholdMember& member, int number_of_people, + int number_of_hh) +{ + + // The size of each household is calculated in a vector household_size_list. + auto households_size_list = last_household_gets_the_rest(number_of_people, number_of_hh); + + auto householdGroup = mio::abm::HouseholdGroup(); + for (auto& household_size : households_size_list) { + auto household = mio::abm::Household(); + household.add_members(member, household_size); // Add members according to the amount of people in the list. + householdGroup.add_households(household, 1); // Add the household to the household group. - myfile.close(); + // assuming 22 square meters per person and 3 meters of room height + // see: https://doi.org/10.1371/journal.pone.0259037 + household.set_space_per_member(66); + } + return householdGroup; } -int main() +/** + * Constructs a household group with families. + * @param child Child Household Member. + * @param parent Parent Household Member. + * @param random Random Household Member. This is for the rest Group where no exact age distribution can be found. + * @param number_of_persons_in_household Amount of people in this household + * @param number_of_full_familes Amount of full families, e.g. two parents and (number_of_persons_in_household - 2) children. + * @param number_of_half_familes Amount of half families, e.g. one parent and (number_of_persons_in_household - 1) children. + * @param number_of_other_familes number_of_persons_in_household random persons. + * @return A Household group. + */ +mio::abm::HouseholdGroup make_homes_with_families(const mio::abm::HouseholdMember& child, + const mio::abm::HouseholdMember& parent, + const mio::abm::HouseholdMember& random, + int number_of_persons_in_household, int number_of_full_familes, + int number_of_half_familes, int number_of_other_familes) { - mio::set_log_level(mio::LogLevel::warn); - // This is a minimal example with children and adults < 60y. - // We divided them into 4 different age groups, which are defined as follows: - const size_t num_age_groups = 4; - 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); - - // Create the model with 4 age groups. - auto model = mio::abm::Model(num_age_groups); - mio::ParameterDistributionLogNormal log_norm(4., 1.); - // Set same infection parameter for all age groups. For example, the incubation period is log normally distributed with parameters 4 and 1. - model.parameters.get() = mio::ParameterDistributionLogNormal(4., 1.); - // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) - model.parameters.get()[age_group_5_to_14] = true; - // Set the age group the can go to work is 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); + auto private_household_group = mio::abm::HouseholdGroup(); + + // Add full families. + auto household_full = mio::abm::Household(); + household_full.add_members(child, number_of_persons_in_household - 2); + household_full.add_members(parent, 2); + private_household_group.add_households(household_full, number_of_full_familes); + + // Add half families. + auto household_half = mio::abm::Household(); + household_half.add_members(child, number_of_persons_in_household - 1); + household_half.add_members(parent, 1); + private_household_group.add_households(household_half, number_of_half_familes); + + // Add other families. + if (number_of_persons_in_household < 5) { + auto household_others = mio::abm::Household(); + household_others.add_members(random, number_of_persons_in_household); + private_household_group.add_households(household_others, number_of_other_familes); + } + else if (number_of_persons_in_household == 5) { + // For 5 and more people in one household we have to distribute the rest onto the left over households. + int people_left_size5 = 545; + + auto households_size_list = last_household_gets_the_rest(people_left_size5, number_of_other_familes); + + auto household_rest = mio::abm::HouseholdGroup(); + for (auto& household_size : households_size_list) { + auto household = mio::abm::Household(); + household.add_members(random, household_size); // Add members according to the amount of people in the list. + household_rest.add_households(household, 1); // Add the household to the household group. + } + } + return private_household_group; +} + +void create_model_from_statistical_data(mio::abm::Model& model) +{ + + /** The data is taken from + * https://www-genesis.destatis.de/genesis/online?operation=statistic&levelindex=0&levelid=1627908577036&code=12211#abreadcrumb + * All numbers are in 1000. + * Destatis divides the Households into community households and private households. + * Community Households are: Refugee, Disabled, Retirement and Others. We have an explicit age distribution, amount of households and amount of people for them but not the exact amount of people in each household. + * The private Households are divided with respect to the amount of people living in each household. For a one person household we have the exact age distribution. For the rest we have data about which kind of family lives in them. The different kinds of families are: A family with two parents and the rest are children, a family with one parent and the rest are children and "other" families with no exact data about their age. + */ + + // Refugee + auto refugee = mio::abm::HouseholdMember(num_age_groups); + refugee.set_age_weight(age_group_0_to_4, 25); + refugee.set_age_weight(age_group_5_to_14, 12); + refugee.set_age_weight(age_group_15_to_34, 25); + refugee.set_age_weight(age_group_35_to_59, 9); + refugee.set_age_weight(age_group_60_to_79, 1); + refugee.set_age_weight(age_group_80_plus, 1); + int refugee_number_of_people = 74; + int refugee_number_of_households = 12; + auto refugeeGroup = make_uniform_households(refugee, refugee_number_of_people, refugee_number_of_households); + + add_household_group_to_model(model, refugeeGroup); + + // Disabled + auto disabled = mio::abm::HouseholdMember(num_age_groups); + disabled.set_age_weight(age_group_0_to_4, 2); + disabled.set_age_weight(age_group_5_to_14, 6); + disabled.set_age_weight(age_group_15_to_34, 13); + disabled.set_age_weight(age_group_35_to_59, 42); + disabled.set_age_weight(age_group_60_to_79, 97); + disabled.set_age_weight(age_group_80_plus, 32); + int disabled_number_of_people = 194; + int disabled_number_of_households = 8; + + auto disabledGroup = make_uniform_households(disabled, disabled_number_of_people, disabled_number_of_households); + + add_household_group_to_model(model, disabledGroup); + + // Retirement + auto retired = mio::abm::HouseholdMember(num_age_groups); + retired.set_age_weight(age_group_15_to_34, 1); + retired.set_age_weight(age_group_35_to_59, 30); + retired.set_age_weight(age_group_60_to_79, 185); + retired.set_age_weight(age_group_80_plus, 530); + int retirement_number_of_people = 744; + int retirement_number_of_households = 16; + + auto retirementGroup = + make_uniform_households(retired, retirement_number_of_people, retirement_number_of_households); - // There are 3 households for each household group. - int n_households = 3; + add_household_group_to_model(model, retirementGroup); + + // Others + auto other = mio::abm::HouseholdMember(num_age_groups); + other.set_age_weight(age_group_0_to_4, 30); + other.set_age_weight(age_group_5_to_14, 40); + other.set_age_weight(age_group_15_to_34, 72); + other.set_age_weight(age_group_35_to_59, 40); + other.set_age_weight(age_group_60_to_79, 30); + other.set_age_weight(age_group_80_plus, 10); + int others_number_of_people = 222; + int others_number_of_households = 20; + + auto otherGroup = make_uniform_households(other, others_number_of_people, others_number_of_households); + + add_household_group_to_model(model, otherGroup); + + // One Person Household (we have exact age data about this) + auto one_person_household_member = mio::abm::HouseholdMember(num_age_groups); + one_person_household_member.set_age_weight(age_group_15_to_34, 4364); + one_person_household_member.set_age_weight(age_group_35_to_59, 7283); + one_person_household_member.set_age_weight(age_group_60_to_79, 4100); + one_person_household_member.set_age_weight(age_group_80_plus, 1800); + int one_person_number_of_people = 15387; + int one_person_number_of_households = 15387; + + auto onePersonGroup = make_uniform_households(one_person_household_member, one_person_number_of_people, + one_person_number_of_households); + + add_household_group_to_model(model, onePersonGroup); // For more than 1 family households we need families. These are parents and children and randoms (which are distributed like the data we have for these households). auto child = mio::abm::HouseholdMember(num_age_groups); // A child is 50/50% 0-4 or 5-14. child.set_age_weight(age_group_0_to_4, 1); child.set_age_weight(age_group_5_to_14, 1); - auto parent = mio::abm::HouseholdMember(num_age_groups); // A parent is 50/50% 15-34 or 35-59. - parent.set_age_weight(age_group_15_to_34, 1); - parent.set_age_weight(age_group_35_to_59, 1); - - // Two-person household with one parent and one child. - auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); - auto twoPersonHousehold_full = mio::abm::Household(); - twoPersonHousehold_full.add_members(child, 1); - twoPersonHousehold_full.add_members(parent, 1); - twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); - add_household_group_to_model(model, twoPersonHousehold_group); - - // Three-person household with two parent and one child. - auto threePersonHousehold_group = mio::abm::HouseholdGroup(); - auto threePersonHousehold_full = mio::abm::Household(); - threePersonHousehold_full.add_members(child, 1); - threePersonHousehold_full.add_members(parent, 2); - threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); - add_household_group_to_model(model, threePersonHousehold_group); - - // Add one social event with 5 maximum contacts. + auto parent = mio::abm::HouseholdMember(num_age_groups); // A child is 40/40/20% 15-34, 35-59 or 60-79. + parent.set_age_weight(age_group_15_to_34, 2); + parent.set_age_weight(age_group_35_to_59, 2); + parent.set_age_weight(age_group_60_to_79, 1); + + auto random = + mio::abm::HouseholdMember(num_age_groups); // Randoms are distributed according to the left over persons. + random.set_age_weight(age_group_0_to_4, 5000); + random.set_age_weight(age_group_5_to_14, 6000); + random.set_age_weight(age_group_15_to_34, 14943); + random.set_age_weight(age_group_35_to_59, 22259); + random.set_age_weight(age_group_60_to_79, 11998); + random.set_age_weight(age_group_80_plus, 5038); + + // Two person households + int two_person_full_families = 11850; + int two_person_half_families = 1765; + int two_person_other_families = 166; + auto twoPersonHouseholds = make_homes_with_families(child, parent, random, 2, two_person_full_families, + two_person_half_families, two_person_other_families); + add_household_group_to_model(model, twoPersonHouseholds); + + // Three person households + int three_person_full_families = 4155; + int three_person_half_families = 662; + int three_person_other_families = 175; + auto threePersonHouseholds = make_homes_with_families(child, parent, random, 3, three_person_full_families, + three_person_half_families, three_person_other_families); + add_household_group_to_model(model, threePersonHouseholds); + + // Four person households + int four_person_full_families = 3551; + int four_person_half_families = 110; + int four_person_other_families = 122; + auto fourPersonHouseholds = make_homes_with_families(child, parent, random, 4, four_person_full_families, + four_person_half_families, four_person_other_families); + add_household_group_to_model(model, fourPersonHouseholds); + + // Five plus person households + int fiveplus_person_full_families = 1245; + int fiveplus_person_half_families = 80; + int fiveplus_person_other_families = 82; + auto fivePlusPersonHouseholds = + make_homes_with_families(child, parent, random, 5, fiveplus_person_full_families, fiveplus_person_half_families, + fiveplus_person_other_families); + add_household_group_to_model(model, fivePlusPersonHouseholds); +} + +/** + * Add locations to the model and assign locations to the people. + */ +void create_assign_locations_and_testing_schemes(mio::abm::Model& model) +{ + // Add one social event with 100 maximum contacts. // Maximum contacs limit the number of people that a person can infect while being at this location. + // A high percentage of people (50-100%) have to get tested in the 2 days before the event + // For the capacity we assume an area of 1.25 m^2 per person (https://doi.org/10.1371/journal.pone.0259037) and a + // room height of 3 m auto event = model.add_location(mio::abm::LocationType::SocialEvent); - model.get_location(event).get_infection_parameters().set(5); + model.get_location(event).get_infection_parameters().set(100); + model.get_location(event).set_capacity(100, 375); + + auto testing_criteria = mio::abm::TestingCriteria(); + auto validity_period = mio::abm::days(2); + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(60); + + auto probability = mio::UncertainValue<>(); + assign_uniform_distribution(probability, 0.5, 1.0); + + auto test_params = model.parameters.get()[mio::abm::TestType::Antigen]; + auto testing_scheme = mio::abm::TestingScheme(testing_criteria, validity_period, start_date, end_date, test_params, + probability.draw_sample()); + + model.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::SocialEvent, testing_scheme); + // Add hospital and ICU with 5 maximum contacs. + // For the number of agents in this example we assume a capacity of 584 persons (80 beds per 10000 residents in + // Germany (Statistisches Bundesamt, 2022) and a volume of 26242 m^3 + // (https://doi.org/10.1016/j.buildenv.2021.107926)) + // For the ICUs we assume a capacity of 30 agents and the same volume. auto hospital = model.add_location(mio::abm::LocationType::Hospital); model.get_location(hospital).get_infection_parameters().set(5); + model.get_location(hospital).set_capacity(584, 26242); 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. + model.get_location(icu).set_capacity(30, 1350); + + // Add schools, workplaces and shops. + // At every school there are 600 students. The maximum contacs are 40. + // Students have to get tested once a week. + // We assume 2 m^2 per student (https://doi.org/10.1371/journal.pone.0259037) and a room height of 3 m. + // At every workplace work 100 people (needs to be varified), maximum contacts are 40. + // People can get tested at work (and do this with 0.5 probability). + // Per person we assume an area of 10 m^2 (https://doi.org/10.1371/journal.pone.0259037) and a room height of 3 m. + // Add one supermarked per 15.000 people, maximum constacts are assumed to be 20. + // A shop has a capacity of 240 persons (https://doi.org/10.1016/j.buildenv.2021.107926) + // and a volume of 7200 cubic meters (10 m^2 per person (https://doi.org/10.1371/journal.pone.0259037) and 3 m + // room height). 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. + model.get_location(shop).set_capacity(240, 7200); + auto school = model.add_location(mio::abm::LocationType::School); - model.get_location(school).get_infection_parameters().set(20); - // At every workplace, maximum contacts are 10. + model.get_location(school).get_infection_parameters().set(40); + model.get_location(school).set_capacity(600, 3600); + auto work = model.add_location(mio::abm::LocationType::Work); - model.get_location(work).get_infection_parameters().set(10); - - // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. - auto validity_period = mio::abm::days(1); - auto probability = 0.5; - auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - auto test_type = mio::abm::TestType::Antigen; - auto test_parameters = model.parameters.get()[test_type]; + model.get_location(work).get_infection_parameters().set(40); + model.get_location(work).set_capacity(100, 3000); + + int counter_event = 0; + int counter_school = 0; + int counter_work = 0; + int counter_shop = 0; + //Assign locations to the people + auto persons = model.get_persons(); + for (auto& person : persons) { + const auto id = person.get_id(); + //assign shop and event + model.assign_location(id, event); + counter_event++; + model.assign_location(id, shop); + counter_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); + counter_school++; + } + if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { + model.assign_location(id, work); + counter_work++; + } + //add new school/work/shop if needed + if (counter_event == 1000) { + counter_event = 0; + event = model.add_location(mio::abm::LocationType::SocialEvent); + model.get_location(event).set_capacity(100, 375); + model.get_location(event).get_infection_parameters().set(100); + } + if (counter_school == 600) { + counter_school = 0; + school = model.add_location(mio::abm::LocationType::School); + model.get_location(school).get_infection_parameters().set(40); + model.get_location(school).set_capacity(600, 3600); + } + if (counter_work == 100) { + counter_work = 0; + work = model.add_location(mio::abm::LocationType::Work); + model.get_location(work).get_infection_parameters().set(40); + model.get_location(work).set_capacity(100, 3000); + } + if (counter_shop == 15000) { + counter_shop = 0; + shop = model.add_location(mio::abm::LocationType::BasicsShop); + model.get_location(shop).get_infection_parameters().set(20); + model.get_location(shop).set_capacity(240, 7200); + } + } + + // add the testing schemes for school and work + auto testing_criteria_school = mio::abm::TestingCriteria(); + validity_period = mio::abm::days(7); + auto testing_scheme_school = mio::abm::TestingScheme(testing_criteria_school, validity_period, start_date, end_date, + test_params, probability.draw_sample()); + model.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::School, testing_scheme_school); + + auto test_at_work = std::vector{mio::abm::LocationType::Work}; auto testing_criteria_work = mio::abm::TestingCriteria(); auto testing_scheme_work = mio::abm::TestingScheme(testing_criteria_work, validity_period, start_date, end_date, - test_parameters, probability); + test_params, probability.draw_sample()); model.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::Work, testing_scheme_work); +} +/** + * Assign an infection state to each person. + */ +void assign_infection_state_masks_and_compliance(mio::abm::Model& model) +{ for (auto& person : model.get_persons()) { auto prng = mio::abm::PersonalRandomNumberGenerator(person); //some % of people are infected, large enough to have some infection activity without everyone dying @@ -167,55 +474,183 @@ int main() loc.set_required_mask(mio::abm::MaskType::Community); } } +} - // Assign locations to the people - for (auto& person : model.get_persons()) { - const auto pid = person.get_id(); - //assign shop and event - model.assign_location(pid, event); - model.assign_location(pid, shop); - //assign hospital and ICU - model.assign_location(pid, hospital); - model.assign_location(pid, icu); - //assign work/school to people depending on their age - if (person.get_age() == age_group_5_to_14) { - model.assign_location(pid, school); - } - if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - model.assign_location(pid, work); - } +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 + + // Incubation period (Exposed to No Symptoms) + auto incubation_period_my_sigma = get_my_and_sigma({4.5, 1.5}); + params.get() = + mio::ParameterDistributionLogNormal(incubation_period_my_sigma.first, incubation_period_my_sigma.second); + + // Infected No Symptoms to Symptoms + auto infected_no_symptoms_to_symptoms_my_sigma = get_my_and_sigma({1.1, 0.9}); + params.get() = mio::ParameterDistributionLogNormal( + infected_no_symptoms_to_symptoms_my_sigma.first, infected_no_symptoms_to_symptoms_my_sigma.second); + + // Infected No Symptoms to Recovered + auto infected_no_symptoms_to_recovered_my_sigma = get_my_and_sigma({8.0, 2.0}); + params.get() = mio::ParameterDistributionLogNormal( + infected_no_symptoms_to_recovered_my_sigma.first, infected_no_symptoms_to_recovered_my_sigma.second); + + // Infected Symptoms to Severe + auto infected_symptoms_to_severe_my_sigma = get_my_and_sigma({6.6, 4.9}); + params.get() = mio::ParameterDistributionLogNormal( + infected_symptoms_to_severe_my_sigma.first, infected_symptoms_to_severe_my_sigma.second); + + // Infected Symptoms to Recovered + auto infected_symptoms_to_recovered_my_sigma = get_my_and_sigma({8.0, 2.0}); + params.get() = mio::ParameterDistributionLogNormal( + infected_symptoms_to_recovered_my_sigma.first, infected_symptoms_to_recovered_my_sigma.second); + + // Infected Severe to Critical + auto infected_severe_to_critical_my_sigma = get_my_and_sigma({1.5, 2.0}); + params.get() = mio::ParameterDistributionLogNormal( + infected_severe_to_critical_my_sigma.first, infected_severe_to_critical_my_sigma.second); + + // Infected Severe to Recovered + auto infected_severe_to_recovered_my_sigma = get_my_and_sigma({18.1, 6.3}); + params.get() = mio::ParameterDistributionLogNormal( + infected_severe_to_recovered_my_sigma.first, infected_severe_to_recovered_my_sigma.second); + + // Infected Critical to Dead + auto infected_critical_to_dead_my_sigma = get_my_and_sigma({10.7, 4.8}); + params.get() = mio::ParameterDistributionLogNormal( + infected_critical_to_dead_my_sigma.first, infected_critical_to_dead_my_sigma.second); + + // Infected Critical to Recovered + auto infected_critical_to_recovered_my_sigma = get_my_and_sigma({18.1, 6.3}); + params.get() = mio::ParameterDistributionLogNormal( + infected_critical_to_recovered_my_sigma.first, infected_critical_to_recovered_my_sigma.second); + + // Set percentage parameters (unchanged) + 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 other parameters + params.get() = 0.0; +} +/** + * Create a sampled model with start time t0. + * @param t0 the start time of the simulation +*/ +mio::abm::Model create_sampled_model() +{ + + // Set the random number generator seed + mio::thread_local_rng().seed({123144124, 835345345, 123123123, 99123}); //set seeds, e.g., for debugging + + // mio::thread_local_rng().seed( + // {123144124, 835345345, 123123123, 99123}); //set seeds, e.g., for debugging + printf("Parameter Sample Seeds: "); + for (auto s : mio::thread_local_rng().get_seeds()) { + printf("%u, ", s); + } + printf("\n"); + + //Set global infection parameters (similar to infection parameters in SECIR model) and initialize the model + auto model = mio::abm::Model(num_age_groups); + + set_parameters(model.parameters); + + // model.get_rng().seed( + // {23144124, 1835345345, 9343763, 9123}); //set seeds, e.g., for debugging + printf("ABM Simulation Seeds: "); + for (auto s : model.get_rng().get_seeds()) { + printf("%u, ", s); } + printf("\n"); - // During the lockdown, social events are closed for 90% of people. - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); + // Create the model object from statistical data. + create_model_from_statistical_data(model); + + // Assign an infection state to each person. + assign_infection_state_masks_and_compliance(model); + + // Add locations and assign locations to the people. + create_assign_locations_and_testing_schemes(model); + + auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(20); + + // During the lockdown, 25% of people work from home and schools are closed for 90% of students. + // Social events are very rare. + mio::abm::set_home_office(t_lockdown, 0.25, model.parameters); + mio::abm::set_school_closure(t_lockdown, 0.9, model.parameters); mio::abm::close_social_events(t_lockdown, 0.9, model.parameters); + return model; +} + +/** + * Run the ABM simulation. + * @param result_dir Directory where all results will be stored. + * @param num_runs Number of runs. + * @param save_single_runs If true, single run results are written to disk. + */ +void run() +{ auto t0 = mio::abm::TimePoint(0); - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); - auto sim = mio::abm::Simulation(t0, std::move(model)); - - struct LogTimePoint : mio::LogAlways { - using Type = double; - static Type log(const mio::abm::Simulation<>& sim) - { - return sim.get_time().hours(); - } - }; - struct LogLocationIds : mio::LogOnce { - using Type = std::vector>; - static Type log(const mio::abm::Simulation<>& sim) - { - Type location_ids{}; - for (auto& location : sim.get_model().get_locations()) { - location_ids.push_back(std::make_tuple(location.get_type(), location.get_id().get())); - } - return location_ids; - } - }; + auto tmax = t0 + mio::abm::days(60); + + // Create the sampled simulation with start time t0 + auto model = create_sampled_model(); - mio::History history; + auto sim = mio::abm::Simulation(t0, std::move(model)); + mio::History historyTimeSeries{ + Eigen::Index(mio::abm::InfectionState::Count)}; + sim.advance(tmax, historyTimeSeries); + + std::ofstream outfile("abm.txt"); + std::get<0>(historyTimeSeries.get_log()) + .print_table({"S", "E", "I_NS", "I_Sy", "I_Sev", "I_Crit", "R", "D"}, 7, 4, outfile); + std::cout << "Results written to abm.txt" << std::endl; +} - sim.advance(tmax, history); +int main() +{ + mio::set_log_level(mio::LogLevel::warn); - write_log_to_file(history); + std::cout << "Starting ABM simulation..." << std::endl; + run(); + std::cout << "ABM simulation finished." << std::endl; + return 0; } From 9eb941d124a5570e6c75df542b8737d70b0af352 Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 14:20:03 +0200 Subject: [PATCH 05/10] Overhaul abm example --- cpp/examples/abm.cpp | 114 ++++++++++++------------------------------- 1 file changed, 32 insertions(+), 82 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 2fae1edb79..bfcf29c5b9 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -187,72 +187,14 @@ void create_model_from_statistical_data(mio::abm::Model& model) * The private Households are divided with respect to the amount of people living in each household. For a one person household we have the exact age distribution. For the rest we have data about which kind of family lives in them. The different kinds of families are: A family with two parents and the rest are children, a family with one parent and the rest are children and "other" families with no exact data about their age. */ - // Refugee - auto refugee = mio::abm::HouseholdMember(num_age_groups); - refugee.set_age_weight(age_group_0_to_4, 25); - refugee.set_age_weight(age_group_5_to_14, 12); - refugee.set_age_weight(age_group_15_to_34, 25); - refugee.set_age_weight(age_group_35_to_59, 9); - refugee.set_age_weight(age_group_60_to_79, 1); - refugee.set_age_weight(age_group_80_plus, 1); - int refugee_number_of_people = 74; - int refugee_number_of_households = 12; - auto refugeeGroup = make_uniform_households(refugee, refugee_number_of_people, refugee_number_of_households); - - add_household_group_to_model(model, refugeeGroup); - - // Disabled - auto disabled = mio::abm::HouseholdMember(num_age_groups); - disabled.set_age_weight(age_group_0_to_4, 2); - disabled.set_age_weight(age_group_5_to_14, 6); - disabled.set_age_weight(age_group_15_to_34, 13); - disabled.set_age_weight(age_group_35_to_59, 42); - disabled.set_age_weight(age_group_60_to_79, 97); - disabled.set_age_weight(age_group_80_plus, 32); - int disabled_number_of_people = 194; - int disabled_number_of_households = 8; - - auto disabledGroup = make_uniform_households(disabled, disabled_number_of_people, disabled_number_of_households); - - add_household_group_to_model(model, disabledGroup); - - // Retirement - auto retired = mio::abm::HouseholdMember(num_age_groups); - retired.set_age_weight(age_group_15_to_34, 1); - retired.set_age_weight(age_group_35_to_59, 30); - retired.set_age_weight(age_group_60_to_79, 185); - retired.set_age_weight(age_group_80_plus, 530); - int retirement_number_of_people = 744; - int retirement_number_of_households = 16; - - auto retirementGroup = - make_uniform_households(retired, retirement_number_of_people, retirement_number_of_households); - - add_household_group_to_model(model, retirementGroup); - - // Others - auto other = mio::abm::HouseholdMember(num_age_groups); - other.set_age_weight(age_group_0_to_4, 30); - other.set_age_weight(age_group_5_to_14, 40); - other.set_age_weight(age_group_15_to_34, 72); - other.set_age_weight(age_group_35_to_59, 40); - other.set_age_weight(age_group_60_to_79, 30); - other.set_age_weight(age_group_80_plus, 10); - int others_number_of_people = 222; - int others_number_of_households = 20; - - auto otherGroup = make_uniform_households(other, others_number_of_people, others_number_of_households); - - add_household_group_to_model(model, otherGroup); - // One Person Household (we have exact age data about this) auto one_person_household_member = mio::abm::HouseholdMember(num_age_groups); one_person_household_member.set_age_weight(age_group_15_to_34, 4364); one_person_household_member.set_age_weight(age_group_35_to_59, 7283); one_person_household_member.set_age_weight(age_group_60_to_79, 4100); one_person_household_member.set_age_weight(age_group_80_plus, 1800); - int one_person_number_of_people = 15387; - int one_person_number_of_households = 15387; + int one_person_number_of_people = 1538; + int one_person_number_of_households = 1538; auto onePersonGroup = make_uniform_households(one_person_household_member, one_person_number_of_people, one_person_number_of_households); @@ -279,33 +221,33 @@ void create_model_from_statistical_data(mio::abm::Model& model) random.set_age_weight(age_group_80_plus, 5038); // Two person households - int two_person_full_families = 11850; - int two_person_half_families = 1765; - int two_person_other_families = 166; + int two_person_full_families = 1185; + int two_person_half_families = 176; + int two_person_other_families = 16; auto twoPersonHouseholds = make_homes_with_families(child, parent, random, 2, two_person_full_families, two_person_half_families, two_person_other_families); add_household_group_to_model(model, twoPersonHouseholds); // Three person households - int three_person_full_families = 4155; - int three_person_half_families = 662; - int three_person_other_families = 175; + int three_person_full_families = 415; + int three_person_half_families = 66; + int three_person_other_families = 17; auto threePersonHouseholds = make_homes_with_families(child, parent, random, 3, three_person_full_families, three_person_half_families, three_person_other_families); add_household_group_to_model(model, threePersonHouseholds); // Four person households - int four_person_full_families = 3551; - int four_person_half_families = 110; - int four_person_other_families = 122; + int four_person_full_families = 355; + int four_person_half_families = 11; + int four_person_other_families = 12; auto fourPersonHouseholds = make_homes_with_families(child, parent, random, 4, four_person_full_families, four_person_half_families, four_person_other_families); add_household_group_to_model(model, fourPersonHouseholds); // Five plus person households - int fiveplus_person_full_families = 1245; - int fiveplus_person_half_families = 80; - int fiveplus_person_other_families = 82; + int fiveplus_person_full_families = 124; + int fiveplus_person_half_families = 8; + int fiveplus_person_other_families = 8; auto fivePlusPersonHouseholds = make_homes_with_families(child, parent, random, 5, fiveplus_person_full_families, fiveplus_person_half_families, fiveplus_person_other_families); @@ -329,7 +271,7 @@ void create_assign_locations_and_testing_schemes(mio::abm::Model& model) auto testing_criteria = mio::abm::TestingCriteria(); auto validity_period = mio::abm::days(2); auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(60); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(20); auto probability = mio::UncertainValue<>(); assign_uniform_distribution(probability, 0.5, 1.0); @@ -629,20 +571,28 @@ mio::abm::Model create_sampled_model() void run() { auto t0 = mio::abm::TimePoint(0); - auto tmax = t0 + mio::abm::days(60); + auto tmax = t0 + mio::abm::days(20); // Create the sampled simulation with start time t0 auto model = create_sampled_model(); auto sim = mio::abm::Simulation(t0, std::move(model)); - mio::History historyTimeSeries{ - Eigen::Index(mio::abm::InfectionState::Count)}; - sim.advance(tmax, historyTimeSeries); - - std::ofstream outfile("abm.txt"); - std::get<0>(historyTimeSeries.get_log()) - .print_table({"S", "E", "I_NS", "I_Sy", "I_Sev", "I_Crit", "R", "D"}, 7, 4, outfile); - std::cout << "Results written to abm.txt" << std::endl; + sim.advance(tmax); + + // Save the simulation results + Eigen::VectorXd sum = Eigen::VectorXd::Zero(Eigen::Index(mio::abm::InfectionState::Count)); + 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(), tmax, mio::abm::InfectionState(inf_state)); + } + } + auto inf_states = std::vector{"S", "E", "I_NS", "I_Sy", "I_Sev", "I_Crit", "R", "D"}; + std::cout << "Infection states at simulation end:"; + for (int i = 0; i < (int)mio::abm::InfectionState::Count; i++) { + std::cout << " " << inf_states[i] << ": " << sum[i]; + } + std::cout << std::endl; } int main() From 5018328fee99455f445592ae1dac8e4a50de3bc2 Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 14:21:03 +0200 Subject: [PATCH 06/10] delete rngs --- cpp/examples/abm.cpp | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index bfcf29c5b9..71b7786208 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -518,30 +518,10 @@ void set_parameters(mio::abm::Parameters& params) mio::abm::Model create_sampled_model() { - // Set the random number generator seed - mio::thread_local_rng().seed({123144124, 835345345, 123123123, 99123}); //set seeds, e.g., for debugging - - // mio::thread_local_rng().seed( - // {123144124, 835345345, 123123123, 99123}); //set seeds, e.g., for debugging - printf("Parameter Sample Seeds: "); - for (auto s : mio::thread_local_rng().get_seeds()) { - printf("%u, ", s); - } - printf("\n"); - //Set global infection parameters (similar to infection parameters in SECIR model) and initialize the model auto model = mio::abm::Model(num_age_groups); set_parameters(model.parameters); - - // model.get_rng().seed( - // {23144124, 1835345345, 9343763, 9123}); //set seeds, e.g., for debugging - printf("ABM Simulation Seeds: "); - for (auto s : model.get_rng().get_seeds()) { - printf("%u, ", s); - } - printf("\n"); - // Create the model object from statistical data. create_model_from_statistical_data(model); @@ -551,14 +531,6 @@ mio::abm::Model create_sampled_model() // Add locations and assign locations to the people. create_assign_locations_and_testing_schemes(model); - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(20); - - // During the lockdown, 25% of people work from home and schools are closed for 90% of students. - // Social events are very rare. - mio::abm::set_home_office(t_lockdown, 0.25, model.parameters); - mio::abm::set_school_closure(t_lockdown, 0.9, model.parameters); - mio::abm::close_social_events(t_lockdown, 0.9, model.parameters); - return model; } @@ -579,7 +551,7 @@ void run() auto sim = mio::abm::Simulation(t0, std::move(model)); sim.advance(tmax); - // Save the simulation results + // Print the infection states at the end of the simulation Eigen::VectorXd sum = Eigen::VectorXd::Zero(Eigen::Index(mio::abm::InfectionState::Count)); for (auto& location : sim.get_model().get_locations()) { for (uint32_t inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { From bfbd2baf95d5a7e586715a06b2c06ab00c63853f Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 14:53:17 +0200 Subject: [PATCH 07/10] Remove unused namespace alias for boost::filesystem in abm.cpp --- cpp/examples/abm.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 71b7786208..3a2125b31b 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -29,8 +29,6 @@ #include "memilio/utils/random_number_generator.h" #include "memilio/utils/uncertain_value.h" -namespace fs = boost::filesystem; - // Assign the name to general age group. size_t num_age_groups = 6; const auto age_group_0_to_4 = mio::AgeGroup(0); From c4457210eee0e9b4485ae86c65fdd8b6d62b81e0 Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 15:10:05 +0200 Subject: [PATCH 08/10] fix --- cpp/examples/abm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 3a2125b31b..fd3eac6fef 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -400,7 +400,7 @@ void assign_infection_state_masks_and_compliance(mio::abm::Model& model) //equal chance of (moderate) mask refusal and (moderate) mask eagerness auto pct_compliance_values = std::array{0.05 /*0*/, 0.2 /*0.25*/, 0.5 /*0.5*/, 0.2 /*0.75*/, 0.05 /*1*/}; - auto compliance_value = 0.25 * mio::DiscreteDistribution::get_instance()(prng, pct_compliance_values); + auto compliance_value = 0.25 * mio::DiscreteDistribution::get_instance()(prng, pct_compliance_values); person.set_compliance(mio::abm::InterventionType::Mask, compliance_value); } From c0866a8ca54083f72815196bb3be6a10d4c387b5 Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 15:12:27 +0200 Subject: [PATCH 09/10] another fix --- cpp/examples/abm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index fd3eac6fef..2eb2599c74 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -399,8 +399,8 @@ void assign_infection_state_masks_and_compliance(mio::abm::Model& model) } //equal chance of (moderate) mask refusal and (moderate) mask eagerness - auto pct_compliance_values = std::array{0.05 /*0*/, 0.2 /*0.25*/, 0.5 /*0.5*/, 0.2 /*0.75*/, 0.05 /*1*/}; - auto compliance_value = 0.25 * mio::DiscreteDistribution::get_instance()(prng, pct_compliance_values); + auto pct_compliance_values = std::array{5, 20, 50, 20, 5}; + auto compliance_value = 0.0025 * mio::DiscreteDistribution::get_instance()(prng, pct_compliance_values); person.set_compliance(mio::abm::InterventionType::Mask, compliance_value); } From 9a4dead43fa44125f78b6abe6727e25c8dd34d4c Mon Sep 17 00:00:00 2001 From: Sascha <51127093+xsaschako@users.noreply.github.com> Date: Thu, 22 May 2025 16:02:56 +0200 Subject: [PATCH 10/10] fix --- cpp/examples/abm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 2eb2599c74..69fadee7f4 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -399,7 +399,7 @@ void assign_infection_state_masks_and_compliance(mio::abm::Model& model) } //equal chance of (moderate) mask refusal and (moderate) mask eagerness - auto pct_compliance_values = std::array{5, 20, 50, 20, 5}; + auto pct_compliance_values = std::array{0.05 /*0*/, 0.2 /*0.25*/, 0.5 /*0.5*/, 0.2 /*0.75*/, 0.05 /*1*/}; auto compliance_value = 0.0025 * mio::DiscreteDistribution::get_instance()(prng, pct_compliance_values); person.set_compliance(mio::abm::InterventionType::Mask, compliance_value); }