diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 0a16c654d3..109806c08c 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -17,9 +17,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/simulation.h" #include "benchmark/benchmark.h" +#include mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list seeds) { @@ -44,25 +46,28 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list auto age = mio::AgeGroup(mio::UniformIntDistribution::get_instance()( model.get_rng(), size_t(0), model.parameters.get_num_groups() - 1)); - auto person = model.add_person(home, age); - model.assign_location(uint32_t(i), home); + auto person = model.add_person(home, age, mio::abm::ActivityType::Home); + model.assign_location(uint32_t(i), home, mio::abm::ActivityType::Home); home_size++; } //create other locations - for (auto loc_type : - {mio::abm::LocationType::School, mio::abm::LocationType::Work, mio::abm::LocationType::SocialEvent, - mio::abm::LocationType::BasicsShop, mio::abm::LocationType::Hospital, mio::abm::LocationType::ICU}) { + for (auto types : {std::make_pair(mio::abm::LocationType::School, mio::abm::ActivityType::School), + std::make_pair(mio::abm::LocationType::Work, mio::abm::ActivityType::Work), + std::make_pair(mio::abm::LocationType::Recreation, mio::abm::ActivityType::Recreation), + std::make_pair(mio::abm::LocationType::BasicsShop, mio::abm::ActivityType::BasicsShop), + std::make_pair(mio::abm::LocationType::Hospital, mio::abm::ActivityType::Hospital), + std::make_pair(mio::abm::LocationType::ICU, mio::abm::ActivityType::ICU)}) { const auto num_locs = std::max(size_t(1), num_persons / 2'000); std::vector locs(num_locs); std::generate(locs.begin(), locs.end(), [&] { - return model.add_location(loc_type); + return model.add_location(types.first); }); for (size_t p = 0; p < num_persons; ++p) { auto loc_idx = mio::UniformIntDistribution::get_instance()(model.get_rng(), size_t(0), num_locs - 1); - model.assign_location(uint32_t(p), locs[loc_idx]); + model.assign_location(uint32_t(p), locs[loc_idx], types.second); } } @@ -112,7 +117,7 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list }; model.get_testing_strategy().add_scheme( - {mio::abm::LocationType::School, mio::abm::LocationType::Work, mio::abm::LocationType::SocialEvent, + {mio::abm::LocationType::School, mio::abm::LocationType::Work, mio::abm::LocationType::Recreation, mio::abm::LocationType::Home}, mio::abm::TestingScheme(random_criteria(), mio::abm::days(3), mio::abm::TimePoint(0), mio::abm::TimePoint(0) + mio::abm::days(10), {}, 0.5)); diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 4e1c3bf3e2..606fd10b44 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -113,7 +113,7 @@ int main() // Add one social event with 5 maximum contacts. // Maximum contacs limit the number of people that a person can infect while being at this location. - auto event = model.add_location(mio::abm::LocationType::SocialEvent); + auto event = model.add_location(mio::abm::LocationType::Recreation); model.get_location(event).get_infection_parameters().set(5); // Add hospital and ICU with 5 maximum contacs. auto hospital = model.add_location(mio::abm::LocationType::Hospital); @@ -158,17 +158,17 @@ int main() 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); + model.assign_location(pid, event, mio::abm::ActivityType::Recreation); + model.assign_location(pid, shop, mio::abm::ActivityType::BasicsShop); //assign hospital and ICU - model.assign_location(pid, hospital); - model.assign_location(pid, icu); + model.assign_location(pid, hospital, mio::abm::ActivityType::Hospital); + model.assign_location(pid, icu, mio::abm::ActivityType::ICU); //assign work/school to people depending on their age if (person.get_age() == age_group_5_to_14) { - model.assign_location(pid, school); + model.assign_location(pid, school, mio::abm::ActivityType::School); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - model.assign_location(pid, work); + model.assign_location(pid, work, mio::abm::ActivityType::Work); } } diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index d5fbad0413..e79fa735be 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -23,6 +23,7 @@ #include "abm/common_abm_loggers.h" #include "memilio/io/directories.h" +#include #include int main() @@ -80,7 +81,7 @@ int main() // Add one social event with 5 maximum contacts. // Maximum contacs limit the number of people that a person can infect while being at this location. - auto event = model.add_location(mio::abm::LocationType::SocialEvent); + auto event = model.add_location(mio::abm::LocationType::Recreation); model.get_location(event).get_infection_parameters().set(5); // Add hospital and ICU with 5 maximum contacs. auto hospital = model.add_location(mio::abm::LocationType::Hospital); @@ -128,21 +129,39 @@ int main() } } + size_t num_teachers = 0; + size_t num_hosp_doctors = 0; + size_t num_icu_doctors = 0; + // Assign locations to the people for (auto& person : model.get_persons()) { const auto id = person.get_id(); //assign shop and event - model.assign_location(id, event); - model.assign_location(id, shop); + model.assign_location(id, event, mio::abm::ActivityType::Recreation); + model.assign_location(id, shop, mio::abm::ActivityType::BasicsShop); //assign hospital and ICU - model.assign_location(id, hospital); - model.assign_location(id, icu); + model.assign_location(id, hospital, mio::abm::ActivityType::Hospital); + model.assign_location(id, icu, mio::abm::ActivityType::ICU); //assign work/school to people depending on their age if (person.get_age() == age_group_5_to_14) { - model.assign_location(id, school); + model.assign_location(id, school, mio::abm::ActivityType::School); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - model.assign_location(id, work); + if (num_teachers < 3) { + model.assign_location(id, school, mio::abm::ActivityType::Work); + num_teachers++; + } + else if (num_hosp_doctors < 1) { + model.assign_location(id, hospital, mio::abm::ActivityType::Work); + num_hosp_doctors++; + } + else if (num_icu_doctors < 1) { + model.assign_location(id, icu, mio::abm::ActivityType::Work); + num_icu_doctors++; + } + else { + model.assign_location(id, work, mio::abm::ActivityType::Work); + } } } diff --git a/cpp/examples/abm_parameter_study.cpp b/cpp/examples/abm_parameter_study.cpp index 313663ef20..33679ac165 100644 --- a/cpp/examples/abm_parameter_study.cpp +++ b/cpp/examples/abm_parameter_study.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/result_simulation.h" #include "abm/household.h" #include "abm/lockdown_rules.h" @@ -91,7 +92,7 @@ mio::abm::Model make_model(const mio::RandomNumberGenerator& rng) // Add one social event with 5 maximum contacts. // Maximum contacts limit the number of people that a person can infect while being at this location. - auto event = model.add_location(mio::abm::LocationType::SocialEvent); + auto event = model.add_location(mio::abm::LocationType::Recreation); model.get_location(event).get_infection_parameters().set(5); // Add hospital and ICU with 5 maximum contacs. auto hospital = model.add_location(mio::abm::LocationType::Hospital); @@ -143,17 +144,17 @@ mio::abm::Model make_model(const mio::RandomNumberGenerator& rng) for (auto& person : model.get_persons()) { const auto id = person.get_id(); //assign shop and event - model.assign_location(id, event); - model.assign_location(id, shop); + model.assign_location(id, event, mio::abm::ActivityType::Recreation); + model.assign_location(id, shop, mio::abm::ActivityType::BasicsShop); //assign hospital and ICU - model.assign_location(id, hospital); - model.assign_location(id, icu); + model.assign_location(id, hospital, mio::abm::ActivityType::Hospital); + model.assign_location(id, icu, mio::abm::ActivityType::ICU); //assign work/school to people depending on their age if (person.get_age() == age_group_5_to_14) { - model.assign_location(id, school); + model.assign_location(id, school, mio::abm::ActivityType::School); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - model.assign_location(id, work); + model.assign_location(id, work, mio::abm::ActivityType::Work); } } diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 9eb1caf65a..02e745f50c 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/household.h" #include "abm/model.h" #include "abm/infection_state.h" @@ -167,7 +168,7 @@ int main() //Create locations for both models //model 1 - auto event_m1 = model1.add_location(mio::abm::LocationType::SocialEvent); + auto event_m1 = model1.add_location(mio::abm::LocationType::Recreation); model1.get_location(event_m1).get_infection_parameters().set(10); auto hospital_m1 = model1.add_location(mio::abm::LocationType::Hospital); model1.get_location(hospital_m1).get_infection_parameters().set(10); @@ -180,7 +181,7 @@ int main() auto work_m1 = model1.add_location(mio::abm::LocationType::Work); model1.get_location(work_m1).get_infection_parameters().set(10); //model 2 - auto event_m2 = model2.add_location(mio::abm::LocationType::SocialEvent); + auto event_m2 = model2.add_location(mio::abm::LocationType::Recreation); model2.get_location(event_m2).get_infection_parameters().set(10); auto hospital_m2 = model2.add_location(mio::abm::LocationType::Hospital); model2.get_location(hospital_m2).get_infection_parameters().set(10); @@ -206,22 +207,22 @@ int main() person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), model1.parameters, start_date, infection_state)); } - person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_m1, model1.get_id()); - person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_m1, model1.get_id()); - person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_m1, model1.get_id()); - person.set_assigned_location(mio::abm::LocationType::ICU, icu_m1, model1.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Recreation, event_m1, model1.get_id()); + person.set_assigned_location(mio::abm::ActivityType::BasicsShop, shop_m1, model1.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Hospital, hospital_m1, model1.get_id()); + person.set_assigned_location(mio::abm::ActivityType::ICU, icu_m1, model1.get_id()); if (person.get_age() == age_group_children) { - person.set_assigned_location(mio::abm::LocationType::School, school_m1, model1.get_id()); + person.set_assigned_location(mio::abm::ActivityType::School, school_m1, model1.get_id()); } if (person.get_age() == age_group_adults) { //10% of adults in model 1 work in model 2 size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), std::vector{0.9, 0.1}); if (work_model == 1) { //person works in other model - person.set_assigned_location(mio::abm::LocationType::Work, work_m2, model2.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Work, work_m2, model2.get_id()); } else { //person works in same model - person.set_assigned_location(mio::abm::LocationType::Work, work_m1, model1.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Work, work_m1, model1.get_id()); } } } @@ -236,22 +237,22 @@ int main() person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), model2.parameters, start_date, infection_state)); } - person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_m2, model2.get_id()); - person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_m2, model2.get_id()); - person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_m2, model2.get_id()); - person.set_assigned_location(mio::abm::LocationType::ICU, icu_m2, model2.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Recreation, event_m2, model2.get_id()); + person.set_assigned_location(mio::abm::ActivityType::BasicsShop, shop_m2, model2.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Hospital, hospital_m2, model2.get_id()); + person.set_assigned_location(mio::abm::ActivityType::ICU, icu_m2, model2.get_id()); if (person.get_age() == age_group_children) { - person.set_assigned_location(mio::abm::LocationType::School, school_m2, model2.get_id()); + person.set_assigned_location(mio::abm::ActivityType::School, school_m2, model2.get_id()); } if (person.get_age() == age_group_adults) { //20% of adults in model 2 work in model 1 size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), std::vector{0.2, 0.8}); if (work_model == 1) { //person works in same model - person.set_assigned_location(mio::abm::LocationType::Work, work_m2, model2.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Work, work_m2, model2.get_id()); } else { //person works in other model - person.set_assigned_location(mio::abm::LocationType::Work, work_m1, model1.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Work, work_m1, model1.get_id()); } } } diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index ae04b68b2d..28849159c2 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(abm model.cpp model.h location_type.h + activity_type.h parameters.h parameters.cpp mobility_rules.cpp diff --git a/cpp/models/abm/activity_type.h b/cpp/models/abm/activity_type.h new file mode 100644 index 0000000000..ee6b174437 --- /dev/null +++ b/cpp/models/abm/activity_type.h @@ -0,0 +1,52 @@ +/* +* Copyright (C) 2020-2026 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef MIO_ABM_ACTIVITY_TYPE_H +#define MIO_ABM_ACTIVITY_TYPE_H + +#include + +namespace mio +{ +namespace abm +{ + +/** + * @brief Type of an Activity. This is used to determine the type of an Activity that a Person does at a Location. It is similar to LocationType, but is not necessarily the same as Persons can do different activities at the same location e.g. "Work" and "School" at a Location of LocationType "School". + */ +enum class ActivityType : std::uint32_t +{ + Home = 0, + School, + Work, + Recreation, // TODO: differentiate different kinds + BasicsShop, // groceries and other necessities + Hospital, + ICU, + Cemetery, // Location for all the dead persons. It is created once for the Model. + PublicTransport, + + Count, //last! + Invalid +}; + +} // namespace abm +} // namespace mio + +#endif diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 0d91c69c24..d7d75c92f5 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -57,21 +57,21 @@ constexpr mio::abm::ActivityType guess_activity_type(mio::abm::LocationType curr case mio::abm::LocationType::Home: return mio::abm::ActivityType::Home; case mio::abm::LocationType::Work: - return mio::abm::ActivityType::Workplace; + return mio::abm::ActivityType::Work; case mio::abm::LocationType::School: - return mio::abm::ActivityType::Education; - case mio::abm::LocationType::SocialEvent: - return mio::abm::ActivityType::Leisure; + return mio::abm::ActivityType::School; + case mio::abm::LocationType::Recreation: + return mio::abm::ActivityType::Recreation; case mio::abm::LocationType::BasicsShop: - return mio::abm::ActivityType::Shopping; + return mio::abm::ActivityType::BasicsShop; case mio::abm::LocationType::ICU: - return mio::abm::ActivityType::OtherActivity; + return mio::abm::ActivityType::ICU; case mio::abm::LocationType::Hospital: - return mio::abm::ActivityType::OtherActivity; + return mio::abm::ActivityType::Hospital; case mio::abm::LocationType::Cemetery: - return mio::abm::ActivityType::OtherActivity; + return mio::abm::ActivityType::Cemetery; default: - return mio::abm::ActivityType::UnknownActivity; + return mio::abm::ActivityType::Home; } } @@ -126,7 +126,7 @@ struct LogPersonInformation : mio::LogOnce { person_information.reserve(sim.get_model().get_persons().size()); for (auto& person : sim.get_model().get_persons()) { person_information.push_back(std::make_tuple( - person.get_id(), sim.get_model().find_location(mio::abm::LocationType::Home, person.get_id()), + person.get_id(), sim.get_model().find_locations(mio::abm::ActivityType::Home, person.get_id())[0], person.get_age())); } return person_information; diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index e234aaff58..984f483358 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -19,6 +19,7 @@ */ #include "abm/household.h" +#include "abm/activity_type.h" #include "abm/person_id.h" #include "abm/location.h" #include "memilio/utils/random_number_generator.h" @@ -69,8 +70,8 @@ void add_household_to_model(Model& model, const Household& household) std::tie(member, count) = memberTouple; for (int j = 0; j < count; j++) { auto age_group = pick_age_group_from_age_distribution(model.get_rng(), member.get_age_weights()); - auto person = model.add_person(home, age_group); - model.assign_location(person, home); + auto person = model.add_person(home, age_group, ActivityType::Home); + model.assign_location(person, home, ActivityType::Home); } } } diff --git a/cpp/models/abm/location_type.h b/cpp/models/abm/location_type.h index 93b601c438..8f146cadbd 100644 --- a/cpp/models/abm/location_type.h +++ b/cpp/models/abm/location_type.h @@ -34,8 +34,9 @@ enum class LocationType : std::uint32_t { Home = 0, School, - Work, - SocialEvent, // TODO: differentiate different kinds + Office, + Work, // other workplaces than offices, e.g. factories + Recreation, // TODO: differentiate different kinds BasicsShop, // groceries and other necessities Hospital, ICU, diff --git a/cpp/models/abm/mobility_data.h b/cpp/models/abm/mobility_data.h index a192589d2a..2fafb67106 100644 --- a/cpp/models/abm/mobility_data.h +++ b/cpp/models/abm/mobility_data.h @@ -43,21 +43,6 @@ enum class TransportMode : uint32_t Count //last!! }; -/** - * @brief Type of the activity. - */ -enum class ActivityType : uint32_t -{ - Workplace, - Education, - Shopping, - Leisure, - PrivateMatters, - OtherActivity, - Home, - UnknownActivity -}; - } // namespace abm } // namespace mio diff --git a/cpp/models/abm/mobility_rules.cpp b/cpp/models/abm/mobility_rules.cpp index 60d1946c5d..29f223a618 100644 --- a/cpp/models/abm/mobility_rules.cpp +++ b/cpp/models/abm/mobility_rules.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/mobility_rules.h" +#include "abm/activity_type.h" #include "abm/person.h" #include "abm/random_events.h" #include "abm/location_type.h" @@ -28,151 +29,150 @@ namespace mio namespace abm { -LocationType random_mobility(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +ActivityType random_mobility(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); auto make_transition = [current_loc](auto l) { return std::make_pair(l, l == current_loc ? 0. : 1.); }; if (t < params.get()) { return random_transition(rng, current_loc, dt, - {make_transition(LocationType::Work), make_transition(LocationType::Home), - make_transition(LocationType::School), make_transition(LocationType::SocialEvent), - make_transition(LocationType::BasicsShop)}); + {make_transition(ActivityType::Work), make_transition(ActivityType::Home), + make_transition(ActivityType::School), make_transition(ActivityType::Recreation), + make_transition(ActivityType::BasicsShop)}); } return current_loc; } -LocationType go_to_school(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, +ActivityType go_to_school(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); - if (current_loc == LocationType::Home && t < params.get() && t.day_of_week() < 5 && + if (current_loc == ActivityType::Home && t < params.get() && t.day_of_week() < 5 && person.get_go_to_school_time(params) >= t.time_since_midnight() && person.get_go_to_school_time(params) < t.time_since_midnight() + dt && params.get()[person.get_age()] && person.goes_to_school(t, params) && !person.is_in_quarantine(t, params)) { - return LocationType::School; + return ActivityType::School; } //return home - if (current_loc == LocationType::School && t.hour_of_day() >= 15) { - return LocationType::Home; + if (current_loc == ActivityType::School && t.hour_of_day() >= 15) { + return ActivityType::Home; } return current_loc; } -LocationType go_to_work(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, +ActivityType go_to_work(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); - if (current_loc == LocationType::Home && t < params.get() && + if (current_loc == ActivityType::Home && t < params.get() && params.get()[person.get_age()] && t.day_of_week() < 5 && t.time_since_midnight() + dt > person.get_go_to_work_time(params) && t.time_since_midnight() <= person.get_go_to_work_time(params) && person.goes_to_work(t, params) && !person.is_in_quarantine(t, params)) { - return LocationType::Work; + return ActivityType::Work; } //return home - if (current_loc == LocationType::Work && t.hour_of_day() >= 17) { - return LocationType::Home; + if (current_loc == ActivityType::Work && t.hour_of_day() >= 17) { + return ActivityType::Home; } return current_loc; } -LocationType go_to_shop(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +ActivityType go_to_shop(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); //leave - if (t.day_of_week() < 6 && t.hour_of_day() > 7 && t.hour_of_day() < 22 && current_loc == LocationType::Home && + if (t.day_of_week() < 6 && t.hour_of_day() > 7 && t.hour_of_day() < 22 && current_loc == ActivityType::Home && !person.is_in_quarantine(t, params)) { return random_transition(rng, current_loc, dt, - {{LocationType::BasicsShop, params.get()[person.get_age()]}}); + {{ActivityType::BasicsShop, params.get()[person.get_age()]}}); } //return home - if (current_loc == LocationType::BasicsShop && person.get_time_at_location() >= hours(1)) { - return LocationType::Home; + if (current_loc == ActivityType::BasicsShop && person.get_time_at_location() >= hours(1)) { + return ActivityType::Home; } return current_loc; } -LocationType go_to_event(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, - const Parameters& params) +ActivityType go_to_recreation(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const Parameters& params) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); //leave - if (current_loc == LocationType::Home && t < params.get() && + if (current_loc == ActivityType::Home && t < params.get() && ((t.day_of_week() <= 4 && t.hour_of_day() >= 19 && t.hour_of_day() < 22) || (t.day_of_week() >= 5 && t.hour_of_day() >= 10 && t.hour_of_day() < 22)) && !person.is_in_quarantine(t, params)) { return random_transition( rng, current_loc, dt, - {{LocationType::SocialEvent, params.get().get_matrix_at( - SimulationTime(t.days()))[(size_t)person.get_age()]}}); + {{ActivityType::Recreation, params.get().get_matrix_at( + SimulationTime(t.days()))[(size_t)person.get_age()]}}); } //return home - if (current_loc == LocationType::SocialEvent && t.hour_of_day() >= 20 && - person.get_time_at_location() >= hours(2)) { - return LocationType::Home; + if (current_loc == ActivityType::Recreation && t.hour_of_day() >= 20 && person.get_time_at_location() >= hours(2)) { + return ActivityType::Home; } return current_loc; } -LocationType go_to_quarantine(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, +ActivityType go_to_quarantine(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan /*dt*/, const Parameters& params) { - auto current_loc = person.get_location_type(); - if (person.is_in_quarantine(t, params) && current_loc != LocationType::Hospital && - current_loc != LocationType::ICU) { - return LocationType::Home; + auto current_loc = person.get_activity_type(); + if (person.is_in_quarantine(t, params) && current_loc != ActivityType::Hospital && + current_loc != ActivityType::ICU) { + return ActivityType::Home; } return current_loc; } -LocationType go_to_hospital(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, +ActivityType go_to_hospital(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); if (person.get_infection_state(t) == InfectionState::InfectedSevere) { - return LocationType::Hospital; + return ActivityType::Hospital; } return current_loc; } -LocationType go_to_icu(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, +ActivityType go_to_icu(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); if (person.get_infection_state(t) == InfectionState::InfectedCritical) { - return LocationType::ICU; + return ActivityType::ICU; } return current_loc; } -LocationType return_home_when_recovered(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, +ActivityType return_home_when_recovered(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location_type(); - if ((current_loc == LocationType::Hospital || current_loc == LocationType::ICU) && + auto current_loc = person.get_activity_type(); + if ((current_loc == ActivityType::Hospital || current_loc == ActivityType::ICU) && person.get_infection_state(t) == InfectionState::Recovered) { - return LocationType::Home; + return ActivityType::Home; } return current_loc; } -LocationType get_buried(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, +ActivityType get_buried(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location_type(); + auto current_loc = person.get_activity_type(); if (person.get_infection_state(t) == InfectionState::Dead) { - return LocationType::Cemetery; + return ActivityType::Cemetery; } return current_loc; } diff --git a/cpp/models/abm/mobility_rules.h b/cpp/models/abm/mobility_rules.h index 98dd9e250e..a531d3de04 100644 --- a/cpp/models/abm/mobility_rules.h +++ b/cpp/models/abm/mobility_rules.h @@ -20,7 +20,8 @@ #ifndef MIO_ABM_MOBILITY_RULES_H #define MIO_ABM_MOBILITY_RULES_H -#include "abm/location_type.h" +#include "abm/activity_type.h" +#include "abm/activity_type.h" #include "abm/parameters.h" #include "abm/time.h" #include "abm/person.h" @@ -45,61 +46,61 @@ namespace abm /** * @brief Completely random mobility to any other Location. */ -LocationType random_mobility(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +ActivityType random_mobility(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, const Parameters& params); /** * @brief School age children go to school in the morning and return later in the day. */ -LocationType go_to_school(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +ActivityType go_to_school(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, const Parameters& params); /** * @brief Adults may go shopping in their free time. */ -LocationType go_to_shop(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +ActivityType go_to_shop(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params); /** * @brief Person%s might go to social events. */ -LocationType go_to_event(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, - const Parameters& params); +ActivityType go_to_recreation(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const Parameters& params); /** * @brief Adults go to work in the morning and return later in the day. */ -LocationType go_to_work(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +ActivityType go_to_work(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params); /** * @brief Person%s who are in quarantine should go home. */ -LocationType go_to_quarantine(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint /*t*/, +ActivityType go_to_quarantine(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint /*t*/, TimeSpan /*dt*/, const Parameters& /*params*/); /** * @brief Infected Person%s may be hospitalized. */ -LocationType go_to_hospital(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +ActivityType go_to_hospital(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, const Parameters& params); /** * @brief Person%s in the hospital may be put in intensive care. */ -LocationType go_to_icu(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +ActivityType go_to_icu(PersonalRandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, const Parameters& params); /** * @brief Person%s in the hospital/icu return home when they recover. */ -LocationType return_home_when_recovered(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, +ActivityType return_home_when_recovered(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params); /** * @brief Person%s in the icu go to cemetery when they are dead. */ -LocationType get_buried(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +ActivityType get_buried(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params); /**@}*/ diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index a886edde98..bee1f134fd 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/model.h" +#include "abm/activity_type.h" #include "abm/location_id.h" #include "abm/location_type.h" #include "abm/intervention_type.h" @@ -52,10 +53,10 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) return id; } -PersonId Model::add_person(const LocationId id, AgeGroup age) +PersonId Model::add_person(const LocationId id, AgeGroup age, ActivityType activity_type) { PersonId person_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, person_id)); + return add_person(Person(m_rng, get_location(id).get_type(), activity_type, id, m_id, age, person_id)); } PersonId Model::add_person(Person&& person) @@ -65,7 +66,7 @@ PersonId Model::add_person(Person&& person) "Added Person's location is not in Model."); assert(person.get_id() != PersonId::invalid_ID() && "Added Person's unique id must be valid."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); - person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); + person.set_assigned_location(ActivityType::Cemetery, m_cemetery_id, m_id); m_persons.emplace_back(person); m_activeness_statuses.push_back(true); auto& new_person = m_persons.back(); @@ -113,8 +114,9 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) auto try_mobility_rule = [&](auto rule) -> bool { // run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); - if (person.get_assigned_location_model_id(target_type) == m_id) { - const Location& target_location = get_location(find_location(target_type, person)); + auto target = person.get_assigned_location(target_type, personal_rng); + if (target.second == m_id) { + const Location& target_location = get_location(target.first); const LocationId current_location = person.get_location(); // the Person cannot move if they do not wear mask as required at targeted location @@ -127,11 +129,10 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) get_number_persons(target_location.get_id()) >= target_location.get_capacity().persons) { return false; } - // The person cannot move if he has a positive test result, except he want to go to a hospital, ICU or home. + // The person cannot move if he has a positive test result, except he want to go to a hospital, ICU (as a patient) or home. if (!m_testing_strategy.run_and_check(personal_rng, person, target_location, t) && - target_location.get_type() != LocationType::Hospital && - target_location.get_type() != LocationType::ICU && - target_location.get_type() != LocationType::Home) { + target_type != ActivityType::Hospital && target_type != ActivityType::ICU && + target_type != ActivityType::Home) { return false; } @@ -147,7 +148,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) person.set_mask(MaskType::None, t); } // all requirements are met, move to target location - change_location(person, target_location.get_id()); + change_location(person, target_location.get_id(), target_type); return true; } return false; @@ -163,8 +164,8 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) (has_locations({LocationType::School, LocationType::Home}) && try_mobility_rule(&go_to_school)) || (has_locations({LocationType::Work, LocationType::Home}) && try_mobility_rule(&go_to_work)) || (has_locations({LocationType::BasicsShop, LocationType::Home}) && try_mobility_rule(&go_to_shop)) || - (has_locations({LocationType::SocialEvent, LocationType::Home}) && - try_mobility_rule(&go_to_event)) || + (has_locations({LocationType::Recreation, LocationType::Home}) && + try_mobility_rule(&go_to_recreation)) || (has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); } else { @@ -203,7 +204,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) continue; } // all requirements are met, move to target location - change_location(person, target_location.get_id(), trip.trip_mode); + change_location(person, target_location.get_id(), trip.activity, trip.trip_mode); // update worn mask to target location's requirements if (target_location.is_mask_required()) { // if the current MaskProtection level is lower than required, the Person changes mask @@ -373,9 +374,9 @@ auto Model::get_activeness_statuses() -> Range return {m_activeness_statuses}; } -LocationId Model::find_location(LocationType type, const PersonId person) const +std::vector Model::find_locations(ActivityType type, const PersonId person) const { - return find_location(type, get_person(person)); + return find_locations(type, get_person(person)); } size_t Model::get_subpopulation_combined(TimePoint t, InfectionState s) const diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index ee9077932f..8e7f11a931 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -20,6 +20,7 @@ #ifndef MIO_ABM_MODEL_H #define MIO_ABM_MODEL_H +#include "abm/activity_type.h" #include "abm/infection_state.h" #include "abm/model_functions.h" #include "abm/location_type.h" @@ -38,6 +39,7 @@ #include "memilio/utils/stl_util.h" #include +#include #include #include @@ -59,7 +61,7 @@ class Model using ConstPersonIterator = std::vector::const_iterator; using ActivenessIterator = std::vector::iterator; using ConstActivenessIterator = std::vector::const_iterator; - using MobilityRuleType = LocationType (*)(PersonalRandomNumberGenerator&, const Person&, TimePoint, TimeSpan, + using MobilityRuleType = ActivityType (*)(PersonalRandomNumberGenerator&, const Person&, TimePoint, TimeSpan, const Parameters&); using Compartments = mio::abm::InfectionState; @@ -203,9 +205,10 @@ class Model * @brief Add a Person to the Model. * @param[in] id The LocationID of the initial Location of the Person. * @param[in] age AgeGroup of the person. + * @param[in] activity_type The ActivityType the Person does at its initial Location. * @return Id of the newly created Person. */ - PersonId add_person(const LocationId id, AgeGroup age); + PersonId add_person(const LocationId id, AgeGroup age, ActivityType activity_type); /** * @brief Adds a copy of a given Person to the Model. @@ -243,12 +246,12 @@ class Model /** @} */ /** - * @brief Find an assigned Location of a Person. - * @param[in] type The #LocationType that specifies the assigned Location. + * @brief Find an all assigned Locations of a Person for a certain ActivityType. + * @param[in] type The ActivityType that specifies the assigned Locations. * @param[in] person Id of the Person. - * @return ID of the Location of LocationType type assigend to person. + * @return IDs of the Locations of ActivityType type assigend to person. */ - LocationId find_location(LocationType type, const PersonId person) const; + std::vector find_locations(ActivityType type, const PersonId person) const; /** * @brief Assign a Location to a Person. @@ -256,10 +259,11 @@ class Model * Assigning another Location of an already assigned LocationType will replace the prior assignment. * @param[in] person The Id of the person this location will be assigned to. * @param[in] location The LocationId of the Location. + * @param[in] activity The ActivityType the person does at the Location. */ - void assign_location(PersonId person, LocationId location) + void assign_location(PersonId person, LocationId location, ActivityType activity) { - assign_location(get_person(person), location); + assign_location(get_person(person), location, activity); } /** @@ -446,10 +450,10 @@ class Model * @param[in] mode The transport mode the person uses to change the Location. * @param[in] cells The cells within the destination the person should be in. */ - inline void change_location(PersonId person, LocationId destination, TransportMode mode = TransportMode::Unknown, - const std::vector& cells = {0}) + inline void change_location(PersonId person, LocationId destination, ActivityType activity, + TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { - change_location(get_person(person), destination, mode, cells); + change_location(get_person(person), destination, activity, mode, cells); } /** @@ -482,10 +486,11 @@ class Model * Assigning another Location of an already assigned LocationType will replace the prior assignment. * @param[in] person reference to the Person the location will be assigned to. * @param[in] location The LocationId of the Location. + * @param[in] activity The ActivityType the person does at the Location. */ - void assign_location(Person& person, LocationId location) + void assign_location(Person& person, LocationId location, ActivityType activity) { - person.set_assigned_location(get_location(location).get_type(), location, m_id); + person.set_assigned_location(activity, location, m_id); } Location& get_location(LocationId id) @@ -571,12 +576,13 @@ class Model * @param[in] mode The transport mode the person uses to change the Location. * @param[in] cells The cells within the destination the person should be in. */ - inline void change_location(Person& person, LocationId destination, TransportMode mode = TransportMode::Unknown, - const std::vector& cells = {0}) + inline void change_location(Person& person, LocationId destination, ActivityType activity, + TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { - LocationId origin = get_location(person).get_id(); - auto old_cells = person.get_cells(); - const bool has_changed_location = mio::abm::change_location(person, get_location(destination), mode, cells); + LocationId origin = get_location(person).get_id(); + auto old_cells = person.get_cells(); + const bool has_changed_location = + mio::abm::change_location(person, get_location(destination), activity, mode, cells); // if the person has changed location, invalidate exposure caches but keep population caches valid if (has_changed_location) { m_are_exposure_caches_valid = false; @@ -612,16 +618,16 @@ class Model } /** - * @brief Find an assigned Location of a Person. - * @param[in] type The #LocationType that specifies the assigned Location. + * @brief Find an assigned Locations of a Person. + * @param[in] type The ActivityType that specifies the assigned Locations. * @param[in] person Reference to Person. - * @return ID of the Location of LocationType type assigend to person. + * @return IDs of the Locations of ActivityType type assigend to person. */ - LocationId find_location(LocationType type, const Person& person) const + std::vector find_locations(ActivityType type, const Person& person) const { - auto location_id = person.get_assigned_location(type); - assert(location_id != LocationId::invalid_id() && "The person has no assigned location of that type."); - return location_id; + auto location_ids = person.get_assigned_locations()[static_cast(type)]; + assert(!location_ids.empty() && "The person has no assigned location of that type."); + return location_ids; } /** diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index 7529590521..c56f30bf39 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -19,6 +19,7 @@ */ #include "abm/model_functions.h" +#include "abm/activity_type.h" #include "abm/location.h" #include "abm/person.h" #include "abm/random_events.h" @@ -159,7 +160,7 @@ void normalize_exposure_contribution(ContactExposureRates& local_contact_exposur } } -bool change_location(Person& person, const Location& destination, const TransportMode mode, +bool change_location(Person& person, const Location& destination, ActivityType activity, const TransportMode mode, const std::vector& cells) { assert(std::all_of(cells.begin(), cells.end(), [&](const auto& cell) { @@ -167,7 +168,7 @@ bool change_location(Person& person, const Location& destination, const Transpor })); // make sure cell indices are valid if (person.get_location() != destination.get_id()) { - person.set_location(destination.get_type(), destination.get_id(), destination.get_model_id()); + person.set_location(activity, destination.get_type(), destination.get_id(), destination.get_model_id()); person.get_cells() = cells; person.set_last_transport_mode(mode); diff --git a/cpp/models/abm/model_functions.h b/cpp/models/abm/model_functions.h index 3b0bdbf462..e8bc2d5710 100644 --- a/cpp/models/abm/model_functions.h +++ b/cpp/models/abm/model_functions.h @@ -101,12 +101,13 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const * If the person already is at the destination, neither mode nor cells are set. * @param[in, out] person The person to change location. * @param[in] destination The destination to change location to. + * @param[in] activity The ActivityType the person does at the destination. * @param[in] mode The transport mode the person uses to change location. * @param[in] cells The cells within the destination the person should be in. * @return Returns false if the person already is at the given destination, true otherwise. */ -bool change_location(Person& person, const Location& destination, const TransportMode mode = TransportMode::Unknown, - const std::vector& cells = {0}); +bool change_location(Person& person, const Location& destination, ActivityType activity, + const TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}); /** * @brief Adjust ContactRates of location by MaximumContacts. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 19a66aec99..9c2ba077cf 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/person.h" +#include "abm/activity_type.h" #include "abm/location_type.h" #include "abm/mask_type.h" #include "abm/parameters.h" @@ -25,6 +26,7 @@ #include "abm/location.h" #include "abm/person_id.h" #include "memilio/utils/random_number_generator.h" +#include #include #include @@ -33,12 +35,13 @@ namespace mio namespace abm { -Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id) +Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, ActivityType activity_type, + LocationId location_id, int location_model_id, AgeGroup age, PersonId person_id) : m_location(location_id) , m_location_type(location_type) + , m_activity_type(activity_type) , m_location_model_id(location_model_id) - , m_assigned_locations((uint32_t)LocationType::Count, LocationId::invalid_id()) + , m_assigned_locations((uint32_t)ActivityType::Count) , m_home_isolation_start(TimePoint(-(std::numeric_limits::max() / 2))) , m_age(age) , m_time_at_location(0) @@ -95,10 +98,11 @@ LocationId Person::get_location() const return m_location; } -void Person::set_location(LocationType type, LocationId id, int model_id) +void Person::set_location(ActivityType activity_type, LocationType location_type, LocationId id, int model_id) { m_location = id; - m_location_type = type; + m_location_type = location_type; + m_activity_type = activity_type; m_location_model_id = model_id; m_time_at_location = TimeSpan(0); } @@ -113,20 +117,19 @@ Infection& Person::get_infection() return m_infections.back(); } -void Person::set_assigned_location(LocationType type, LocationId id, int model_id = 0) +void Person::set_assigned_location(ActivityType activity_type, LocationId id, int model_id = 0) { - m_assigned_locations[static_cast(type)] = id; - m_assigned_location_model_ids[static_cast(type)] = model_id; + m_assigned_locations[static_cast(activity_type)].push_back(id); + m_assigned_location_model_ids[static_cast(activity_type)].push_back(model_id); } -LocationId Person::get_assigned_location(LocationType type) const +std::pair Person::get_assigned_location(ActivityType activity_type, + PersonalRandomNumberGenerator& rng) const { - return m_assigned_locations[static_cast(type)]; -} - -int Person::get_assigned_location_model_id(LocationType type) const -{ - return m_assigned_location_model_ids[static_cast(type)]; + size_t index = UniformIntDistribution::get_instance()( + rng, size_t(0), m_assigned_locations[static_cast(activity_type)].size() - 1); + return {m_assigned_locations[static_cast(activity_type)][index], + m_assigned_location_model_ids[static_cast(activity_type)][index]}; } bool Person::goes_to_work(TimePoint t, const Parameters& params) const diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index e1cea60230..510b8af7b9 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -24,6 +24,7 @@ #include "abm/infection_state.h" #include "abm/location_id.h" #include "abm/location_type.h" +#include "abm/activity_type.h" #include "abm/parameters.h" #include "abm/person_id.h" #include "abm/personal_rng.h" @@ -57,8 +58,9 @@ class Person * @param[in] person_index Index of the Person. * */ - explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id = PersonId::invalid_ID()); + explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, ActivityType activity_type, + LocationId location_id, int location_model_id, AgeGroup age, + PersonId person_id = PersonId::invalid_ID()); explicit Person(const Person& other, PersonId person_id); @@ -128,11 +130,27 @@ class Person */ LocationId get_location() const; + /** + * @brief Get the LocationType of the current Location of the Person. + * @return LocationType of the current Location of the Person. + */ LocationType get_location_type() const { return m_location_type; } + /** + * @brief Get the ActivityType the Person does at its current Location. + * @return ActivityType of the current Location of the Person. + */ + ActivityType get_activity_type() const + { + return m_activity_type; + } + + /** + * @brief Get the model id of the current Location of the Person. Only used for Graph ABM. + */ int get_location_model_id() const { return m_location_model_id; @@ -140,11 +158,12 @@ class Person /** * @brief Change the location of the person. - * @param[in] type The LocationType of the new Location. + * @param[in] activity_type The ActivityType the Person does at the new Location. + * @param[in] location_type The LocationType of the new Location. * @param[in] id The LocationId of the new Location. * @param[in] model_id The model id of the new Location. */ - void set_location(LocationType type, LocationId id, int model_id); + void set_location(ActivityType activity_type, LocationType location_type, LocationId id, int model_id); /** * @brief Get the time the Person has been at its current Location. @@ -170,44 +189,36 @@ class Person * Important: Setting incorrect values will cause issues during simulation. It is preferable to use * Model::assign_location with a valid LocationId, obtained e.g. through Model::add_location. * - * The assigned Location is saved by the index of its LocationId. Assume that a Person has at most one assigned - * Location of a certain #LocationType. - * @param[in] type The LocationType of the Location. + * The assigned Location is saved by the index of its LocationId. + * @param[in] activity_type The ActivityType the Person does at the Location. * @param[in] id The LocationId of the Location. * @param[in] model_id The model id of the Location. */ - void set_assigned_location(LocationType type, LocationId id, int model_id); + void set_assigned_location(ActivityType activity_type, LocationId id, int model_id); /** - * @brief Returns the index of an assigned Location of the Person. - * Assume that a Person has at most one assigned Location of a certain #LocationType. - * @param[in] type #LocationType of the assigned Location. - * @return The index in the LocationId of the assigned Location. + * @brief Returns the index of an assigned Location of the Person. The index is uniformly sampled from the LocationIds of the assigned ActivityType. + * @param[in] activity_type #ActivityType of the assigned Location. + * @param[in] rng RandomNumberGenerator for sampling the index of the assigned LocationId. + * @return The index in the LocationId and the model id of the assigned Location. */ - LocationId get_assigned_location(LocationType type) const; + std::pair get_assigned_location(ActivityType activity_type, + PersonalRandomNumberGenerator& rng) const; /** * @brief Get the assigned Location%s of the Person. * @return A vector with the indices of the assigned Location%s of the Person. */ - const std::vector& get_assigned_locations() const + const std::vector>& get_assigned_locations() const { return m_assigned_locations; } - /** - * @brief Returns the model id of an assigned location of the Person. - * Assume that a Person has at most one assigned Location of a certain #LocationType. - * @param[in] type #LocationType of the assigned Location. - * @return The model id of the assigned Location. - */ - int get_assigned_location_model_id(LocationType type) const; - /** * @brief Get the assigned locations' model ids of the Person. * @return A vector with the model ids of the assigned locations of the Person */ - const std::vector& get_assigned_location_model_ids() const + const std::vector>& get_assigned_location_model_ids() const { return m_assigned_location_model_ids; } @@ -409,6 +420,7 @@ class Person return Members("Person") .add("location", m_location) .add("location_type", m_location_type) + .add("activity_type", m_activity_type) .add("assigned_locations", m_assigned_locations) .add("vaccinations", m_vaccinations) .add("infections", m_infections) @@ -446,9 +458,11 @@ class Person private: LocationId m_location; ///< Current Location of the Person. - LocationType m_location_type; ///< Type of the current Location. + LocationType m_location_type; ///< LocationType of the current Location. + ActivityType m_activity_type; ///< ActivityType the Person does at the current Location. int m_location_model_id; ///< Model id of the current Location. Only used for Graph ABM. - std::vector m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the + std::vector> + m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the Person always visits the same Home or School etc. */ std::vector m_vaccinations; ///< Vector with all vaccinations the Person has received. std::vector m_infections; ///< Vector with all Infection%s the Person had. @@ -467,7 +481,7 @@ class Person std::vector m_cells; ///< Vector with all Cell%s the Person visits at its current Location. mio::abm::TransportMode m_last_transport_mode; ///< TransportMode the Person used to get to its current Location. CustomIndexArray m_test_results; ///< CustomIndexArray for TestResults. - std::vector + std::vector> m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. PersonId m_person_id; ///< Unique identifier of a person. Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator. @@ -480,8 +494,8 @@ template <> struct DefaultFactory { static abm::Person create() { - return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::LocationId(), 0, AgeGroup(0), - abm::PersonId()); + return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::ActivityType::Count, abm::LocationId(), 0, + AgeGroup(0), abm::PersonId()); } }; diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index 0ffc03d93d..f244db7c67 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -20,6 +20,7 @@ #ifndef MIO_ABM_TRIP_LIST_H #define MIO_ABM_TRIP_LIST_H +#include "abm/activity_type.h" #include "abm/location_id.h" #include "abm/mobility_data.h" #include "abm/person.h" @@ -44,6 +45,7 @@ struct Trip { Model, where all Person%s are saved.*/ TimePoint trip_time; ///< Daytime at which a Person changes the Location. LocationId destination; ///< Location where the Person changes to. + ActivityType activity; ///< Activity the person does at the destination location. int destination_model_id; ///< Model id of destination Location. TransportMode trip_mode; ///< Mode of transportation. See TransportMode for all possible modes of transportation. std::vector cells; /**< If destination consists of different Cell%s, this gives the index of the @@ -54,17 +56,19 @@ struct Trip { * @param[in] id ID of the Person that makes the Trip. * @param[in] time Time at which a Person changes the Location this currently cant be set for s specific day just a timepoint in a day. * @param[in] destination Location where the Person changes to. - * @param[in] destination_model_id Model the Person changes to. + * @param[in] actvty Activity the person does at the destination location. + * @param[in] dest_model_id Model the Person changes to. * @param[in] origin Location where the person starts the Trip. * @param[in] origin_model_id Model the Person starts the Trip. * @param[in] input_cells The index of the Cell%s the Person changes to. */ - Trip(PersonId id, const TimePoint time, const LocationId dest, const int dest_model_id = 0, + Trip(PersonId id, const TimePoint time, const LocationId dest, ActivityType actvty, const int dest_model_id = 0, const TransportMode mode_of_transport = mio::abm::TransportMode::Unknown, const std::vector& input_cells = {}) : person_id(id) , trip_time(mio::abm::TimePoint(time.time_since_midnight().seconds())) , destination(dest) + , activity(actvty) , destination_model_id(dest_model_id) , trip_mode(mode_of_transport) , cells(input_cells) @@ -78,7 +82,8 @@ struct Trip { bool operator==(const Trip& other) const { return (person_id == other.person_id) && (trip_time == other.trip_time) && (destination == other.destination) && - (destination_model_id == other.destination_model_id) && (trip_mode == other.trip_mode); + (activity == other.activity) && (destination_model_id == other.destination_model_id) && + (trip_mode == other.trip_mode); } auto default_serialize() @@ -87,6 +92,7 @@ struct Trip { .add("person_id", person_id) .add("trip_time", trip_time) .add("destination", destination) + .add("activity", activity) .add("model_id", destination_model_id) .add("trip_mode", trip_mode); } @@ -173,7 +179,7 @@ template <> struct DefaultFactory { static abm::Trip create() { - return abm::Trip{abm::PersonId{}, abm::TimePoint{}, abm::LocationId{}}; + return abm::Trip{abm::PersonId{}, abm::TimePoint{}, abm::LocationId{}, abm::ActivityType::Count}; } }; diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 96f5da34d9..b561a38686 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -116,17 +116,16 @@ class ABMMobilityEdge std::sort(persons_to_change.begin(), persons_to_change.end()); //iterate over all persons that change from node_from for (int i = int(persons_to_change.size()) - 1; i >= 0; --i) { - auto& person = model_from.get_persons()[persons_to_change[i]]; - auto target_type = person.get_location_type(); - if (target_type == abm::LocationType::Invalid) { - target_type = model_to.get_location(person.get_location()).get_type(); - } + auto& person = model_from.get_persons()[persons_to_change[i]]; //check if Person uses this edge - if (person.get_assigned_location_model_id(target_type) == model_to.get_id()) { - - auto target_id = person.get_assigned_location(target_type); + if (person.get_location_model_id() == model_to.get_id()) { + auto target_id = person.get_location(); + auto target_type = person.get_location_type(); + if (target_type == abm::LocationType::Invalid) { + target_type = model_to.get_location(target_id).get_type(); + } //set correct location for person - person.set_location(target_type, target_id, model_to.get_id()); + person.set_location(person.get_activity_type(), target_type, target_id, model_to.get_id()); //add person to model_to model_to.add_person(std::move(person)); //remove person from model_from diff --git a/cpp/models/graph_abm/graph_abmodel.h b/cpp/models/graph_abm/graph_abmodel.h index 50f8c322c7..434d6f8e60 100644 --- a/cpp/models/graph_abm/graph_abmodel.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -20,6 +20,7 @@ #ifndef MIO_ABM_GRAPH_ABMODEL_H #define MIO_ABM_GRAPH_ABMODEL_H +#include "abm/activity_type.h" #include "abm/location_type.h" #include "abm/model.h" #include "abm/person_id.h" @@ -45,7 +46,7 @@ class GraphABModel : public abm::Model std::vector mobility_rules = std::vector{&get_buried, &return_home_when_recovered, &go_to_hospital, &go_to_icu, &go_to_school, &go_to_work, &go_to_shop, - &go_to_event, &go_to_quarantine}) + &go_to_recreation, &go_to_quarantine}) : Base(num_agegroups, id) { Base::m_mobility_rules = mobility_rules; @@ -96,8 +97,9 @@ class GraphABModel : public abm::Model auto try_mobility_rule = [&](auto rule) -> bool { //run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); - if (person.get_assigned_location_model_id(target_type) == Base::m_id) { - const Location& target_location = Base::get_location(Base::find_location(target_type, person)); + auto target = person.get_assigned_location(target_type, personal_rng); + if (target.second == Base::m_id) { + const Location& target_location = Base::get_location(target.first); const LocationId current_location = person.get_location(); // the Person cannot move if they do not wear mask as required at targeted location if (target_location.is_mask_required() && @@ -124,13 +126,12 @@ class GraphABModel : public abm::Model else { person.set_mask(MaskType::None, t); } - Base::change_location(person, target_location.get_id()); + Base::change_location(person, target_location.get_id(), target_type); return true; } else { //person moves to other world Base::m_activeness_statuses[person_index] = false; - person.set_location(target_type, abm::LocationId::invalid_id(), - std::numeric_limits::max()); + person.set_location(target_type, abm::LocationType::Invalid, target.first, target.second); m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; @@ -173,7 +174,7 @@ class GraphABModel : public abm::Model continue; } // all requirements are met, move to target location - change_location(person, target_location.get_id(), trip.trip_mode); + change_location(person, target_location.get_id(), trip.activity, trip.trip_mode); // update worn mask to target location's requirements if (target_location.is_mask_required()) { // if the current MaskProtection level is lower than required, the Person changes mask @@ -188,7 +189,8 @@ class GraphABModel : public abm::Model } else { //person moves to other world Base::m_activeness_statuses[person_index] = false; - person.set_location(abm::LocationType::Invalid, trip.destination, std::numeric_limits::max()); + person.set_location(trip.activity, abm::LocationType::Invalid, trip.destination, + trip.destination_model_id); m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index bf9af22c29..2ca81defaa 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -18,16 +18,18 @@ * limitations under the License. */ #include "abm_helpers.h" +#include "abm/activity_type.h" #include "abm/person.h" #include "abm/person_id.h" #include "memilio/utils/random_number_generator.h" -mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age, +mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, + mio::abm::ActivityType actvty_type, mio::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t, mio::abm::Parameters params, mio::abm::PersonId id) { assert(age.get() < params.get_num_groups()); - mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, id); + mio::abm::Person p(rng, location.get_type(), actvty_type, location.get_id(), location.get_model_id(), age, id); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( @@ -36,11 +38,13 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc return p; } -mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, +mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, + mio::abm::ActivityType actvty_type, mio::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t) { - return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, - model.parameters, static_cast(model.get_persons().size()))); + return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), actvty_type, age, + infection_state, t, model.parameters, + static_cast(model.get_persons().size()))); } void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 808e8dc173..518102433e 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -20,6 +20,7 @@ #ifndef ABM_HELPERS_H #define ABM_HELPERS_H +#include "abm/activity_type.h" #include "abm/model.h" #include "abm/person_id.h" @@ -94,17 +95,18 @@ struct ScopedMockDistribution { * @brief Create a Person without a Model object. Intended for simple use in tests. */ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, - mio::AgeGroup age = age_group_15_to_34, + mio::abm::ActivityType actvty_type, mio::AgeGroup age = age_group_15_to_34, mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0), mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), - mio::abm::PersonId id = mio::abm::PersonId(0)); + + mio::abm::PersonId id = mio::abm::PersonId(0)); /** * @brief Add a Person to the Model. Intended for simple use in tests. */ mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, - mio::AgeGroup age = age_group_15_to_34, + mio::abm::ActivityType actvty_type, mio::AgeGroup age = age_group_15_to_34, mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0)); diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index f8c7650de5..2ca97c7b81 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -298,8 +298,8 @@ TEST_F(TestInfection, getPersonalProtectiveFactor) const ScalarType eps = 1e-4; auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = mio::abm::Person(this->get_rng(), location.get_type(), location.get_id(), location.get_model_id(), - age_group_15_to_34); + auto person = mio::abm::Person(this->get_rng(), location.get_type(), mio::abm::ActivityType::School, + location.get_id(), location.get_model_id(), age_group_15_to_34); auto t0 = mio::abm::TimePoint(0); person.add_new_vaccination(mio::abm::ProtectionType::GenericVaccine, t0); auto latest_protection = person.get_latest_protection(t0); diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 5e04ddc931..13b41b74f2 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/location_id.h" #include "abm/parameters.h" #include "abm/person.h" @@ -102,11 +103,11 @@ TEST_F(TestLocation, interact) // Setup location with some chance of exposure mio::abm::Location location(mio::abm::LocationType::Work, 0, num_age_groups); location.get_infection_parameters().get() = true; - auto infected1 = make_test_person(this->get_rng(), location, age_group_15_to_34, + auto infected1 = make_test_person(this->get_rng(), location, mio::abm::ActivityType::Work, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t, params); - auto infected2 = make_test_person(this->get_rng(), location, age_group_80_plus, + auto infected2 = make_test_person(this->get_rng(), location, mio::abm::ActivityType::Work, age_group_80_plus, mio::abm::InfectionState::InfectedSymptoms, t, params); - auto infected3 = make_test_person(this->get_rng(), location, age_group_5_to_14, + auto infected3 = make_test_person(this->get_rng(), location, mio::abm::ActivityType::Work, age_group_5_to_14, mio::abm::InfectionState::InfectedSymptoms, t, params); std::vector local_population{infected1, infected2, infected3}; @@ -115,8 +116,8 @@ TEST_F(TestLocation, interact) ScopedMockDistribution>>> mock_discrete_dist; // Create a susceptible person and test interaction. - auto susceptible = - make_test_person(this->get_rng(), location, age, mio::abm::InfectionState::Susceptible, t, params); + auto susceptible = make_test_person(this->get_rng(), location, mio::abm::ActivityType::Work, age, + mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); // Probability of no infection auto person_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), susceptible); interact_testing(person_rng, susceptible, location, local_population, t, dt, params); @@ -176,7 +177,7 @@ TEST_F(TestLocation, getGeographicalLocation) */ TEST_F(TestLocation, adjustContactRates) { - mio::abm::Location loc(mio::abm::LocationType::SocialEvent, mio::abm::LocationId(0)); + mio::abm::Location loc(mio::abm::LocationType::Recreation, mio::abm::LocationId(0)); //Set the maximum contacts smaller than the contact rates loc.get_infection_parameters().get() = 2; loc.get_infection_parameters().get().get_baseline()(0, 0) = 4; diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index a292fc9984..e004b95375 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/lockdown_rules.h" #include "abm/mobility_rules.h" #include "abm/person.h" @@ -52,12 +53,14 @@ TEST_F(TestLockdownRules, school_closure) .WillRepeatedly(testing::Return(1.0)); // Set up two people with assigned locations (home and school) - auto p1 = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p1.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); - auto p2 = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p2.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); + auto p1 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_5_to_14); + p1.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p1.set_assigned_location(mio::abm::ActivityType::School, school.get_id(), school.get_model_id()); + auto p2 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_5_to_14); + p2.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p2.set_assigned_location(mio::abm::ActivityType::School, school.get_id(), school.get_model_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) params.get() = false; @@ -72,9 +75,9 @@ TEST_F(TestLockdownRules, school_closure) // Test that p1 stays home and p2 goes to school auto p1_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p1); - EXPECT_EQ(mio::abm::go_to_school(p1_rng, p1, t_morning, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_school(p1_rng, p1, t_morning, dt, params), mio::abm::ActivityType::Home); auto p2_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p2); - EXPECT_EQ(mio::abm::go_to_school(p2_rng, p2, t_morning, dt, params), mio::abm::LocationType::School); + EXPECT_EQ(mio::abm::go_to_school(p2_rng, p2, t_morning, dt, params), mio::abm::ActivityType::School); } /** @@ -101,9 +104,10 @@ TEST_F(TestLockdownRules, school_opening) .WillRepeatedly(testing::Return(1.0)); // Set up one person with assigned locations (home and school) - auto p = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); + auto p = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_5_to_14); + p.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p.set_assigned_location(mio::abm::ActivityType::School, school.get_id(), school.get_model_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) @@ -120,7 +124,7 @@ TEST_F(TestLockdownRules, school_opening) // Test that after reopening, the person goes to school auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); - EXPECT_EQ(mio::abm::go_to_school(p_rng, p, t_morning, dt, params), mio::abm::LocationType::School); + EXPECT_EQ(mio::abm::go_to_school(p_rng, p, t_morning, dt, params), mio::abm::ActivityType::School); } /** @@ -157,20 +161,20 @@ TEST_F(TestLockdownRules, home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto person1 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - auto person2 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - person1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - person1.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); - person2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - person2.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); + auto person1 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_15_to_34); + auto person2 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_15_to_34); + person1.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + person1.set_assigned_location(mio::abm::ActivityType::Work, work.get_id(), work.get_model_id()); + person2.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + person2.set_assigned_location(mio::abm::ActivityType::Work, work.get_id(), work.get_model_id()); // Check that person1 goes to work and person2 stays at home. auto p1_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person1); - EXPECT_EQ(mio::abm::go_to_work(p1_rng, person1, t_morning, dt, params), mio::abm::LocationType::Work); + EXPECT_EQ(mio::abm::go_to_work(p1_rng, person1, t_morning, dt, params), mio::abm::ActivityType::Work); auto p2_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person2); - EXPECT_EQ(mio::abm::go_to_work(p2_rng, person2, t_morning, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_work(p2_rng, person2, t_morning, dt, params), mio::abm::ActivityType::Home); } /** @@ -195,9 +199,10 @@ TEST_F(TestLockdownRules, no_home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto p = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); + auto p = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_15_to_34); + p.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p.set_assigned_location(mio::abm::ActivityType::Work, work.get_id(), work.get_model_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) params.get() = false; @@ -213,7 +218,7 @@ TEST_F(TestLockdownRules, no_home_office) // Test that after removing the home office rules, p goes back to the office. auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); - EXPECT_EQ(mio::abm::go_to_work(p_rng, p, t_morning, dt, params), mio::abm::LocationType::Work); + EXPECT_EQ(mio::abm::go_to_work(p_rng, p, t_morning, dt, params), mio::abm::ActivityType::Work); } /** @@ -226,10 +231,11 @@ TEST_F(TestLockdownRules, social_event_closure) auto t_evening = mio::abm::TimePoint(0) + mio::abm::hours(19); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location event(mio::abm::LocationType::SocialEvent, 1, num_age_groups); - auto p = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p.set_assigned_location(event.get_type(), event.get_id(), event.get_model_id()); + mio::abm::Location event(mio::abm::LocationType::Recreation, 1, num_age_groups); + auto p = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_5_to_14); + p.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p.set_assigned_location(mio::abm::ActivityType::Recreation, event.get_id(), event.get_model_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); // Close social events @@ -237,7 +243,7 @@ TEST_F(TestLockdownRules, social_event_closure) // Checks that p stays home instead of attending social events during a closure. auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); - EXPECT_EQ(mio::abm::go_to_event(p_rng, p, t_evening, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_recreation(p_rng, p, t_evening, dt, params), mio::abm::ActivityType::Home); } /** @@ -251,10 +257,11 @@ TEST_F(TestLockdownRules, social_events_opening) auto t_evening = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(19); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location event(mio::abm::LocationType::SocialEvent, 1, num_age_groups); - auto p = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - p.set_assigned_location(event.get_type(), event.get_id(), event.get_model_id()); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + mio::abm::Location event(mio::abm::LocationType::Recreation, 1, num_age_groups); + auto p = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_5_to_14); + p.set_assigned_location(mio::abm::ActivityType::Recreation, event.get_id(), event.get_model_id()); + p.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); // Close then open social events @@ -267,5 +274,5 @@ TEST_F(TestLockdownRules, social_events_opening) // Test that after reopening, p attends social events again. auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); - EXPECT_EQ(mio::abm::go_to_event(p_rng, p, t_evening, dt, params), mio::abm::LocationType::SocialEvent); + EXPECT_EQ(mio::abm::go_to_recreation(p_rng, p, t_evening, dt, params), mio::abm::ActivityType::Recreation); } diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index 5031dc7205..c3e68da12b 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/person.h" #include "abm_helpers.h" #include "random_number_test.h" @@ -79,13 +80,16 @@ TEST_F(TestMasks, maskProtection) auto t = mio::abm::TimePoint(0); mio::abm::Location infection_location(mio::abm::LocationType::School, 0, num_age_groups); // Two susceptible persons, one with a mask, one without - auto susc_person1 = mio::abm::Person(this->get_rng(), infection_location.get_type(), infection_location.get_id(), - infection_location.get_model_id(), age_group_15_to_34); - auto susc_person2 = mio::abm::Person(this->get_rng(), infection_location.get_type(), infection_location.get_id(), - infection_location.get_model_id(), age_group_15_to_34); + auto susc_person1 = + mio::abm::Person(this->get_rng(), infection_location.get_type(), mio::abm::ActivityType::School, + infection_location.get_id(), infection_location.get_model_id(), age_group_15_to_34); + auto susc_person2 = + mio::abm::Person(this->get_rng(), infection_location.get_type(), mio::abm::ActivityType::School, + infection_location.get_id(), infection_location.get_model_id(), age_group_15_to_34); // Infected person interacting with susceptible persons - auto infected1 = make_test_person(this->get_rng(), infection_location, age_group_15_to_34, - mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior + auto infected1 = + make_test_person(this->get_rng(), infection_location, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior // Cache precomputed results for 1-day intervals auto dt = mio::abm::days(1); diff --git a/cpp/tests/test_abm_mobility_rules.cpp b/cpp/tests/test_abm_mobility_rules.cpp index f5bd9fcc3a..1f30f40681 100644 --- a/cpp/tests/test_abm_mobility_rules.cpp +++ b/cpp/tests/test_abm_mobility_rules.cpp @@ -17,6 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" +#include "abm/location.h" #include "abm/location_type.h" #include "abm/mobility_rules.h" #include "abm/person.h" @@ -31,10 +33,11 @@ using TestMobilityRules = RandomNumberTest; TEST_F(TestMobilityRules, random_mobility) { int t = 0, dt = 1; - auto default_type = mio::abm::LocationType::Cemetery; - auto person = mio::abm::Person(this->get_rng(), default_type, 0, 0, age_group_15_to_34); - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); - auto params = mio::abm::Parameters(num_age_groups); + auto default_type = mio::abm::ActivityType::Cemetery; + auto person = mio::abm::Person(this->get_rng(), mio::abm::LocationType::Cemetery, mio::abm::ActivityType::Cemetery, + 0, 0, age_group_15_to_34); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto params = mio::abm::Parameters(num_age_groups); ScopedMockDistribution>>> mock_exp_dist; EXPECT_CALL(mock_exp_dist.get_mock(), invoke) @@ -84,10 +87,10 @@ TEST_F(TestMobilityRules, student_goes_to_school) .WillRepeatedly(testing::Return(1.0)); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - auto p_child = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - auto p_adult = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); + auto p_child = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_5_to_14); + auto p_adult = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_15_to_34); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(7); auto t_weekend = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(7); @@ -104,11 +107,11 @@ TEST_F(TestMobilityRules, student_goes_to_school) params.get()[age_group_35_to_59] = true; // Test that child goes to school - EXPECT_EQ(mio::abm::go_to_school(child_rng, p_child, t_morning, dt, params), mio::abm::LocationType::School); + EXPECT_EQ(mio::abm::go_to_school(child_rng, p_child, t_morning, dt, params), mio::abm::ActivityType::School); // Test that adult does not go to school - EXPECT_EQ(mio::abm::go_to_school(adult_rng, p_adult, t_morning, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_school(adult_rng, p_adult, t_morning, dt, params), mio::abm::ActivityType::Home); // Test that child goes back home after school - EXPECT_EQ(mio::abm::go_to_school(child_rng, p_child, t_weekend, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_school(child_rng, p_child, t_weekend, dt, params), mio::abm::ActivityType::Home); } /** @@ -134,12 +137,12 @@ TEST_F(TestMobilityRules, students_go_to_school_in_different_times) .WillRepeatedly(testing::Return(1.0)); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - auto p_child_goes_to_school_at_6 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + auto p_child_goes_to_school_at_6 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, + home.get_id(), home.get_model_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_6); - auto p_child_goes_to_school_at_8 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + auto p_child_goes_to_school_at_8 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, + home.get_id(), home.get_model_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_8 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_8); @@ -159,16 +162,16 @@ TEST_F(TestMobilityRules, students_go_to_school_in_different_times) // Mock randomness ensures children leave home at various school start times. EXPECT_EQ( mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_6, dt, params), - mio::abm::LocationType::School); + mio::abm::ActivityType::School); EXPECT_EQ( mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_8, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); EXPECT_EQ( mio::abm::go_to_school(rng_child_goes_to_school_at_8, p_child_goes_to_school_at_8, t_morning_6, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); EXPECT_EQ( mio::abm::go_to_school(rng_child_goes_to_school_at_8, p_child_goes_to_school_at_8, t_morning_8, dt, params), - mio::abm::LocationType::School); + mio::abm::ActivityType::School); } /** @@ -200,14 +203,15 @@ TEST_F(TestMobilityRules, students_go_to_school_in_different_times_with_smaller_ mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); // First student goes to school at 6:00 AM - auto p_child_goes_to_school_at_6 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + auto p_child_goes_to_school_at_6 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, + home.get_id(), home.get_model_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_6); // Second student goes to school at 8:30 AM auto p_child_goes_to_school_at_8_30 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_8_30 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_8_30); @@ -227,17 +231,17 @@ TEST_F(TestMobilityRules, students_go_to_school_in_different_times_with_smaller_ // Check that the first student goes to school at 6:00 AM EXPECT_EQ( mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_6, dt, params), - mio::abm::LocationType::School); + mio::abm::ActivityType::School); EXPECT_EQ( mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_8_30, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); // Check that the second student goes to school at 8:30 AM EXPECT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_8_30, p_child_goes_to_school_at_8_30, t_morning_6, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); EXPECT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_8_30, p_child_goes_to_school_at_8_30, t_morning_8_30, dt, params), - mio::abm::LocationType::School); + mio::abm::ActivityType::School); } /** * @brief Test return home from school. @@ -245,8 +249,8 @@ TEST_F(TestMobilityRules, students_go_to_school_in_different_times_with_smaller_ TEST_F(TestMobilityRules, school_return) { mio::abm::Location school(mio::abm::LocationType::School, 0, num_age_groups); - auto p_child = - mio::abm::Person(this->get_rng(), school.get_type(), school.get_id(), school.get_model_id(), age_group_5_to_14); + auto p_child = mio::abm::Person(this->get_rng(), school.get_type(), mio::abm::ActivityType::School, school.get_id(), + school.get_model_id(), age_group_5_to_14); auto rng_child = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child); // Simulate a time point after school hours @@ -255,7 +259,7 @@ TEST_F(TestMobilityRules, school_return) // Ensure that the child returns home after school is over EXPECT_EQ(mio::abm::go_to_school(rng_child, p_child, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); } /** @@ -264,6 +268,9 @@ TEST_F(TestMobilityRules, school_return) TEST_F(TestMobilityRules, worker_goes_to_work) { mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); + mio::abm::Location work(mio::abm::LocationType::Work, 1, num_age_groups); + mio::abm::Location school(mio::abm::LocationType::School, 2, num_age_groups); + mio::abm::Location hospital(mio::abm::LocationType::Hospital, 3, num_age_groups); // Mock the uniform distribution to control the randomness for workers' decisions. ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -276,16 +283,33 @@ TEST_F(TestMobilityRules, worker_goes_to_work) .WillOnce(testing::Return(0.)) .WillOnce(testing::Return(0.)) .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) + .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_retiree); - auto p_adult = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); + auto p_adult = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_15_to_34); + p_adult.set_assigned_location(mio::abm::ActivityType::Work, work.get_id(), home.get_model_id()); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult); - - auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); + auto p_teacher = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_35_to_59); + p_teacher.set_assigned_location(mio::abm::ActivityType::Work, school.get_id(), school.get_model_id()); + auto rng_teacher = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_teacher); + auto p_doctor = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_35_to_59); + p_doctor.set_assigned_location(mio::abm::ActivityType::Work, hospital.get_id(), hospital.get_model_id()); + auto rng_doctor = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_doctor); + + auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(6); auto t_night = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(4); auto dt = mio::abm::hours(1); @@ -299,11 +323,15 @@ TEST_F(TestMobilityRules, worker_goes_to_work) params.get()[age_group_35_to_59] = true; // Check that the retiree (age group 60-79) should stay home and not go to work. - EXPECT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, params), mio::abm::LocationType::Home); - // Check that the adult (age group 15-34) should go to work at 8:00 AM. - EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, params), mio::abm::LocationType::Home); - // Check that during the night (4:00 AM), the adult should stay home and not go to work. - EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_night, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, params), mio::abm::ActivityType::Home); + // Check that the adults (age group 15-34) should go to work at 6:00 AM. + EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, params), mio::abm::ActivityType::Work); + EXPECT_EQ(mio::abm::go_to_work(rng_teacher, p_teacher, t_morning, dt, params), mio::abm::ActivityType::Work); + EXPECT_EQ(mio::abm::go_to_work(rng_doctor, p_doctor, t_morning, dt, params), mio::abm::ActivityType::Work); + // Check that during the night (4:00 AM), the adults should stay home and not go to work. + EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_night, dt, params), mio::abm::ActivityType::Home); + EXPECT_EQ(mio::abm::go_to_work(rng_teacher, p_teacher, t_night, dt, params), mio::abm::ActivityType::Home); + EXPECT_EQ(mio::abm::go_to_work(rng_doctor, p_doctor, t_night, dt, params), mio::abm::ActivityType::Home); } /** @@ -328,12 +356,12 @@ TEST_F(TestMobilityRules, worker_goes_to_work_with_non_dividable_timespan) .WillRepeatedly(testing::Return(1.0)); // Set up two people: one retiree and one working adult. - auto p_retiree = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_retiree); - auto p_adult = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult); + auto p_adult = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_15_to_34); + auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); auto t_night = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(4); @@ -349,11 +377,11 @@ TEST_F(TestMobilityRules, worker_goes_to_work_with_non_dividable_timespan) params.get()[age_group_35_to_59] = true; // Check that the retiree (age group 60-79) should stay home and not go to work even with non-dividable time step. - EXPECT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, params), mio::abm::ActivityType::Home); // Check that the adult (age group 15-34) should still go to work at 8:00 AM even with the non-dividable time step. - EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, params), mio::abm::ActivityType::Home); // Check that during the night (4:00 AM), the adult should stay home and not go to work even with the non-dividable time step. - EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_night, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_night, dt, params), mio::abm::ActivityType::Home); } /** @@ -383,12 +411,12 @@ TEST_F(TestMobilityRules, workers_go_to_work_in_different_times) .WillRepeatedly(testing::Return(1.0)); // Create two workers: one goes to work at 6 AM and the other at 8 AM. - auto p_adult_goes_to_work_at_6 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); + auto p_adult_goes_to_work_at_6 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, + home.get_id(), home.get_model_id(), age_group_15_to_34); auto rng_adult_goes_to_work_at_6 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult_goes_to_work_at_6); - auto p_adult_goes_to_work_at_8 = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); + auto p_adult_goes_to_work_at_8 = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, + home.get_id(), home.get_model_id(), age_group_15_to_34); auto rng_adult_goes_to_work_at_8 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult_goes_to_work_at_8); @@ -407,22 +435,22 @@ TEST_F(TestMobilityRules, workers_go_to_work_in_different_times) // Check that the worker going to work at 6 AM goes to work EXPECT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_6, p_adult_goes_to_work_at_6, t_morning_6, dt, params), - mio::abm::LocationType::Work); + mio::abm::ActivityType::Work); // Check that the worker going to work at 6 AM stays home at 8 AM (since they are already at work) EXPECT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_6, p_adult_goes_to_work_at_6, t_morning_8, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); // Check that the worker going to work at 6 AM returns home at night EXPECT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_6, p_adult_goes_to_work_at_6, t_night, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); // Check that the worker going to work at 8 AM stays home at 6 AM EXPECT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_8, p_adult_goes_to_work_at_8, t_morning_6, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); // Check that the worker going to work at 8 AM goes to work at 8 AM EXPECT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_8, p_adult_goes_to_work_at_8, t_morning_8, dt, params), - mio::abm::LocationType::Work); + mio::abm::ActivityType::Work); // Check that the worker going to work at 8 AM returns home at night EXPECT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_8, p_adult_goes_to_work_at_8, t_night, dt, params), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); } /** @@ -432,15 +460,15 @@ TEST_F(TestMobilityRules, work_return) { mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); // Set up a random number generator and a worker who is currently at work - auto p_adult = - mio::abm::Person(this->get_rng(), work.get_type(), work.get_id(), work.get_model_id(), age_group_35_to_59); + auto p_adult = mio::abm::Person(this->get_rng(), work.get_type(), mio::abm::ActivityType::Work, work.get_id(), + work.get_model_id(), age_group_35_to_59); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult); // Set the time to 5 PM (17:00) when the worker should return home auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); auto dt = mio::abm::hours(1); // Test that the worker, who is currently at work, goes home after 5 PM EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); } /** @@ -455,28 +483,28 @@ TEST_F(TestMobilityRules, quarantine) mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0, num_age_groups); - auto p_inf1 = - make_test_person(this->get_rng(), work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); + auto p_inf1 = make_test_person(this->get_rng(), work, mio::abm::ActivityType::Work, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms, t); auto rng_inf1 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf1); p_inf1.get_tested(rng_inf1, t, test_params); // Check detected infected person quarantines at home EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf1, p_inf1, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); - auto p_inf2 = - make_test_person(this->get_rng(), work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); + auto p_inf2 = make_test_person(this->get_rng(), work, mio::abm::ActivityType::Work, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms, t); auto rng_inf2 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf2); // Check that undetected infected person does not quaratine EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf2, p_inf2, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Work); + mio::abm::ActivityType::Work); - auto p_inf3 = - make_test_person(this->get_rng(), hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); + auto p_inf3 = make_test_person(this->get_rng(), hospital, mio::abm::ActivityType::Hospital, age_group_15_to_34, + mio::abm::InfectionState::InfectedSevere, t); auto rng_inf3 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf3); p_inf1.get_tested(rng_inf3, t, test_params); // Check that detected infected person does not leave hospital to quarantine EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf3, p_inf3, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Hospital); + mio::abm::ActivityType::Hospital); } /** @@ -485,22 +513,22 @@ TEST_F(TestMobilityRules, quarantine) TEST_F(TestMobilityRules, hospital) { mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); - auto p_inf = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); + auto p_inf = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedSevere, t); auto rng_inf = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf); // Ensure person goes to the hospital when severely infected EXPECT_EQ(mio::abm::go_to_hospital(rng_inf, p_inf, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Hospital); + mio::abm::ActivityType::Hospital); - auto p_car = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); + auto p_car = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms); auto rng_car = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_car); // Ensure person has infection symptoms still stay at home EXPECT_EQ(mio::abm::go_to_hospital(rng_car, p_car, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); } /** @@ -517,23 +545,23 @@ TEST_F(TestMobilityRules, go_shopping) auto dt = mio::abm::hours(1); // Create an infected child in the hospital - auto p_hosp = make_test_person(this->get_rng(), hospital, age_group_0_to_4, + auto p_hosp = make_test_person(this->get_rng(), hospital, mio::abm::ActivityType::Hospital, age_group_0_to_4, mio::abm::InfectionState::InfectedSymptoms, t_weekday); auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_hosp); // Create a healthy elderly person at home - auto p_home = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); + auto p_home = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_home); // Check that an infected person stays in the hospital and doesn't go shopping EXPECT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Hospital); + mio::abm::ActivityType::Hospital); // Check that a person at home doesn't go shopping on a Sunday EXPECT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_sunday, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); // Check that a person at home doesn't go shopping on a Sunday EXPECT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_night, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); // Mocking the random distribution to simulate a person going shopping on a weekday ScopedMockDistribution>>> @@ -541,7 +569,7 @@ TEST_F(TestMobilityRules, go_shopping) EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); // Test that a person goes to a basic shop on a weekday at 9 AM EXPECT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_weekday, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::BasicsShop); + mio::abm::ActivityType::BasicsShop); } /** @@ -555,15 +583,15 @@ TEST_F(TestMobilityRules, shop_return) // Create a person at a basic shop who is asymptomatically infected mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0, num_age_groups); - auto p = - make_test_person(this->get_rng(), shop, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + auto p = make_test_person(this->get_rng(), shop, mio::abm::ActivityType::BasicsShop, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); // Simulate the person spending 1 hour at the shop p.add_time_at_location(dt); // After spending sufficient time at the shop, the person should return home EXPECT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); } /** @@ -573,12 +601,12 @@ TEST_F(TestMobilityRules, go_event) { // Initialize two people, one at work and one at home mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); - auto p_work = - mio::abm::Person(this->get_rng(), work.get_type(), work.get_id(), work.get_model_id(), age_group_35_to_59); + auto p_work = mio::abm::Person(this->get_rng(), work.get_type(), mio::abm::ActivityType::Work, work.get_id(), + work.get_model_id(), age_group_35_to_59); auto rng_work = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_work); mio::abm::Location home(mio::abm::LocationType::Home, 1, num_age_groups); - auto p_home = - mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); + auto p_home = mio::abm::Person(this->get_rng(), home.get_type(), mio::abm::ActivityType::Home, home.get_id(), + home.get_model_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_home); auto t_weekday = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(20); @@ -587,23 +615,23 @@ TEST_F(TestMobilityRules, go_event) auto dt = mio::abm::hours(1); // Check that person at work should not go to an event during a weekday evening, so they stay at work - EXPECT_EQ(mio::abm::go_to_event(rng_work, p_work, t_weekday, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Work); + EXPECT_EQ(mio::abm::go_to_recreation(rng_work, p_work, t_weekday, dt, mio::abm::Parameters(num_age_groups)), + mio::abm::ActivityType::Work); // Check that person at home during nighttime should not go to an event, so they stay at home - EXPECT_EQ(mio::abm::go_to_event(rng_home, p_home, t_night, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_recreation(rng_home, p_home, t_night, dt, mio::abm::Parameters(num_age_groups)), + mio::abm::ActivityType::Home); // Mock the random distribution to simulate event participation based on random draw ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); // Check that person at home goes to a social event on a weekday evening based on a low random value - EXPECT_EQ(mio::abm::go_to_event(rng_home, p_home, t_weekday, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::SocialEvent); + EXPECT_EQ(mio::abm::go_to_recreation(rng_home, p_home, t_weekday, dt, mio::abm::Parameters(num_age_groups)), + mio::abm::ActivityType::Recreation); // Check that person at home goes to a social event on a Saturday morning based on a low random value EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - EXPECT_EQ(mio::abm::go_to_event(rng_home, p_home, t_saturday, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::SocialEvent); + EXPECT_EQ(mio::abm::go_to_recreation(rng_home, p_home, t_saturday, dt, mio::abm::Parameters(num_age_groups)), + mio::abm::ActivityType::Recreation); } /** @@ -616,15 +644,15 @@ TEST_F(TestMobilityRules, event_return) auto dt = mio::abm::hours(3); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 1, num_age_groups); + mio::abm::Location social_event(mio::abm::LocationType::Recreation, 1, num_age_groups); // Initialize the person at the social event location - auto p = mio::abm::Person(this->get_rng(), social_event.get_type(), social_event.get_id(), - social_event.get_model_id(), age_group_15_to_34); + auto p = mio::abm::Person(this->get_rng(), social_event.get_type(), mio::abm::ActivityType::Recreation, + social_event.get_id(), social_event.get_model_id(), age_group_15_to_34); auto rng_p = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); // Simulate the person spending 3 hours at the social event p.add_time_at_location(dt); // After spending the time at the social event, the person should return home - EXPECT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, params), mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_recreation(rng_p, p, t, dt, params), mio::abm::ActivityType::Home); } /** @@ -633,23 +661,23 @@ TEST_F(TestMobilityRules, event_return) TEST_F(TestMobilityRules, icu) { mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0, num_age_groups); - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); - auto p_hosp = - make_test_person(this->get_rng(), hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedCritical, t); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); + auto p_hosp = make_test_person(this->get_rng(), hospital, mio::abm::ActivityType::Hospital, age_group_15_to_34, + mio::abm::InfectionState::InfectedCritical, t); auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_hosp); // Ensure critically infected person goes to the ICU EXPECT_EQ(mio::abm::go_to_icu(rng_hosp, p_hosp, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::ICU); + mio::abm::ActivityType::ICU); mio::abm::Location work(mio::abm::LocationType::Work, 1, num_age_groups); - auto p_work = - make_test_person(this->get_rng(), work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); + auto p_work = make_test_person(this->get_rng(), work, mio::abm::ActivityType::Work, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms, t); auto rng_work = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_work); // Ensure infected with symptions person can still go to work EXPECT_EQ(mio::abm::go_to_icu(rng_work, p_work, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Work); + mio::abm::ActivityType::Work); } /** @@ -658,19 +686,19 @@ TEST_F(TestMobilityRules, icu) TEST_F(TestMobilityRules, recover) { mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0); - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); - auto p_rec = - make_test_person(this->get_rng(), hospital, age_group_60_to_79, mio::abm::InfectionState::Recovered, t); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); + auto p_rec = make_test_person(this->get_rng(), hospital, mio::abm::ActivityType::Hospital, age_group_60_to_79, + mio::abm::InfectionState::Recovered, t); auto rng_rec = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_rec); - auto p_inf = - make_test_person(this->get_rng(), hospital, age_group_60_to_79, mio::abm::InfectionState::InfectedSevere, t); + auto p_inf = make_test_person(this->get_rng(), hospital, mio::abm::ActivityType::Hospital, age_group_60_to_79, + mio::abm::InfectionState::InfectedSevere, t); auto rng_inf = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf); // Ensure recovered person returns home and infected severe person stay in hospital EXPECT_EQ(mio::abm::return_home_when_recovered(rng_rec, p_rec, t, dt, {num_age_groups}), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); EXPECT_EQ(mio::abm::return_home_when_recovered(rng_inf, p_inf, t, dt, {num_age_groups}), - mio::abm::LocationType::Hospital); + mio::abm::ActivityType::Hospital); } /** @@ -681,8 +709,9 @@ TEST_F(TestMobilityRules, dead) mio::abm::Location icu(mio::abm::LocationType::ICU, 0); auto t = mio::abm::TimePoint(12346); auto dt = mio::abm::hours(1); - auto p_dead = make_test_person(this->get_rng(), icu, age_group_60_to_79, mio::abm::InfectionState::Dead, t); + auto p_dead = make_test_person(this->get_rng(), icu, mio::abm::ActivityType::ICU, age_group_60_to_79, + mio::abm::InfectionState::Dead, t); auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_dead); - EXPECT_EQ(mio::abm::get_buried(p_rng, p_dead, t, dt, {num_age_groups}), mio::abm::LocationType::Cemetery); + EXPECT_EQ(mio::abm::get_buried(p_rng, p_dead, t, dt, {num_age_groups}), mio::abm::ActivityType::Cemetery); } diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index fe7926ff06..371ee1d1c9 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/location_id.h" #include "abm/person_id.h" #include "memilio/utils/random_number_generator.h" @@ -99,8 +100,8 @@ TEST_F(TestModel, addPerson) auto model = mio::abm::Model(num_age_groups); auto location = model.add_location(mio::abm::LocationType::School); - auto id1 = model.add_person(location, age_group_15_to_34); - auto id2 = model.add_person(location, age_group_35_to_59); + auto id1 = model.add_person(location, age_group_15_to_34, mio::abm::ActivityType::School); + auto id2 = model.add_person(location, age_group_35_to_59, mio::abm::ActivityType::Work); // Verify the number of persons in the model and their respective age groups. EXPECT_EQ(model.get_persons().size(), 2); @@ -123,8 +124,8 @@ TEST_F(TestModel, getNumberPersons) auto location = model.add_location(mio::abm::LocationType::School); // Add persons to the model. - model.add_person(location, age_group_15_to_34); - model.add_person(location, age_group_35_to_59); + model.add_person(location, age_group_15_to_34, mio::abm::ActivityType::School); + model.add_person(location, age_group_35_to_59, mio::abm::ActivityType::Work); EXPECT_TRUE(logger.read().empty()); @@ -161,12 +162,18 @@ TEST_F(TestModel, getSubpopulationCombined) auto home1 = model.add_location(mio::abm::LocationType::Home); // Add persons to these locations with various infection states. - add_test_person(model, school1, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - add_test_person(model, school1, age_group_15_to_34, mio::abm::InfectionState::Susceptible); - add_test_person(model, school2, age_group_15_to_34, mio::abm::InfectionState::Susceptible); - add_test_person(model, school2, age_group_15_to_34, mio::abm::InfectionState::Susceptible); - add_test_person(model, school3, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - add_test_person(model, home1, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); + add_test_person(model, school1, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms); + add_test_person(model, school1, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::Susceptible); + add_test_person(model, school2, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::Susceptible); + add_test_person(model, school2, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::Susceptible); + add_test_person(model, school3, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms); + add_test_person(model, home1, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms); // Verify the count of susceptible persons across all School locations. EXPECT_EQ(model.get_subpopulation_combined_per_location_type(t, mio::abm::InfectionState::Susceptible, @@ -194,22 +201,22 @@ TEST_F(TestModel, findLocation) auto work_id = model.add_location(mio::abm::LocationType::Work); // Add a person to the model and assign them to multiple locations. - auto person_id = add_test_person(model, home_id); + auto person_id = add_test_person(model, home_id, mio::abm::ActivityType::Home); auto& person = model.get_person(person_id); - person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - person.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + person.set_assigned_location(mio::abm::ActivityType::School, school_id, model.get_id()); // Verify that the find_location method correctly identifies each assigned location. - EXPECT_EQ(model.find_location(mio::abm::LocationType::Work, person_id), work_id); - EXPECT_EQ(model.find_location(mio::abm::LocationType::School, person_id), school_id); - EXPECT_EQ(model.find_location(mio::abm::LocationType::Home, person_id), home_id); + EXPECT_EQ(model.find_locations(mio::abm::ActivityType::Work, person_id)[0], work_id); + EXPECT_EQ(model.find_locations(mio::abm::ActivityType::School, person_id)[0], school_id); + EXPECT_EQ(model.find_locations(mio::abm::ActivityType::Home, person_id)[0], home_id); // Check that the method also works with a constant reference to the model. auto&& model_test = std::as_const(model); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Work, person_id), work_id); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::School, person_id), school_id); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Home, person_id), home_id); + EXPECT_EQ(model_test.find_locations(mio::abm::ActivityType::Work, person_id)[0], work_id); + EXPECT_EQ(model_test.find_locations(mio::abm::ActivityType::School, person_id)[0], school_id); + EXPECT_EQ(model_test.find_locations(mio::abm::ActivityType::Home, person_id)[0], home_id); } /** @@ -251,18 +258,21 @@ TEST_F(TestModel, evolveStateTransition) // Add locations and persons to the model with different initial infection states. auto location1 = model.add_location(mio::abm::LocationType::School); auto location2 = model.add_location(mio::abm::LocationType::Work); - add_test_person(model, location1, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - add_test_person(model, location1, age_group_15_to_34, mio::abm::InfectionState::Susceptible); - add_test_person(model, location2, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); + add_test_person(model, location1, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms); + add_test_person(model, location1, mio::abm::ActivityType::School, age_group_15_to_34, + mio::abm::InfectionState::Susceptible); + add_test_person(model, location2, mio::abm::ActivityType::Work, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms); auto& p1 = model.get_persons()[0]; auto& p2 = model.get_persons()[1]; auto& p3 = model.get_persons()[2]; // Assign persons to their respective locations. - p1.set_assigned_location(mio::abm::LocationType::School, location1, model.get_id()); - p2.set_assigned_location(mio::abm::LocationType::School, location1, model.get_id()); - p3.set_assigned_location(mio::abm::LocationType::Work, location2, model.get_id()); + p1.set_assigned_location(mio::abm::ActivityType::School, location1, model.get_id()); + p2.set_assigned_location(mio::abm::ActivityType::School, location1, model.get_id()); + p3.set_assigned_location(mio::abm::ActivityType::Work, location2, model.get_id()); // Setup mock so p2 becomes infected ScopedMockDistribution>>> @@ -312,18 +322,20 @@ TEST_F(TestModel, evolveMobilityRules) .WillOnce(testing::Return(0.8)) // draw random school hour .WillRepeatedly(testing::Return(1.0)); - auto pid2 = add_test_person(model, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t); - auto pid1 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + auto pid2 = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_5_to_14, + mio::abm::InfectionState::Susceptible, t); + auto pid1 = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, t); auto& p1 = model.get_person(pid1); auto& p2 = model.get_person(pid2); - p1.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); - p2.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p1.set_assigned_location(mio::abm::ActivityType::School, school_id, model.get_id()); + p2.set_assigned_location(mio::abm::ActivityType::School, school_id, model.get_id()); + p1.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p2.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p1.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p2.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); ScopedMockDistribution>>> mock_exponential_dist; @@ -368,7 +380,7 @@ TEST_F(TestModel, evolveMobilityTrips) // Add different location types to the model. auto home_id = model.add_location(mio::abm::LocationType::Home); - auto event_id = model.add_location(mio::abm::LocationType::SocialEvent); + auto event_id = model.add_location(mio::abm::LocationType::Recreation); auto work_id = model.add_location(mio::abm::LocationType::Work); auto hospital_id = model.add_location(mio::abm::LocationType::Hospital); @@ -386,11 +398,11 @@ TEST_F(TestModel, evolveMobilityTrips) .WillRepeatedly(testing::Return(0.8)); // this forces p1 and p3 to recover // Create persons with various infection states and assign them to multiple locations. - auto pid1 = model.add_person(home_id, age_group_15_to_34); - auto pid2 = model.add_person(home_id, age_group_15_to_34); - auto pid3 = model.add_person(home_id, age_group_15_to_34); - auto pid4 = model.add_person(hospital_id, age_group_15_to_34); - auto pid5 = model.add_person(home_id, age_group_15_to_34); + auto pid1 = model.add_person(home_id, age_group_15_to_34, mio::abm::ActivityType::Home); + auto pid2 = model.add_person(home_id, age_group_15_to_34, mio::abm::ActivityType::Home); + auto pid3 = model.add_person(home_id, age_group_15_to_34, mio::abm::ActivityType::Home); + auto pid4 = model.add_person(hospital_id, age_group_15_to_34, mio::abm::ActivityType::Hospital); + auto pid5 = model.add_person(home_id, age_group_15_to_34, mio::abm::ActivityType::Home); // Assign persons to locations for trips. auto& p1 = model.get_person(pid1); @@ -399,25 +411,28 @@ TEST_F(TestModel, evolveMobilityTrips) auto& p4 = model.get_person(pid4); auto& p5 = model.get_person(pid5); - p1.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); - p2.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p3.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p4.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p3.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); - p4.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); - p5.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); - p5.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p5.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p1.set_assigned_location(mio::abm::ActivityType::Recreation, event_id, model.get_id()); + p2.set_assigned_location(mio::abm::ActivityType::Recreation, event_id, model.get_id()); + p1.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p2.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p1.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p2.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p3.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p4.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p3.set_assigned_location(mio::abm::ActivityType::Hospital, hospital_id, model.get_id()); + p4.set_assigned_location(mio::abm::ActivityType::Hospital, hospital_id, model.get_id()); + p5.set_assigned_location(mio::abm::ActivityType::Recreation, event_id, model.get_id()); + p5.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p5.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); // Set trips for persons between assigned locations. mio::abm::TripList& data = model.get_trip_list(); - mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id); - mio::abm::Trip trip2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id); - mio::abm::Trip trip3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id); + mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, + mio::abm::ActivityType::Work); + mio::abm::Trip trip2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, + mio::abm::ActivityType::Recreation); + mio::abm::Trip trip3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, + mio::abm::ActivityType::Recreation); auto trips_part1 = std::vector{trip2, trip3}; auto trips_part2 = std::vector{trip1}; @@ -500,14 +515,14 @@ TEST_F(TestModel, reachCapacity) .WillRepeatedly(testing::Return(1.0)); // Create two persons with different infection states. - auto p1 = add_test_person(model, home_id, age_group_5_to_14); - auto p2 = add_test_person(model, home_id, age_group_5_to_14); + auto p1 = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_5_to_14); + auto p2 = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_5_to_14); // Assign both persons to School and Home. - model.get_person(p1).set_assigned_location(mio::abm::LocationType::School, school_id, 0); - model.get_person(p2).set_assigned_location(mio::abm::LocationType::School, school_id, 0); - model.get_person(p1).set_assigned_location(mio::abm::LocationType::Home, home_id, 0); - model.get_person(p2).set_assigned_location(mio::abm::LocationType::Home, home_id, 0); + model.get_person(p1).set_assigned_location(mio::abm::ActivityType::School, school_id, 0); + model.get_person(p2).set_assigned_location(mio::abm::ActivityType::School, school_id, 0); + model.get_person(p1).set_assigned_location(mio::abm::ActivityType::Home, home_id, 0); + model.get_person(p2).set_assigned_location(mio::abm::ActivityType::Home, home_id, 0); // Set the capacity of the school to 1 person with a distance requirement of 66. model.get_location(school_id).set_capacity(1, 66); @@ -555,25 +570,29 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) EXPECT_CALL(mock_uniform_dist.get_mock(), invoke).WillRepeatedly(testing::Return(1.0)); // Create a person that is dead at time t - add_test_person(model, icu_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t); + add_test_person(model, icu_id, mio::abm::ActivityType::ICU, age_group_60_to_79, mio::abm::InfectionState::Dead, t); // Create a person that is severe at hospital and will be dead at time t + dt - add_test_person(model, hospital_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); + add_test_person(model, hospital_id, mio::abm::ActivityType::Hospital, age_group_60_to_79, + mio::abm::InfectionState::Dead, t + dt); auto& p_dead = model.get_persons()[0]; auto& p_severe = model.get_persons()[1]; - p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); - p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_dead.set_assigned_location(mio::abm::ActivityType::ICU, icu_id, model.get_id()); + p_dead.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_dead.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::ActivityType::Hospital, hospital_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::ActivityType::ICU, icu_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); // Add trip to see if a dead person can change location outside of cemetery by scheduled trips mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), home_id); - mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id); - mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id); + mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), home_id, + mio::abm::ActivityType::Home); + mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id, + mio::abm::ActivityType::ICU); + mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id, + mio::abm::ActivityType::ICU); trip_list.add_trips({trip1, trip2, trip3}); // Check the dead person got burried and the severely infected person starts in Hospital @@ -612,12 +631,12 @@ TEST_F(TestModelTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) auto test_time = mio::abm::minutes(30); // Add a person to the model with an infection state that requires testing. // Since tests are performed before current_time, the InfectionState of the Person has to take into account test_time - auto pid = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, - current_time - test_time); + auto pid = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms, current_time - test_time); auto& person = model.get_person(pid); auto rng_person = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); - person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + person.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); auto validity_period = mio::abm::days(1); const auto start_date = mio::abm::TimePoint(0) + mio::abm::days(1); @@ -851,16 +870,16 @@ TEST_F(TestModel, mobilityRulesWithAppliedNPIs) .WillRepeatedly(testing::Return(0.9)); // draw that satisfies all pre-conditions of NPIs // Since tests are performed before t, the InfectionState of all the Person have to take into account test_time - auto p_id_compliant_go_to_work = - add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t - test_time); - auto p_id_compliant_go_to_school = - add_test_person(model, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t - test_time); - auto p_id_no_mask = - add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t - test_time); - auto p_id_no_test = add_test_person(model, home_id, age_group_15_to_34, - mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); - auto p_id_no_isolation = add_test_person(model, home_id, age_group_15_to_34, - mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); + auto p_id_compliant_go_to_work = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Susceptible, t - test_time); + auto p_id_compliant_go_to_school = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_5_to_14, + mio::abm::InfectionState::Susceptible, t - test_time); + auto p_id_no_mask = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Susceptible, t - test_time); + auto p_id_no_test = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); + auto p_id_no_isolation = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); auto& p_compliant_go_to_work = model.get_person(p_id_compliant_go_to_work); auto& p_compliant_go_to_school = model.get_person(p_id_compliant_go_to_school); @@ -868,17 +887,17 @@ TEST_F(TestModel, mobilityRulesWithAppliedNPIs) auto& p_no_test = model.get_person(p_id_no_test); auto& p_no_isolation = model.get_person(p_id_no_isolation); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_no_mask.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_no_mask.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_no_test.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_no_test.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::ActivityType::School, school_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); auto testing_criteria = mio::abm::TestingCriteria( {}, {mio::abm::InfectionState::InfectedSymptoms, mio::abm::InfectionState::InfectedNoSymptoms}); @@ -967,16 +986,16 @@ TEST_F(TestModel, mobilityTripWithAppliedNPIs) .WillRepeatedly(testing::Return(0.9)); // draw that satisfies all pre-conditions of NPIs // Since tests are performed before t, the InfectionState of all the Person have to take into account test_time - auto p_id_compliant_go_to_work = - add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t - test_time); - auto p_id_compliant_go_to_school = - add_test_person(model, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t - test_time); - auto p_id_no_mask = - add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t - test_time); - auto p_id_no_test = add_test_person(model, home_id, age_group_15_to_34, - mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); - auto p_id_no_isolation = add_test_person(model, home_id, age_group_15_to_34, - mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); + auto p_id_compliant_go_to_work = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Susceptible, t - test_time); + auto p_id_compliant_go_to_school = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_5_to_14, + mio::abm::InfectionState::Susceptible, t - test_time); + auto p_id_no_mask = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Susceptible, t - test_time); + auto p_id_no_test = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); + auto p_id_no_isolation = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, t - test_time); auto& p_compliant_go_to_work = model.get_person(p_id_compliant_go_to_work); auto& p_compliant_go_to_school = model.get_person(p_id_compliant_go_to_school); @@ -984,17 +1003,17 @@ TEST_F(TestModel, mobilityTripWithAppliedNPIs) auto& p_no_test = model.get_person(p_id_no_test); auto& p_no_isolation = model.get_person(p_id_no_isolation); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_no_mask.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_no_mask.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_no_test.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_no_test.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::ActivityType::School, school_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); auto testing_criteria = mio::abm::TestingCriteria( {}, {mio::abm::InfectionState::InfectedSymptoms, mio::abm::InfectionState::InfectedNoSymptoms}); @@ -1019,11 +1038,11 @@ TEST_F(TestModel, mobilityTripWithAppliedNPIs) // Using trip list mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_compliant_go_to_work.get_id(), t, work_id); - mio::abm::Trip trip2(p_compliant_go_to_school.get_id(), t, school_id); - mio::abm::Trip trip3(p_no_mask.get_id(), t, work_id); - mio::abm::Trip trip4(p_no_test.get_id(), t, work_id); - mio::abm::Trip trip5(p_no_isolation.get_id(), t, work_id); + mio::abm::Trip trip1(p_compliant_go_to_work.get_id(), t, work_id, mio::abm::ActivityType::Work); + mio::abm::Trip trip2(p_compliant_go_to_school.get_id(), t, school_id, mio::abm::ActivityType::School); + mio::abm::Trip trip3(p_no_mask.get_id(), t, work_id, mio::abm::ActivityType::Work); + mio::abm::Trip trip4(p_no_test.get_id(), t, work_id, mio::abm::ActivityType::Work); + mio::abm::Trip trip5(p_no_isolation.get_id(), t, work_id, mio::abm::ActivityType::Work); trip_list.add_trips({trip1, trip2, trip3, trip4, trip5}); model.use_mobility_rules(false); model.evolve(t, dt); @@ -1087,13 +1106,14 @@ TEST_F(TestModel, personCanDieInHospital) EXPECT_CALL(mock_uniform_dist.get_mock(), invoke).WillRepeatedly(testing::Return(0.09)); // Create a person that has InfectedSymptoms at time t - dt. The person is dead at time t + dt - add_test_person(model, home_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); + add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_60_to_79, mio::abm::InfectionState::Dead, + t + dt); auto& p_severe = model.get_persons()[0]; - p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::ActivityType::Hospital, hospital_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::ActivityType::ICU, icu_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::ActivityType::Home, home_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::ActivityType::Work, work_id, model.get_id()); // Check the infection course goes from InfectedSymptoms to Severe to Dead and skips Critical EXPECT_EQ(p_severe.get_infection_state(t - dt), mio::abm::InfectionState::InfectedSymptoms); @@ -1118,7 +1138,7 @@ TEST_F(TestModel, reset_rng) { // use DefaultFactory to avoid using the RNG in the Person ctor auto p = mio::DefaultFactory::create(); - p.set_location(mio::abm::LocationType::Cemetery, mio::abm::LocationId(0), 0); + p.set_location(mio::abm::ActivityType::Cemetery, mio::abm::LocationType::Cemetery, mio::abm::LocationId(0), 0); model.add_person(mio::abm::Person(p, mio::abm::PersonId(0))); model.add_person(mio::abm::Person(p, mio::abm::PersonId(1))); } diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 9bc2cabc40..a4b66a78c2 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/location_id.h" #include "abm/model_functions.h" #include "abm/location_type.h" @@ -27,6 +28,7 @@ #include "abm_helpers.h" #include "random_number_test.h" +#include "gmock/gmock.h" #include using TestPerson = RandomNumberTest; @@ -38,8 +40,8 @@ TEST_F(TestPerson, init) { mio::abm::Location location(mio::abm::LocationType::Work, 7, num_age_groups); auto t = mio::abm::TimePoint(0); - auto person = mio::abm::Person(this->get_rng(), location.get_type(), location.get_id(), location.get_model_id(), - age_group_60_to_79); + auto person = mio::abm::Person(this->get_rng(), location.get_type(), mio::abm::ActivityType::Work, + location.get_id(), location.get_model_id(), age_group_60_to_79); // Verify default state and location assignments. EXPECT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); @@ -61,27 +63,31 @@ TEST_F(TestPerson, change_location) mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 1, 6, 0, 1); mio::abm::Location loc2(mio::abm::LocationType::School, 2, num_age_groups); mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 3, 6, 0, 2); - auto person = make_test_person(this->get_rng(), home, age_group_0_to_4, mio::abm::InfectionState::Recovered); + auto person = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_0_to_4, + mio::abm::InfectionState::Recovered); // Check that a person does not change location to its current location person.add_time_at_location(mio::abm::hours(1)); - EXPECT_FALSE(mio::abm::change_location(person, home)); + EXPECT_FALSE(mio::abm::change_location(person, home, mio::abm::ActivityType::Home)); EXPECT_EQ(person.get_time_at_location(), mio::abm::hours(1)); EXPECT_EQ(person.get_location(), home.get_id()); // Change the location of the person a couple of times - EXPECT_TRUE(mio::abm::change_location(person, loc1, mio::abm::TransportMode::Unknown, {0})); + EXPECT_TRUE(mio::abm::change_location(person, loc1, mio::abm::ActivityType::Invalid, + mio::abm::TransportMode::Unknown, {0})); EXPECT_EQ(person.get_time_at_location(), mio::abm::TimeSpan(0)); EXPECT_EQ(person.get_location(), loc1.get_id()); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); - EXPECT_TRUE(mio::abm::change_location(person, loc2, mio::abm::TransportMode::Walking, {0})); + EXPECT_TRUE(mio::abm::change_location(person, loc2, mio::abm::ActivityType::Invalid, + mio::abm::TransportMode::Walking, {0})); EXPECT_EQ(person.get_time_at_location(), mio::abm::TimeSpan(0)); EXPECT_EQ(person.get_location(), loc2.get_id()); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); // Test changing location with cell indices. - EXPECT_TRUE(mio::abm::change_location(person, loc3, mio::abm::TransportMode::Bike, {0, 1})); + EXPECT_TRUE(mio::abm::change_location(person, loc3, mio::abm::ActivityType::Invalid, mio::abm::TransportMode::Bike, + {0, 1})); EXPECT_EQ(person.get_time_at_location(), mio::abm::TimeSpan(0)); EXPECT_EQ(person.get_location(), loc3.get_id()); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Bike); @@ -95,32 +101,46 @@ TEST_F(TestPerson, change_location) */ TEST_F(TestPerson, setGetAssignedLocation) { + // Mock assigned location draws + ScopedMockDistribution>>> + mock_uniform_dist; + EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) + .Times(testing::Exactly(4)) // 1 draw per get_assigned_location_draw + .WillOnce(testing::Return(0)) // LocationId(2) + .WillOnce(testing::Return(1)) // LocationId(4) + .WillOnce(testing::Return(2)) // LocationId(0) + .WillOnce(testing::Return(3)); // LocationId(max) + mio::abm::Location location(mio::abm::LocationType::Work, 2, num_age_groups); - auto person = mio::abm::Person(this->get_rng(), location.get_type(), location.get_id(), location.get_model_id(), - age_group_35_to_59); + auto person = mio::abm::Person(this->get_rng(), location.get_type(), mio::abm::ActivityType::Work, + location.get_id(), location.get_model_id(), age_group_35_to_59); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); // Assign and verify a location for the person. - person.set_assigned_location(location.get_type(), location.get_id(), location.get_model_id()); - EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(2)); + person.set_assigned_location(mio::abm::ActivityType::Work, location.get_id(), location.get_model_id()); + EXPECT_EQ(person.get_assigned_location(mio::abm::ActivityType::Work, p_rng).first, mio::abm::LocationId(2)); // Change the assigned location and verify. - person.set_assigned_location(mio::abm::LocationType::Work, mio::abm::LocationId(4), 0); - EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(4)); - - // Fuzzing: assign random valid LocationId values and verify correctness. - for (int i = 0; i < 100; ++i) { - auto random_id = this->random_integer(0, std::numeric_limits::max()); - auto random_type = this->random_integer(0, (int)mio::abm::LocationType::Count - 1); - person.set_assigned_location((mio::abm::LocationType)random_type, mio::abm::LocationId(random_id), 0); - EXPECT_EQ(person.get_assigned_location((mio::abm::LocationType)random_type), mio::abm::LocationId(random_id)); - } + person.set_assigned_location(mio::abm::ActivityType::Work, mio::abm::LocationId(4), 0); + EXPECT_EQ(person.get_assigned_location(mio::abm::ActivityType::Work, p_rng).first, mio::abm::LocationId(4)); // Boundary test cases: test with boundary LocationIds. - person.set_assigned_location(mio::abm::LocationType::Work, mio::abm::LocationId(0), 0); - EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(0)); + person.set_assigned_location(mio::abm::ActivityType::Work, mio::abm::LocationId(0), 0); + EXPECT_EQ(person.get_assigned_location(mio::abm::ActivityType::Work, p_rng).first, mio::abm::LocationId(0)); - person.set_assigned_location(mio::abm::LocationType::Work, mio::abm::LocationId(std::numeric_limits::max()), + person.set_assigned_location(mio::abm::ActivityType::Work, mio::abm::LocationId(std::numeric_limits::max()), 0); - EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), + EXPECT_EQ(person.get_assigned_location(mio::abm::ActivityType::Work, p_rng).first, mio::abm::LocationId(std::numeric_limits::max())); + + // Fuzzing: assign random valid LocationId values and verify correctness. + for (int i = 0; i < 100; ++i) { + + EXPECT_CALL(mock_uniform_dist.get_mock(), invoke).Times(testing::Exactly(1)).WillOnce(testing::Return(i + 4)); + + auto random_id = this->random_integer(0, std::numeric_limits::max()); + person.set_assigned_location(mio::abm::ActivityType::Work, mio::abm::LocationId(random_id), 0); + EXPECT_EQ(person.get_assigned_location(mio::abm::ActivityType::Work, p_rng).first, + mio::abm::LocationId(random_id)); + } } /** @@ -160,7 +180,7 @@ TEST_F(TestPerson, quarantine) infection_parameters.get().set_multiple({age_group_5_to_14}, true); infection_parameters.get().set_multiple({age_group_15_to_34, age_group_35_to_59}, true); - auto person = make_test_person(this->get_rng(), home, age_group_35_to_59, + auto person = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_35_to_59, mio::abm::InfectionState::InfectedSymptoms, t_morning, infection_parameters); auto rng_person = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); @@ -168,13 +188,13 @@ TEST_F(TestPerson, quarantine) person.get_tested(rng_person, t_morning, test_params); EXPECT_EQ(person.get_infection_state(t_morning), mio::abm::InfectionState::InfectedSymptoms); EXPECT_EQ(mio::abm::go_to_work(rng_person, person, t_morning, dt, infection_parameters), - mio::abm::LocationType::Home); + mio::abm::ActivityType::Home); EXPECT_EQ(person.get_infection_state(t_morning + dt), mio::abm::InfectionState::Recovered); // Test removal from quarantine. person.remove_quarantine(); EXPECT_EQ(mio::abm::go_to_work(rng_person, person, t_morning, dt, infection_parameters), - mio::abm::LocationType::Work); + mio::abm::ActivityType::Work); } /** @@ -187,11 +207,11 @@ TEST_F(TestPerson, get_tested) mio::abm::TimePoint t(0); mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); - auto infected = - make_test_person(this->get_rng(), loc, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); - auto rng_infected = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), infected); - auto susceptible = - mio::abm::Person(this->get_rng(), loc.get_type(), loc.get_id(), loc.get_model_id(), age_group_15_to_34); + auto infected = make_test_person(this->get_rng(), loc, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms); + auto rng_infected = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), infected); + auto susceptible = mio::abm::Person(this->get_rng(), loc.get_type(), mio::abm::ActivityType::Home, loc.get_id(), + loc.get_model_id(), age_group_15_to_34); auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), susceptible); auto pcr_parameters = params.get()[mio::abm::TestType::PCR]; @@ -253,11 +273,12 @@ TEST_F(TestPerson, getCells) mio::abm::Location home(mio::abm::LocationType::Home, 0, 6, 1); mio::abm::Location location(mio::abm::LocationType::PublicTransport, 1, 6, 0, 7); // Create a test person at the home location. - auto person = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); + auto person = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms); // Move the person to a new location with specified cells (3, 5). - EXPECT_TRUE(mio::abm::change_location(person, location, mio::abm::TransportMode::Unknown, {3, 5})); + EXPECT_TRUE(mio::abm::change_location(person, location, mio::abm::ActivityType::Invalid, + mio::abm::TransportMode::Unknown, {3, 5})); // Check that the person's cell indices have been updated correctly. EXPECT_EQ(person.get_cells().size(), 2); @@ -276,8 +297,8 @@ TEST_F(TestPerson, interact) mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::TimePoint t(0); // Create a person and set up a random number generator specific to that person. - auto person = - mio::abm::Person(this->get_rng(), loc.get_type(), loc.get_id(), loc.get_model_id(), age_group_15_to_34); + auto person = mio::abm::Person(this->get_rng(), loc.get_type(), mio::abm::ActivityType::Home, loc.get_id(), + loc.get_model_id(), age_group_15_to_34); auto rng_person = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); auto dt = mio::abm::seconds(8640); //0.1 days // Simulate interaction and check that the person accumulates time at the location. @@ -292,7 +313,7 @@ TEST_F(TestPerson, setWearMask) { auto t = mio::abm::TimePoint(0); mio::abm::Location location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = make_test_person(this->get_rng(), location); + auto person = make_test_person(this->get_rng(), location, mio::abm::ActivityType::School); // Test setting and verifying different mask types. person.set_mask(mio::abm::MaskType::None, t); @@ -309,13 +330,13 @@ TEST_F(TestPerson, getMaskProtectiveFactor) { auto t = mio::abm::TimePoint(0); mio::abm::Location location(mio::abm::LocationType::School, 0, 6); - auto person_community = make_test_person(this->get_rng(), location); + auto person_community = make_test_person(this->get_rng(), location, mio::abm::ActivityType::School); person_community.set_mask(mio::abm::MaskType::Community, t); - auto person_surgical = make_test_person(this->get_rng(), location); + auto person_surgical = make_test_person(this->get_rng(), location, mio::abm::ActivityType::School); person_surgical.set_mask(mio::abm::MaskType::Surgical, t); - auto person_ffp2 = make_test_person(this->get_rng(), location); + auto person_ffp2 = make_test_person(this->get_rng(), location, mio::abm::ActivityType::School); person_ffp2.set_mask(mio::abm::MaskType::FFP2, t); - auto person_without = make_test_person(this->get_rng(), location); + auto person_without = make_test_person(this->get_rng(), location, mio::abm::ActivityType::School); person_without.set_mask(mio::abm::MaskType::None, t); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); @@ -335,10 +356,10 @@ TEST_F(TestPerson, getMaskProtectiveFactor) */ TEST_F(TestPerson, getLatestProtection) { - auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = mio::abm::Person(this->get_rng(), location.get_type(), location.get_id(), location.get_model_id(), - age_group_15_to_34); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); + auto person = mio::abm::Person(this->get_rng(), location.get_type(), mio::abm::ActivityType::School, + location.get_id(), location.get_model_id(), age_group_15_to_34); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); @@ -362,7 +383,8 @@ TEST_F(TestPerson, getLatestProtection) */ TEST_F(TestPerson, rng) { - auto p = mio::abm::Person(this->get_rng(), mio::abm::LocationType::Home, 0, 0, age_group_35_to_59, 13); + auto p = mio::abm::Person(this->get_rng(), mio::abm::LocationType::Home, mio::abm::ActivityType::Home, 0, 0, + age_group_35_to_59, 13); EXPECT_EQ(p.get_rng_counter(), mio::Counter(0)); @@ -381,7 +403,7 @@ TEST_F(TestPerson, rng) TEST_F(TestPerson, addAndGetTestResult) { mio::abm::Location location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = make_test_person(this->get_rng(), location); + auto person = make_test_person(this->get_rng(), location, mio::abm::ActivityType::School); auto t = mio::abm::TimePoint(0); // Tests if m_test_results initialized correctly EXPECT_EQ(person.get_test_result(mio::abm::TestType::Generic).time_of_testing, @@ -409,7 +431,7 @@ TEST_F(TestPerson, isCompliant) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); // Create test person and associated random number generator - auto person = make_test_person(this->get_rng(), home); + auto person = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home); auto rng_person = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); // Test cases with a complete truth table for compliance levels diff --git a/cpp/tests/test_abm_serialization.cpp b/cpp/tests/test_abm_serialization.cpp index 7fd48dda52..41275577a7 100644 --- a/cpp/tests/test_abm_serialization.cpp +++ b/cpp/tests/test_abm_serialization.cpp @@ -35,6 +35,7 @@ #include "models/abm/model.h" #include #include +#include #ifdef MEMILIO_HAS_JSONCPP @@ -83,6 +84,7 @@ TEST(TestAbmSerialization, Trip) reference_json["destination"] = Json::UInt(i++); reference_json["model_id"] = Json::Int(i++); reference_json["trip_mode"] = Json::UInt(i++); + reference_json["activity"] = Json::UInt(i++); test_json_serialization(reference_json); } @@ -190,18 +192,24 @@ TEST(TestAbmSerialization, Person) return mio::serialize_json(values).value(); }; + auto json_uint_array_array = [](std::vector> values) { + return mio::serialize_json(values).value(); + }; + unsigned i = 1; // counter s.t. members have different values Json::Value reference_json; - reference_json["age_group"] = Json::UInt(i++); - reference_json["assigned_locations"] = json_uint_array({i++, i++, i++, i++, i++, i++, i++, i++, i++, i++, i++}); - reference_json["cells"] = json_uint_array({i++}); - reference_json["compliance"] = json_double_array({(double)i++, (double)i++, (double)i++}); - reference_json["infections"] = Json::Value(Json::arrayValue); - reference_json["last_transport_mode"] = Json::UInt(i++); - reference_json["location"] = Json::UInt(i++); - reference_json["location_type"] = Json::UInt(0); - reference_json["mask"]["mask_type"] = Json::UInt(0); + reference_json["age_group"] = Json::UInt(i++); + reference_json["assigned_locations"] = + json_uint_array_array({{i++, i++, i++, i++, i++, i++, i++, i++, i++, i++, i++}}); + reference_json["cells"] = json_uint_array({i++}); + reference_json["compliance"] = json_double_array({(double)i++, (double)i++, (double)i++}); + reference_json["infections"] = Json::Value(Json::arrayValue); + reference_json["last_transport_mode"] = Json::UInt(i++); + reference_json["location"] = Json::UInt(i++); + reference_json["location_type"] = Json::UInt(0); + reference_json["activity_type"] = Json::UInt(0); + reference_json["mask"]["mask_type"] = Json::UInt(0); reference_json["mask"]["time_first_used"]["seconds"] = Json::Int(i++); reference_json["home_isolation_start"]["seconds"] = Json::Int(i++); reference_json["rnd_go_to_school_hour"] = Json::Value((double)i++); diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index 8fd25b66aa..58afd6dbfd 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/location_type.h" #include "abm/simulation.h" #include "abm/result_simulation.h" @@ -33,15 +34,15 @@ TEST(TestSimulation, advance_random) auto model = mio::abm::Model(num_age_groups); auto location1 = model.add_location(mio::abm::LocationType::School); auto location2 = model.add_location(mio::abm::LocationType::School); - auto p1 = model.add_person(location1, age_group_5_to_14); - auto p2 = model.add_person(location1, age_group_5_to_14); - auto p3 = model.add_person(location2, age_group_5_to_14); - auto p4 = model.add_person(location2, age_group_5_to_14); + auto p1 = model.add_person(location1, age_group_5_to_14, mio::abm::ActivityType::School); + auto p2 = model.add_person(location1, age_group_5_to_14, mio::abm::ActivityType::School); + auto p3 = model.add_person(location2, age_group_5_to_14, mio::abm::ActivityType::School); + auto p4 = model.add_person(location2, age_group_5_to_14, mio::abm::ActivityType::School); - model.assign_location(p1, location1); - model.assign_location(p2, location1); - model.assign_location(p3, location2); - model.assign_location(p4, location2); + model.assign_location(p1, location1, mio::abm::ActivityType::School); + model.assign_location(p2, location1, mio::abm::ActivityType::School); + model.assign_location(p3, location2, mio::abm::ActivityType::School); + model.assign_location(p4, location2, mio::abm::ActivityType::School); auto sim = mio::abm::Simulation(mio::abm::TimePoint(0), std::move(model)); @@ -78,39 +79,49 @@ TEST(TestSimulation, advanceWithCommonHistory) auto icu_id = model.add_location(mio::abm::LocationType::ICU); auto hospital_id = model.add_location(mio::abm::LocationType::Hospital); auto school_id = model.add_location(mio::abm::LocationType::School); - auto social_id = model.add_location(mio::abm::LocationType::SocialEvent); + auto social_id = model.add_location(mio::abm::LocationType::Recreation); auto basics_id = model.add_location(mio::abm::LocationType::BasicsShop); auto public_id = model.add_location(mio::abm::LocationType::PublicTransport); - auto person1 = add_test_person(model, home_id, age_group_5_to_14, mio::abm::InfectionState::Exposed); - auto person2 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Exposed); - auto person3 = add_test_person(model, home_id, age_group_35_to_59, mio::abm::InfectionState::Dead); - - model.assign_location(person1, home_id); - model.assign_location(person2, home_id); - model.assign_location(person3, home_id); - model.assign_location(person1, school_id); - model.assign_location(person2, work_id); - model.assign_location(person2, icu_id); - model.assign_location(person2, hospital_id); - model.assign_location(person1, social_id); - model.assign_location(person2, social_id); - model.assign_location(person3, social_id); - model.assign_location(person1, basics_id); - model.assign_location(person2, basics_id); - model.assign_location(person3, basics_id); - model.assign_location(person2, public_id); + auto person1 = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_5_to_14, + mio::abm::InfectionState::Exposed); + auto person2 = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Exposed); + auto person3 = add_test_person(model, home_id, mio::abm::ActivityType::Home, age_group_35_to_59, + mio::abm::InfectionState::Dead); + + model.assign_location(person1, home_id, mio::abm::ActivityType::Home); + model.assign_location(person2, home_id, mio::abm::ActivityType::Home); + model.assign_location(person3, home_id, mio::abm::ActivityType::Home); + model.assign_location(person1, school_id, mio::abm::ActivityType::School); + model.assign_location(person2, work_id, mio::abm::ActivityType::Work); + model.assign_location(person2, icu_id, mio::abm::ActivityType::ICU); + model.assign_location(person2, hospital_id, mio::abm::ActivityType::Hospital); + model.assign_location(person1, social_id, mio::abm::ActivityType::Recreation); + model.assign_location(person2, social_id, mio::abm::ActivityType::Recreation); + model.assign_location(person3, social_id, mio::abm::ActivityType::Recreation); + model.assign_location(person1, basics_id, mio::abm::ActivityType::BasicsShop); + model.assign_location(person2, basics_id, mio::abm::ActivityType::BasicsShop); + model.assign_location(person3, basics_id, mio::abm::ActivityType::BasicsShop); + model.assign_location(person2, public_id, mio::abm::ActivityType::PublicTransport); mio::abm::TripList& trip_list = model.get_trip_list(); // We add trips for person two to test the history and if it is working correctly - mio::abm::Trip trip1(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(2), home_id); - mio::abm::Trip trip2(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id); - mio::abm::Trip trip3(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(4), home_id); - mio::abm::Trip trip4(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(5), home_id); - mio::abm::Trip trip5(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(6), home_id); - mio::abm::Trip trip6(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(7), home_id); - mio::abm::Trip trip7(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(8), home_id); + mio::abm::Trip trip1(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(2), home_id, + mio::abm::ActivityType::Home); + mio::abm::Trip trip2(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, + mio::abm::ActivityType::Home); + mio::abm::Trip trip3(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(4), home_id, + mio::abm::ActivityType::Home); + mio::abm::Trip trip4(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(5), home_id, + mio::abm::ActivityType::Home); + mio::abm::Trip trip5(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(6), home_id, + mio::abm::ActivityType::Home); + mio::abm::Trip trip6(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(7), home_id, + mio::abm::ActivityType::Home); + mio::abm::Trip trip7(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(8), home_id, + mio::abm::ActivityType::Home); // Add to one vector auto trips = std::vector{trip1, trip2, trip3, trip4, trip5, trip6, trip7}; @@ -153,9 +164,9 @@ TEST(TestSimulation, ResultSimulation) // run a ResultSimulation on a minimal setup auto model = mio::abm::Model(num_age_groups); auto location = model.add_location(mio::abm::LocationType::Home); - auto person = model.add_person(location, age_group_15_to_34); + auto person = model.add_person(location, age_group_15_to_34, mio::abm::ActivityType::Home); - model.assign_location(person, location); + model.assign_location(person, location, mio::abm::ActivityType::Home); const auto t0 = mio::abm::TimePoint(0) + mio::abm::hours(100); const auto tmax = t0 + mio::abm::hours(50); diff --git a/cpp/tests/test_abm_testing_strategy.cpp b/cpp/tests/test_abm_testing_strategy.cpp index 64cbb88fc8..4e27d66cb3 100644 --- a/cpp/tests/test_abm_testing_strategy.cpp +++ b/cpp/tests/test_abm_testing_strategy.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/testing_strategy.h" #include "abm_helpers.h" #include "random_number_test.h" @@ -30,8 +31,8 @@ TEST_F(TestTestingCriteria, addRemoveAndEvaluateTestCriteria) // Create test locations and a person in a specific infection state. mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); - auto person = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); + auto person = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms); mio::abm::TimePoint t{0}; // Initialize testing criteria with no age group or infection state set. @@ -102,11 +103,11 @@ TEST_F(TestTestingScheme, runScheme) mio::abm::Location loc_work(mio::abm::LocationType::Work, 0, num_age_groups); // Since tests are performed before start_date, the InfectionState of all the Person have to take into account the test's required_time auto person1 = - make_test_person(this->get_rng(), loc_home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, - start_date - test_params_pcr.required_time); + make_test_person(this->get_rng(), loc_home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, start_date - test_params_pcr.required_time); auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person1); - auto person2 = make_test_person(this->get_rng(), loc_home, age_group_15_to_34, mio::abm::InfectionState::Recovered, - start_date - test_params_pcr.required_time); + auto person2 = make_test_person(this->get_rng(), loc_home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Recovered, start_date - test_params_pcr.required_time); auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person2); // Mock uniform distribution to control random behavior in testing. @@ -151,11 +152,11 @@ TEST_F(TestTestingScheme, initAndRunTestingStrategy) mio::abm::Location loc_work(mio::abm::LocationType::Work, 0); // Since tests are performed before start_date, the InfectionState of all the Person have to take into account the test's required_time auto person1 = - make_test_person(this->get_rng(), loc_work, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, - start_date - test_params_pcr.required_time); + make_test_person(this->get_rng(), loc_work, mio::abm::ActivityType::Work, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, start_date - test_params_pcr.required_time); auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person1); - auto person2 = make_test_person(this->get_rng(), loc_work, age_group_15_to_34, mio::abm::InfectionState::Recovered, - start_date - test_params_pcr.required_time); + auto person2 = make_test_person(this->get_rng(), loc_work, mio::abm::ActivityType::Work, age_group_15_to_34, + mio::abm::InfectionState::Recovered, start_date - test_params_pcr.required_time); auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person2); // Mock uniform distribution to control random behavior in testing. @@ -190,14 +191,14 @@ TEST_F(TestTestingCriteria, testingCriteriaEdgeCases) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); // Test with various infection states - auto person_exposed = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::Exposed); - auto person_symptoms = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); - auto person_no_symptoms = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - auto person_recovered = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::Recovered); + auto person_exposed = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Exposed); + auto person_symptoms = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms); + auto person_no_symptoms = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms); + auto person_recovered = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Recovered); mio::abm::TimePoint t{0}; @@ -217,14 +218,14 @@ TEST_F(TestTestingCriteria, testingCriteriaEdgeCases) auto testing_criteria_age = mio::abm::TestingCriteria(test_age_groups, {}); // Create persons with different age groups - auto person_young = - make_test_person(this->get_rng(), home, age_group_5_to_14, mio::abm::InfectionState::Susceptible); - auto person_adult = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::Susceptible); - auto person_older = - make_test_person(this->get_rng(), home, age_group_35_to_59, mio::abm::InfectionState::Susceptible); - auto person_senior = - make_test_person(this->get_rng(), home, age_group_60_to_79, mio::abm::InfectionState::Susceptible); + auto person_young = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_5_to_14, + mio::abm::InfectionState::Susceptible); + auto person_adult = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Susceptible); + auto person_older = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_35_to_59, + mio::abm::InfectionState::Susceptible); + auto person_senior = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, age_group_60_to_79, + mio::abm::InfectionState::Susceptible); // Should match only specified age groups EXPECT_EQ(testing_criteria_age.evaluate(person_young, t), false); @@ -236,12 +237,12 @@ TEST_F(TestTestingCriteria, testingCriteriaEdgeCases) auto testing_criteria_both = mio::abm::TestingCriteria(test_age_groups, test_infection_states); // Should match only when both criteria are met - auto person_adult_infected = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); - auto person_young_infected = - make_test_person(this->get_rng(), home, age_group_5_to_14, mio::abm::InfectionState::InfectedSymptoms); - auto person_adult_recovered = - make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::Recovered); + auto person_adult_infected = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, + age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); + auto person_young_infected = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, + age_group_5_to_14, mio::abm::InfectionState::InfectedSymptoms); + auto person_adult_recovered = make_test_person(this->get_rng(), home, mio::abm::ActivityType::Home, + age_group_15_to_34, mio::abm::InfectionState::Recovered); EXPECT_EQ(testing_criteria_both.evaluate(person_adult_infected, t), true); EXPECT_EQ(testing_criteria_both.evaluate(person_young_infected, t), false); @@ -289,7 +290,7 @@ TEST_F(TestTestingScheme, testingSchemeResultCaching) // Create test person and location mio::abm::Location loc_home(mio::abm::LocationType::Home, 0, num_age_groups); - auto person = make_test_person(this->get_rng(), loc_home, age_group_15_to_34, + auto person = make_test_person(this->get_rng(), loc_home, mio::abm::ActivityType::Home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, start_date); auto rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); @@ -342,13 +343,13 @@ TEST_F(TestTestingScheme, differentTestTypes) // Create test persons with different infection states mio::abm::Location loc_home(mio::abm::LocationType::Home, 0, num_age_groups); auto person_infected = - make_test_person(this->get_rng(), loc_home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, - start_date - test_params_pcr.required_time); + make_test_person(this->get_rng(), loc_home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::InfectedNoSymptoms, start_date - test_params_pcr.required_time); auto rng_infected = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person_infected); auto person_healthy = - make_test_person(this->get_rng(), loc_home, age_group_15_to_34, mio::abm::InfectionState::Susceptible, - start_date - test_params_pcr.required_time); + make_test_person(this->get_rng(), loc_home, mio::abm::ActivityType::Home, age_group_15_to_34, + mio::abm::InfectionState::Susceptible, start_date - test_params_pcr.required_time); auto rng_healthy = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person_healthy); // Mock uniform distribution to control test results @@ -420,11 +421,11 @@ TEST_F(TestTestingScheme, multipleSchemesCombination) // Add schemes to different location types test_strategy.add_scheme(mio::abm::LocationType::School, testing_scheme_children); test_strategy.add_scheme(mio::abm::LocationType::Work, testing_scheme_adults); - test_strategy.add_scheme(mio::abm::LocationType::SocialEvent, testing_scheme_seniors); + test_strategy.add_scheme(mio::abm::LocationType::Recreation, testing_scheme_seniors); // Also add multiple location types at once std::vector public_locations = {mio::abm::LocationType::BasicsShop, - mio::abm::LocationType::SocialEvent}; + mio::abm::LocationType::Recreation}; test_strategy.add_scheme(public_locations, testing_scheme_adults); // Create locations @@ -434,10 +435,10 @@ TEST_F(TestTestingScheme, multipleSchemesCombination) mio::abm::Location loc_shop(mio::abm::LocationType::BasicsShop, 3, num_age_groups); // Create persons of different age groups - auto child = make_test_person(this->get_rng(), loc_home, age_group_5_to_14, mio::abm::InfectionState::Susceptible, - start_date); - auto adult = make_test_person(this->get_rng(), loc_home, age_group_35_to_59, mio::abm::InfectionState::Susceptible, - start_date); + auto child = make_test_person(this->get_rng(), loc_home, mio::abm::ActivityType::Home, age_group_5_to_14, + mio::abm::InfectionState::Susceptible, start_date); + auto adult = make_test_person(this->get_rng(), loc_home, mio::abm::ActivityType::Home, age_group_35_to_59, + mio::abm::InfectionState::Susceptible, start_date); auto rng_child = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), child); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), adult); @@ -493,9 +494,9 @@ TEST_F(TestTestingScheme, locationSpecificSchemes) mio::abm::Location shop2(mio::abm::LocationType::BasicsShop, 43, num_age_groups); // Different ID // Create a test person - auto person = - make_test_person(this->get_rng(), shop1, age_group_15_to_34, mio::abm::InfectionState::Susceptible, start_date); - auto rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto person = make_test_person(this->get_rng(), shop1, mio::abm::ActivityType::BasicsShop, age_group_15_to_34, + mio::abm::InfectionState::Susceptible, start_date); + auto rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); // Mock uniform distribution to control test results ScopedMockDistribution>>> mock_uniform_dist; @@ -539,9 +540,9 @@ TEST_F(TestTestingScheme, testCompliance) mio::abm::Location shop1(mio::abm::LocationType::BasicsShop, 42, num_age_groups); // Has the specific ID // Create a test person - auto person = - make_test_person(this->get_rng(), shop1, age_group_15_to_34, mio::abm::InfectionState::Susceptible, start_date); - auto rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto person = make_test_person(this->get_rng(), shop1, mio::abm::ActivityType::BasicsShop, age_group_15_to_34, + mio::abm::InfectionState::Susceptible, start_date); + auto rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); person.set_compliance(mio::abm::InterventionType::Testing, 0.1); // Set compliance for testing // Mock uniform distribution to control test results diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 1e0c23210d..4c02d1c63b 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ +#include "abm/activity_type.h" #include "abm/location.h" #include "abm/location_id.h" #include "abm/mobility_data.h" @@ -52,11 +53,11 @@ TEST(TestGraphAbm, test_advance_node) auto home_id = model.add_location(mio::abm::LocationType::Home); auto& home = model.get_location(home_id); auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0), size_t(1), 2); - auto pid = model.add_person(home_id, mio::AgeGroup(0)); + auto pid = model.add_person(home_id, mio::AgeGroup(0), mio::abm::ActivityType::Home); auto index = model.get_person_index(pid); auto& p = model.get_person(pid); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p.set_assigned_location(work.get_type(), work.get_id(), 2); + p.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p.set_assigned_location(mio::abm::ActivityType::Work, work.get_id(), 2); mio::ABMSimulationNode node(MockHistory{}, t, std::move(model)); EXPECT_EQ(node.get_simulation().get_model().get_activeness_statuses()[index], true); node.advance(t, dt); @@ -87,8 +88,8 @@ TEST(TestGraphAbm, test_apply_mobility) auto home_id = model1.add_location(mio::abm::LocationType::Home); auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); auto work_id_3 = model3.add_location(mio::abm::LocationType::Work); - auto event_id_1 = model1.add_location(mio::abm::LocationType::SocialEvent); - auto event_id_2 = model2.add_location(mio::abm::LocationType::SocialEvent); + auto event_id_1 = model1.add_location(mio::abm::LocationType::Recreation); + auto event_id_2 = model2.add_location(mio::abm::LocationType::Recreation); auto& work_1 = model1.get_location(work_id_1); auto& work_2 = model2.get_location(work_id_2); auto& work_3 = model3.get_location(work_id_3); @@ -100,32 +101,31 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(work_2.get_model_id(), 2); EXPECT_EQ(work_3.get_model_id(), 3); - auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p5_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p1_id = model1.add_person(home_id, mio::AgeGroup(0), mio::abm::ActivityType::Home); + auto p2_id = model1.add_person(home_id, mio::AgeGroup(0), mio::abm::ActivityType::Home); + auto p3_id = model1.add_person(home_id, mio::AgeGroup(1), mio::abm::ActivityType::Home); + auto p4_id = model1.add_person(home_id, mio::AgeGroup(1), mio::abm::ActivityType::Home); + auto p5_id = model1.add_person(home_id, mio::AgeGroup(0), mio::abm::ActivityType::Home); auto& p1 = model1.get_person(p1_id); auto& p2 = model1.get_person(p2_id); auto& p3 = model1.get_person(p3_id); auto& p4 = model1.get_person(p4_id); auto& p5 = model1.get_person(p5_id); - p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); - p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); - p5.set_assigned_location(work_3.get_type(), work_3.get_id(), work_3.get_model_id()); - p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p3.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p4.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p5.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p3.set_assigned_location(event_1.get_type(), event_1.get_id(), event_1.get_model_id()); - p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); - + p1.set_assigned_location(mio::abm::ActivityType::Work, work_1.get_id(), work_1.get_model_id()); + p2.set_assigned_location(mio::abm::ActivityType::Work, work_2.get_id(), work_2.get_model_id()); + p5.set_assigned_location(mio::abm::ActivityType::Work, work_3.get_id(), work_3.get_model_id()); + p1.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p2.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p3.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p4.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p5.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p3.set_assigned_location(mio::abm::ActivityType::Recreation, event_1.get_id(), event_1.get_model_id()); + p4.set_assigned_location(mio::abm::ActivityType::Recreation, event_2.get_id(), event_2.get_model_id()); mio::abm::TripList& trips = model1.get_trip_list(); - mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), - mio::abm::TransportMode::Unknown, {}); - mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), - mio::abm::TransportMode::Unknown, {}); + mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, + mio::abm::ActivityType::Recreation, model1.get_id(), mio::abm::TransportMode::Unknown, {}); + mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, + mio::abm::ActivityType::Recreation, model2.get_id(), mio::abm::TransportMode::Unknown, {}); auto tripsvec = std::vector{trip1, trip2}; trips.add_trips(tripsvec); @@ -202,22 +202,22 @@ TEST(TestGraphABM, mask_compliance) //school and work require FFP2 masks school.set_required_mask(mio::abm::MaskType::FFP2); work.set_required_mask(mio::abm::MaskType::FFP2); - auto p_id1 = model.add_person(home_id, mio::AgeGroup(1)); - auto p_id2 = model.add_person(home_id, mio::AgeGroup(0)); + auto p_id1 = model.add_person(home_id, mio::AgeGroup(1), mio::abm::ActivityType::Home); + auto p_id2 = model.add_person(home_id, mio::AgeGroup(0), mio::abm::ActivityType::Home); auto& p1 = model.get_person(p_id1); auto& p2 = model.get_person(p_id2); - p1.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); - p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p2.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); - p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p1.set_assigned_location(mio::abm::ActivityType::Work, work.get_id(), work.get_model_id()); + p1.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); + p2.set_assigned_location(mio::abm::ActivityType::School, school.get_id(), school.get_model_id()); + p2.set_assigned_location(mio::abm::ActivityType::Home, home.get_id(), home.get_model_id()); //person is not compliant with mask p1.set_compliance(mio::abm::InterventionType::Mask, 0.0); p2.set_compliance(mio::abm::InterventionType::Mask, 0.0); //add trips for p2 mio::abm::TripList& trips = model.get_trip_list(); - mio::abm::Trip trip1(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), school_id, model.get_id(), - mio::abm::TransportMode::Unknown, {}); + mio::abm::Trip trip1(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), school_id, + mio::abm::ActivityType::School, model.get_id(), mio::abm::TransportMode::Unknown, {}); trips.add_trips({trip1}); @@ -246,8 +246,8 @@ TEST(TestGraphABM, test_get_person) mio::abm::GraphABModel(size_t(2), 0, std::vector{&mio::abm::go_to_work}); auto home = model.add_location(mio::abm::LocationType::Home); auto work = model.add_location(mio::abm::LocationType::Work); - auto pid1 = model.add_person(home, mio::AgeGroup(0)); - auto pid2 = model.add_person(work, mio::AgeGroup(1)); + auto pid1 = model.add_person(home, mio::AgeGroup(0), mio::abm::ActivityType::Home); + auto pid2 = model.add_person(work, mio::AgeGroup(1), mio::abm::ActivityType::Work); auto& p1 = model.get_person(pid1); EXPECT_EQ(p1.get_location(), home); diff --git a/docs/source/cpp/mobility_based_abm.rst b/docs/source/cpp/mobility_based_abm.rst index a854494fcb..783adbf4d0 100644 --- a/docs/source/cpp/mobility_based_abm.rst +++ b/docs/source/cpp/mobility_based_abm.rst @@ -123,13 +123,13 @@ level of the susceptible person. Mobility phase ~~~~~~~~~~~~~~ -During the mobility phase, each person may change their location. +During the mobility phase, each person may change their location. A person can move between its assigned locations according to different activity types that can but not have to match the location type. For instance, a location of ``LocationType::School`` can be assigned to students with ``ActivityType::School`` but also to teachers using ``ActivityType::Work``. -The available location types defined in `location_type.h `_ are: +The available location and activity types defined in `location_type.h `_ and `activity_type.h `_ , respectively, are: * **Home**: Home location of a person - * **School**: School location for children - * **Work**: Workplace for adults + * **School**: School location + * **Work**: Workplace * **SocialEvent**: Locations for social gatherings (e.g., parties, events) * **BasicsShop**: Basic shop for essential goods (e.g., grocery store) * **Hospital**: Hospital for severely infected persons @@ -164,7 +164,7 @@ The optional mobility rules consist of: * Going to a social event in the evening or on weekends * Going to a shop randomly during the day (except Sunday) -Another way of mobility is using trips. A trip consists of the ID of the person that performs this trip, a time point when this trip is performed, and the destination. +Another way of mobility is using trips. A trip consists of the ID of the person that performs this trip, a time point when this trip is performed, the destination and the activity type at the destination. At the beginning of the simulation, a list with all trips is initialized and followed during the simulation. The agents do the same trips every day. As before, agents that are in quarantine or in the hospital cannot change their location. Trips can be used even for locations that are not the assigned locations for the respective person. @@ -222,7 +222,7 @@ People are added with an age. Then we have to assign them, so the model knows th .. code-block:: cpp - auto person = model.add_person(home, age_group_0_to_4); + auto person = model.add_person(home, age_group_0_to_4, mio::abm::ActivityType::Home); person.set_assigned_location(home); Note that adding the person to the model in one location does not mean that this location is in the list of assigned locations the person can visit afterwards. diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp index 289395bf4c..9e11288217 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp @@ -19,6 +19,7 @@ */ //Includes from pymio +#include "abm/activity_type.h" #include "abm/person_id.h" #include "pybind_util.h" #include "utils/custom_index_array.h" @@ -60,7 +61,7 @@ PYBIND11_MODULE(_simulation_abm, m) .value("Home", mio::abm::LocationType::Home) .value("School", mio::abm::LocationType::School) .value("Work", mio::abm::LocationType::Work) - .value("SocialEvent", mio::abm::LocationType::SocialEvent) + .value("Recreation", mio::abm::LocationType::Recreation) .value("BasicsShop", mio::abm::LocationType::BasicsShop) .value("Hospital", mio::abm::LocationType::Hospital) .value("ICU", mio::abm::LocationType::ICU) @@ -68,6 +69,16 @@ PYBIND11_MODULE(_simulation_abm, m) .value("PublicTransport", mio::abm::LocationType::PublicTransport) .value("TransportWithoutContact", mio::abm::LocationType::TransportWithoutContact); + pymio::iterable_enum(m, "ActivityType") + .value("Home", mio::abm::ActivityType::Home) + .value("School", mio::abm::ActivityType::School) + .value("Work", mio::abm::ActivityType::Work) + .value("Recreation", mio::abm::ActivityType::Recreation) + .value("BasicsShop", mio::abm::ActivityType::BasicsShop) + .value("Hospital", mio::abm::ActivityType::Hospital) + .value("ICU", mio::abm::ActivityType::ICU) + .value("PublicTransport", mio::abm::ActivityType::PublicTransport); + pymio::iterable_enum(m, "TestType") .value("Generic", mio::abm::TestType::Generic) .value("Antigen", mio::abm::TestType::Antigen) @@ -147,7 +158,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def("index", &mio::abm::PersonId::get); pymio::bind_class(m, "Person") - .def("set_assigned_location", py::overload_cast( + .def("set_assigned_location", py::overload_cast( &mio::abm::Person::set_assigned_location)) .def_property_readonly("location", py::overload_cast<>(&mio::abm::Person::get_location, py::const_)) .def_property_readonly("age", &mio::abm::Person::get_age) @@ -186,11 +197,12 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_Range().get_persons())>(m, "_ModelPersonsRange"); pymio::bind_class(m, "Trip") - .def(py::init(), py::arg("person_id"), - py::arg("time"), py::arg("destination")) + .def(py::init(), + py::arg("person_id"), py::arg("time"), py::arg("destination"), py::arg("activity")) .def_readwrite("person_id", &mio::abm::Trip::person_id) .def_readwrite("trip_time", &mio::abm::Trip::trip_time) - .def_readwrite("destination", &mio::abm::Trip::destination); + .def_readwrite("destination", &mio::abm::Trip::destination) + .def_readwrite("activity", &mio::abm::Trip::activity); pymio::bind_class(m, "TripList") .def(py::init<>()) @@ -201,11 +213,14 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_class(m, "Model") .def(py::init()) .def("add_location", &mio::abm::Model::add_location, py::arg("location_type"), py::arg("num_cells") = 1) - .def("add_person", py::overload_cast(&mio::abm::Model::add_person), - py::arg("location_id"), py::arg("age_group")) + .def("add_person", + py::overload_cast( + &mio::abm::Model::add_person), + py::arg("location_id"), py::arg("age_group"), py::arg("activity_type")) .def("assign_location", - py::overload_cast(&mio::abm::Model::assign_location), - py::arg("person_id"), py::arg("location_id")) + py::overload_cast( + &mio::abm::Model::assign_location), + py::arg("person_id"), py::arg("location_id"), py::arg("activity_type")) .def_property_readonly("locations", py::overload_cast<>(&mio::abm::Model::get_locations, py::const_), py::keep_alive<1, 0>{}) //keep this model alive while contents are referenced in ranges .def_property_readonly("persons", py::overload_cast<>(&mio::abm::Model::get_persons, py::const_), diff --git a/pycode/memilio-simulation/tests/test_abm.py b/pycode/memilio-simulation/tests/test_abm.py index eca79085e3..40f8b2c39d 100644 --- a/pycode/memilio-simulation/tests/test_abm.py +++ b/pycode/memilio-simulation/tests/test_abm.py @@ -47,13 +47,9 @@ def test_locations(self): model = sim.model home_id = model.add_location(abm.LocationType.Home) - social_event_id = model.add_location(abm.LocationType.SocialEvent) self.assertEqual(len(model.locations), 3) home = model.locations[home_id.index()] - self.assertEqual(home.type, abm.LocationType.Home) - - testing_ages = [mio.AgeGroup(0)] home.infection_parameters.MaximumContacts = 10 self.assertEqual(home.infection_parameters.MaximumContacts, 10) @@ -65,10 +61,12 @@ def test_persons(self): model = sim.model home_id = model.add_location(abm.LocationType.Home) - social_event_id = model.add_location(abm.LocationType.SocialEvent) + social_event_id = model.add_location(abm.LocationType.Recreation) - p1_id = model.add_person(home_id, mio.AgeGroup(2)) - p2_id = model.add_person(social_event_id, mio.AgeGroup(5)) + p1_id = model.add_person( + home_id, mio.AgeGroup(2), abm.ActivityType.Home) + p2_id = model.add_person(social_event_id, mio.AgeGroup( + 5), abm.ActivityType.Recreation) p1 = model.persons[p1_id.index()] p2 = model.persons[p2_id.index()] @@ -88,19 +86,21 @@ def test_simulation(self): # add some locations and persons home_id = model.add_location(abm.LocationType.Home) - social_event_id = model.add_location(abm.LocationType.SocialEvent) + social_event_id = model.add_location(abm.LocationType.Recreation) work_id = model.add_location(abm.LocationType.Work) - p1_id = model.add_person(home_id, mio.AgeGroup(0)) - p2_id = model.add_person(home_id, mio.AgeGroup(2)) + p1_id = model.add_person( + home_id, mio.AgeGroup(0), abm.ActivityType.Home) + p2_id = model.add_person( + home_id, mio.AgeGroup(2), abm.ActivityType.Home) - for loc_id in [home_id, social_event_id, work_id]: - model.assign_location(p1_id, loc_id) - model.assign_location(p2_id, loc_id) + for loc in [(home_id, abm.ActivityType.Home), (social_event_id, abm.ActivityType.Recreation), (work_id, abm.ActivityType.Work)]: + model.assign_location(p1_id, loc[0], loc[1]) + model.assign_location(p2_id, loc[0], loc[1]) # trips trip_list = abm.TripList() trip_list.add_trips( - [abm.Trip(abm.PersonId(0), abm.TimePoint(0) + abm.hours(8), social_event_id), abm.Trip(abm.PersonId(1), abm.TimePoint(0) + abm.hours(8), work_id)]) + [abm.Trip(abm.PersonId(0), abm.TimePoint(0) + abm.hours(8), social_event_id, abm.ActivityType.Recreation), abm.Trip(abm.PersonId(1), abm.TimePoint(0) + abm.hours(8), work_id, abm.ActivityType.Work)]) model.trip_list = trip_list model.use_mobility_rules = False