From 5b8a0b7ccd79c478bd56ed5808ce4930ca81aced Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:02:04 +0100 Subject: [PATCH 01/54] add include providing uint32_t --- cpp/models/abm/movement_data.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/models/abm/movement_data.h b/cpp/models/abm/movement_data.h index 10ba131909..da25a1d831 100644 --- a/cpp/models/abm/movement_data.h +++ b/cpp/models/abm/movement_data.h @@ -22,13 +22,13 @@ #define ABM_MOVEMENT_DATA_H #include "abm/time.h" +#include namespace mio { namespace abm { - /** * @brief Mode of Transport. */ @@ -43,7 +43,6 @@ enum class TransportMode : uint32_t Unknown }; - /** * @brief Type of the activity. */ From 5d38f2f3465780dae6a776400a23c7034db3be5a Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:11:43 +0100 Subject: [PATCH 02/54] move interact and migrate_to to world --- cpp/models/abm/location.cpp | 32 ------------- cpp/models/abm/location.h | 10 ----- cpp/models/abm/person.cpp | 24 +--------- cpp/models/abm/person.h | 60 +++++++++++-------------- cpp/models/abm/world.cpp | 11 ++--- cpp/models/abm/world.h | 62 ++++++++++++++++++++++++++ cpp/tests/test_abm_location.cpp | 16 ++++--- cpp/tests/test_abm_masks.cpp | 4 +- cpp/tests/test_abm_migration_rules.cpp | 9 ++-- cpp/tests/test_abm_person.cpp | 11 ++--- cpp/tests/test_abm_world.cpp | 11 ++--- 11 files changed, 123 insertions(+), 127 deletions(-) diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index fef7fe0594..0ab4cbf39b 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -73,38 +73,6 @@ ScalarType Location::transmission_air_per_day(uint32_t cell_index, VirusVariant global_params.get()[{virus}]; } -void Location::interact(Person::RandomNumberGenerator& rng, Person& person, TimePoint t, TimeSpan dt, - const Parameters& global_params) const -{ - // TODO: we need to define what a cell is used for, as the loop may lead to incorrect results for multiple cells - auto age_receiver = person.get_age(); - ScalarType mask_protection = person.get_mask_protective_factor(global_params); - assert(person.get_cells().size() && "Person is in multiple cells. Interact logic is incorrect at the moment."); - for (auto cell_index : - person.get_cells()) { // TODO: the logic here is incorrect in case a person is in multiple cells - std::pair local_indiv_trans_prob[static_cast(VirusVariant::Count)]; - for (uint32_t v = 0; v != static_cast(VirusVariant::Count); ++v) { - VirusVariant virus = static_cast(v); - ScalarType local_indiv_trans_prob_v = - (std::min( - m_parameters.get(), - transmission_contacts_per_day(cell_index, virus, age_receiver, global_params.get_num_groups())) + - transmission_air_per_day(cell_index, virus, global_params)) * - (1 - mask_protection) * dt.days() * (1 - person.get_protection_factor(t, virus, global_params)); - - local_indiv_trans_prob[v] = std::make_pair(virus, local_indiv_trans_prob_v); - } - VirusVariant virus = - random_transition(rng, VirusVariant::Count, dt, - local_indiv_trans_prob); // use VirusVariant::Count for no virus submission - if (virus != VirusVariant::Count) { - person.add_new_infection(Infection(rng, virus, age_receiver, global_params, t + dt / 2, - mio::abm::InfectionState::Exposed, person.get_latest_protection(), - false)); // Starting time in first approximation - } - } -} - void Location::cache_exposure_rates(TimePoint t, TimeSpan dt, size_t num_agegroups) { //cache for next step so it stays constant during the step while subpopulations change diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 1c8f2d027c..fde21b1861 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -190,16 +190,6 @@ class Location */ ScalarType transmission_air_per_day(uint32_t cell_index, VirusVariant virus, const Parameters& global_params) const; - /** - * @brief A Person interacts with the population at this Location and may become infected. - * @param[in, out] rng Person::RandomNumberGenerator for this Person. - * @param[in, out] person The Person that interacts with the population. - * @param[in] dt Length of the current Simulation time step. - * @param[in] params Parameters of the Model. - */ - void interact(Person::RandomNumberGenerator& rng, Person& person, TimePoint t, TimeSpan dt, - const Parameters& params) const; - /** * @brief Add a Person to the population at this Location. * @param[in] person The Person arriving. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index dd493adfc1..dcc2663881 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -31,7 +31,7 @@ namespace mio namespace abm { -Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, uint32_t person_id) +Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, PersonID person_id) : m_location(&location) , m_assigned_locations((uint32_t)LocationType::Count, INVALID_LOCATION_INDEX) , m_quarantine(false) @@ -59,26 +59,6 @@ Person Person::copy_person(Location& location) return copied_person; } -void Person::interact(RandomNumberGenerator& rng, TimePoint t, TimeSpan dt, const Parameters& params) -{ - if (get_infection_state(t) == InfectionState::Susceptible) { // Susceptible - m_location->interact(rng, *this, t, dt, params); - } - m_time_at_location += dt; -} - -void Person::migrate_to(Location& loc_new, mio::abm::TransportMode transport_mode, const std::vector& cells) -{ - if (*m_location != loc_new) { - m_location->remove_person(*this); - m_location = &loc_new; - m_cells = cells; - loc_new.add_person(*this, cells); - m_time_at_location = TimeSpan(0); - m_last_transport_mode = transport_mode; - } -} - bool Person::is_infected(TimePoint t) const { if (m_infections.empty()) { @@ -218,7 +198,7 @@ bool Person::get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParam } } -uint32_t Person::get_person_id() +PersonID Person::get_person_id() { return m_person_id; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index c517d57265..e3a9b5f5f5 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -38,11 +38,13 @@ namespace mio namespace abm { +using PersonID = uint32_t; + struct LocationId; class Location; class Infection; -static constexpr uint32_t INVALID_PERSON_ID = std::numeric_limits::max(); +static constexpr PersonID INVALID_PERSON_ID = std::numeric_limits::max(); /** * @brief Agents in the simulated World that can carry and spread the Infection. @@ -73,7 +75,7 @@ class Person * @param id Id of the Person. * @param counter Reference to the Person's RNG Counter. */ - RandomNumberGenerator(Key key, uint32_t id, Counter& counter) + RandomNumberGenerator(Key key, PersonID id, Counter& counter) : m_key(key) , m_person_id(id) , m_counter(counter) @@ -117,7 +119,7 @@ class Person private: Key m_key; ///< Global RNG Key - uint32_t m_person_id; ///< Id of the Person + PersonID m_person_id; ///< Id of the Person Counter& m_counter; ///< Reference to the Person's rng counter }; @@ -129,7 +131,7 @@ class Person * @param[in] person_id Index of the Person. */ explicit Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, - uint32_t person_id = INVALID_PERSON_ID); + PersonID person_id = INVALID_PERSON_ID); /** * @brief Create a copy of this #Person object with a new Location. @@ -145,34 +147,6 @@ class Person return (m_person_id == other.m_person_id); } - /** - * @brief Time passes and the Person interacts with the population at its current Location. - * The Person might become infected. - * @param[in] t Current time. - * @param[in] dt Length of the current Simulation TimeStep. - * @param[in, out] params Infection parameters that are the same in all Location%s. - */ - void interact(RandomNumberGenerator& rng, TimePoint t, TimeSpan dt, const Parameters& params); - - /** - * @brief Migrate to a different Location. - * @param[in, out] loc_new The new Location of the Person. - * @param[in] cells_new The Cell%s that the Person visits at the new Location. - * */ - void migrate_to(Location& loc_new, const std::vector& cells_new = {0}) - { - migrate_to(loc_new, TransportMode::Unknown, cells_new); - } - - /** - * @brief Migrate to a different Location. - * @param[in] loc_new The new Location of the Person. - * @param[in] transport_mode The TransportMode the Person used to get to the new Location. - * @param[in] cells_new The Cell%s that the Person visits at the new Location. - * */ - void migrate_to(Location& loc_new, mio::abm::TransportMode transport_mode, - const std::vector& cells = {0}); - /** * @brief Get the latest #Infection of the Person. * @return The latest #Infection of the Person. @@ -231,6 +205,12 @@ class Person const Location& get_location() const; + void set_location(Location& location) + { + m_location = &location; + m_time_at_location = TimeSpan(0); + } + /** * @brief Get the time the Person has been at its current Location. * @return TimeSpan the Person has been at the Location. @@ -240,6 +220,11 @@ class Person return m_time_at_location; } + void add_time_at_location(const TimeSpan dt) + { + m_time_at_location += dt; + } + /** * @brief Get the time since the Person has been tested. * @return TimeSpan since the last test. @@ -355,7 +340,7 @@ class Person * The PersonID should correspond to the index in m_persons in world. * @return The PersonID. */ - uint32_t get_person_id(); + PersonID get_person_id(); /** * @brief Get index of Cell%s of the Person. @@ -463,6 +448,11 @@ class Person return m_last_transport_mode; } + void set_last_transport_mode(const mio::abm::TransportMode mode) + { + m_last_transport_mode = mode; + } + /** * Get this persons RandomNumberGenerator counter. * @see mio::abm::Person::RandomNumberGenerator. @@ -500,7 +490,7 @@ class Person auto obj = io.expect_object("Person"); auto loc = obj.expect_element("Location", mio::Tag{}); auto age = obj.expect_element("age", Tag{}); - auto id = obj.expect_element("id", Tag{}); + auto id = obj.expect_element("id", Tag{}); return apply( io, [](auto&& loc_, auto&& age_, auto&& id_) { @@ -526,7 +516,7 @@ class Person Mask m_mask; ///< The Mask of the Person. bool m_wears_mask = false; ///< Whether the Person currently wears a Mask. std::vector m_mask_compliance; ///< Vector of Mask compliance values for all #LocationType%s. - uint32_t m_person_id; ///< Id of the Person. + PersonID m_person_id; ///< Id of the 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. Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 03da243b27..4ecbeee0c4 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -67,9 +67,10 @@ void World::interaction(TimePoint t, TimeSpan dt) { PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { - auto&& person = m_persons[i]; - auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); - person->interact(personal_rng, t, dt, parameters); + auto&& person = m_persons[i]; + // auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); + // person->interact(personal_rng, t, dt, parameters); + interact(*person, person->get_location(), t, dt); } } @@ -90,7 +91,7 @@ void World::migration(TimePoint t, TimeSpan dt) target_location.get_number_persons() < target_location.get_capacity().persons) { bool wears_mask = person->apply_mask_intervention(personal_rng, target_location); if (wears_mask) { - person->migrate_to(target_location); + migrate(*person, target_location); } return true; } @@ -135,7 +136,7 @@ void World::migration(TimePoint t, TimeSpan dt) auto& target_location = get_individualized_location(trip.migration_destination); if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { person->apply_mask_intervention(personal_rng, target_location); - person->migrate_to(target_location, trip.trip_mode); + migrate(*person, target_location, trip.trip_mode); } } m_trip_list.increase_index(); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 52f41bad7f..791cd1d220 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -21,11 +21,13 @@ #define EPI_ABM_WORLD_H #include "abm/location_type.h" +#include "abm/movement_data.h" #include "abm/parameters.h" #include "abm/location.h" #include "abm/person.h" #include "abm/lockdown_rules.h" #include "abm/trip_list.h" +#include "abm/random_events.h" #include "abm/testing_strategy.h" #include "memilio/utils/pointer_dereferencing_iterator.h" #include "memilio/utils/random_number_generator.h" @@ -301,6 +303,66 @@ class World */ void remove_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme); + // std::unordered_map> m_cache_local_population; + + static void migrate(Person& person, Location& destination, TransportMode mode = TransportMode::Unknown, + const std::vector& cells = {0}) + { + if (person.get_location() == destination) { + return; + } + person.get_location().remove_person(person); + person.set_location(destination); + person.get_cells() = cells; + destination.add_person(person, cells); + person.set_last_transport_mode(mode); + } + + void interact(Person& person, Location& location, TimePoint t, TimeSpan dt) + { + auto personal_rng = Person::RandomNumberGenerator(m_rng, person); + interact(person, location, t, dt, personal_rng, parameters); + } + + static void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, + Person::RandomNumberGenerator personal_rng, Parameters global_parameters) + { + if (person.get_infection_state(t) == InfectionState::Susceptible) { + auto& local_parameters = location.get_infection_parameters(); + // TODO: we need to define what a cell is used for, as the loop may lead to incorrect results for multiple cells + auto age_receiver = person.get_age(); + ScalarType mask_protection = person.get_mask_protective_factor(global_parameters); + assert(person.get_cells().size() && + "Person is in multiple cells. Interact logic is incorrect at the moment."); + for (auto cell_index : + person.get_cells()) { // TODO: the logic here is incorrect in case a person is in multiple cells + std::pair local_indiv_trans_prob[static_cast(VirusVariant::Count)]; + for (uint32_t v = 0; v != static_cast(VirusVariant::Count); ++v) { + VirusVariant virus = static_cast(v); + ScalarType local_indiv_trans_prob_v = + (std::min(local_parameters.get(), + location.transmission_contacts_per_day(cell_index, virus, age_receiver, + global_parameters.get_num_groups())) + + location.transmission_air_per_day(cell_index, virus, global_parameters)) * + (1 - mask_protection) * dt.days() * + (1 - person.get_protection_factor(t, virus, global_parameters)); + + local_indiv_trans_prob[v] = std::make_pair(virus, local_indiv_trans_prob_v); + } + VirusVariant virus = + random_transition(personal_rng, VirusVariant::Count, dt, + local_indiv_trans_prob); // use VirusVariant::Count for no virus submission + if (virus != VirusVariant::Count) { + person.add_new_infection(Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2, + mio::abm::InfectionState::Exposed, + person.get_latest_protection(), + false)); // Starting time in first approximation + } + } + } + person.add_time_at_location(dt); + } + private: /** * @brief Person%s interact at their Location and may become infected. diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index d596fa8ada..904a732f40 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -19,7 +19,9 @@ */ #include "abm/infection.h" +#include "abm/movement_data.h" #include "abm/person.h" +#include "abm/world.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" #include @@ -74,9 +76,9 @@ TEST(TestLocation, addRemovePerson) home.add_person(person2, {0}); home.add_person(person3, {0}); - person1.migrate_to(location, {0, 1}); - person2.migrate_to(location, {0}); - person3.migrate_to(location, {0, 1}); + mio::abm::World::migrate(person1, location, mio::abm::TransportMode::Unknown, {0, 1}); + mio::abm::World::migrate(person2, location, mio::abm::TransportMode::Unknown, {0}); + mio::abm::World::migrate(person3, location, mio::abm::TransportMode::Unknown, {0, 1}); auto t = mio::abm::TimePoint(0); ASSERT_EQ(home.get_number_persons(), 0u); @@ -119,12 +121,12 @@ TEST(TestLocation, CacheExposureRate) auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); infected1.add_new_infection( mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - infected1.migrate_to(location, {0}); + mio::abm::World::migrate(infected1, location, mio::abm::TransportMode::Unknown, {0}); auto infected2 = mio::abm::Person(rng, home, age); auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); infected2.add_new_infection( mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - infected2.migrate_to(location, {0, 1}); + mio::abm::World::migrate(infected2, location, mio::abm::TransportMode::Unknown, {0, 1}); //cache precomputed results location.cache_exposure_rates(t, dt, NUM_AGE_GROUPS); @@ -268,12 +270,12 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); auto person_rng = mio::abm::Person::RandomNumberGenerator(rng, susceptible); - location.interact(person_rng, susceptible, t, dt, params); + mio::abm::World::interact(susceptible, location, t, dt, person_rng, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - location.interact(person_rng, susceptible, t, dt, params); + mio::abm::World::interact(susceptible, location, t, dt, person_rng, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); } diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index ab337bb7ac..9964c893a3 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -85,10 +85,10 @@ TEST(TestMasks, maskProtection) mock_exponential_dist; auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person1); - infection_location.interact(p1_rng, susc_person1, t, dt, params); + mio::abm::World::interact(susc_person1, infection_location, t, dt, p1_rng, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person2); - infection_location.interact(p2_rng, susc_person2, t, dt, params); + mio::abm::World::interact(susc_person2, infection_location, t, dt, p2_rng, params); // The person susc_person1 should have full protection against an infection, susc_person2 not ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 8cc9e324d8..7528c043ad 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/person.h" +#include "abm/world.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" @@ -396,8 +397,8 @@ TEST(TestMigrationRules, shop_return) auto p = make_test_person(home, AGE_GROUP_15_TO_34, mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); home.add_person(p); - p.migrate_to(shop); - p.interact(rng_p, t, dt, params); //person only returns home after some time passed + mio::abm::World::migrate(p, shop); + mio::abm::World::interact(p, p.get_location(), t, dt, rng_p, params); ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(NUM_AGE_GROUPS)), mio::abm::LocationType::Home); @@ -446,8 +447,8 @@ TEST(TestMigrationRules, event_return) auto p = mio::abm::Person(rng, home, AGE_GROUP_15_TO_34); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); home.add_person(p); - p.migrate_to(social_event); - p.interact(rng_p, t, dt, params); + mio::abm::World::migrate(p, social_event); + mio::abm::World::interact(p, p.get_location(), t, dt, rng_p, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(NUM_AGE_GROUPS)), mio::abm::LocationType::Home); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index fd14098de8..a8f077b49e 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/location_type.h" +#include "abm/movement_data.h" #include "abm/person.h" #include "abm_helpers.h" @@ -61,7 +62,7 @@ TEST(TestPerson, migrate) mio::abm::Location loc2(mio::abm::LocationType::School, 0, NUM_AGE_GROUPS); mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 0, 6, 2); auto person = make_test_person(home, AGE_GROUP_0_TO_4, mio::abm::InfectionState::Recovered); - person.migrate_to(loc1, {0}); + mio::abm::World::migrate(person, loc1); ASSERT_EQ(person.get_location(), loc1); ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); @@ -69,7 +70,7 @@ TEST(TestPerson, migrate) ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); ASSERT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); - person.migrate_to(loc2, mio::abm::TransportMode::Walking); + mio::abm::World::migrate(person, loc2, mio::abm::TransportMode::Walking); ASSERT_EQ(person.get_location(), loc2); ASSERT_EQ(loc2.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); @@ -77,7 +78,7 @@ TEST(TestPerson, migrate) ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); ASSERT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); - person.migrate_to(loc3, mio::abm::TransportMode::Bike, {0, 1}); + mio::abm::World::migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1}); ASSERT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); ASSERT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); @@ -195,7 +196,7 @@ TEST(TestPerson, getCells) mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 2); auto person = make_test_person(home, AGE_GROUP_15_TO_34, mio::abm::InfectionState::InfectedNoSymptoms); home.add_person(person); - person.migrate_to(location, {0, 1}); + mio::abm::World::migrate(person, location, mio::abm::TransportMode::Unknown, {0, 1}); ASSERT_EQ(person.get_cells().size(), 2); } @@ -210,7 +211,7 @@ TEST(TestPerson, interact) auto person = mio::abm::Person(rng, loc, AGE_GROUP_15_TO_34); auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days - person.interact(rng_person, t, dt, infection_parameters); + mio::abm::World::interact(person, person.get_location(), t, dt, rng_person, infection_parameters); EXPECT_EQ(person.get_time_at_location(), dt); } diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 026b1809db..59ed12c0aa 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/movement_data.h" #include "abm/person.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" @@ -309,9 +310,9 @@ TEST(TestWorld, evolveMigration) EXPECT_EQ(home.get_number_persons(), 1); EXPECT_EQ(hospital.get_number_persons(), 1); - p1.migrate_to(home); - p2.migrate_to(home); - p5.migrate_to(home); + mio::abm::World::migrate(p1, home); + mio::abm::World::migrate(p2, home); + mio::abm::World::migrate(p5, home); t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); world.get_trip_list().reset_index(); @@ -712,8 +713,8 @@ TEST(TestWorld, copyWorld) ASSERT_NE(&(copied_world.get_persons()[1].get_cells()), &world.get_persons()[1].get_cells()); // Evolve the world and check that the copied world has not evolved - copied_world.get_persons()[0].migrate_to(work, {0}); - copied_world.get_persons()[1].migrate_to(home, {0}); + mio::abm::World::migrate(copied_world.get_persons()[0], work, mio::abm::TransportMode::Unknown, {0}); + mio::abm::World::migrate(copied_world.get_persons()[1], home, mio::abm::TransportMode::Unknown, {0}); ASSERT_NE(copied_world.get_persons()[0].get_location().get_type(), world.get_persons()[0].get_location().get_type()); ASSERT_NE(copied_world.get_persons()[1].get_location().get_type(), From e3a30d58b7e0c0bb82c0509f245cde31a5cbfbbf Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:13:35 +0100 Subject: [PATCH 03/54] - remove location* from Person - we now identify Location using their LocationID - LocationID is now serializable - added a (naively implemented) get_location --- cpp/models/abm/location.h | 6 ++ cpp/models/abm/location_type.h | 43 +++++++++++++++ cpp/models/abm/person.cpp | 17 ++++-- cpp/models/abm/person.h | 18 +++--- cpp/models/abm/world.cpp | 2 +- cpp/models/abm/world.h | 63 +++++++++++++++++---- cpp/tests/test_abm_location.cpp | 24 ++++---- cpp/tests/test_abm_migration_rules.cpp | 22 ++++---- cpp/tests/test_abm_person.cpp | 39 +++++++------ cpp/tests/test_abm_world.cpp | 76 +++++++++++++------------- 10 files changed, 211 insertions(+), 99 deletions(-) diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index fde21b1861..d58d233b8d 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -385,6 +385,12 @@ class Location m_geographical_location = location; } + // return id by value. used to identify a location in a World + LocationId get_id() const + { + return m_id; + } + private: std::mutex m_mut; ///< Mutex to protect the list of persons from concurrent modification. LocationId m_id; ///< Id of the Location including type and index. diff --git a/cpp/models/abm/location_type.h b/cpp/models/abm/location_type.h index 9b685a9a7a..c583ba24db 100644 --- a/cpp/models/abm/location_type.h +++ b/cpp/models/abm/location_type.h @@ -20,6 +20,7 @@ #ifndef EPI_ABM_LOCATION_TYPE_H #define EPI_ABM_LOCATION_TYPE_H +#include "memilio/io/io.h" #include #include #include @@ -76,6 +77,48 @@ struct LocationId { } return (type < rhs.type); } + + // same interface as location + uint32_t get_index() const + { + return index; + } + + // same interface as location + LocationType get_type() const + { + return type; + } + + /** + * serialize this. + * @see mio::serialize + */ + template + void serialize(IOContext& io) const + { + auto obj = io.create_object("LocationId"); + obj.add_element("index", index); + obj.add_element("type", type); + } + + /** + * deserialize an object of this class. + * @see mio::deserialize + */ + template + static IOResult deserialize(IOContext& io) + { + auto obj = io.expect_object("LocationId"); + auto i = obj.expect_element("index", mio::Tag{}); + auto t = obj.expect_element("type", mio::Tag{}); + return apply( + io, + [](auto&& index_, auto&& type_) { + return LocationId{index_, type_}; + }, + i, t); + } }; struct GeographicalLocation { diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index dcc2663881..29b1c98527 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -32,7 +32,7 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, PersonID person_id) - : m_location(&location) + : m_location(location.get_id()) , m_assigned_locations((uint32_t)LocationType::Count, INVALID_LOCATION_INDEX) , m_quarantine(false) , m_age(age) @@ -54,7 +54,7 @@ Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age Person Person::copy_person(Location& location) { Person copied_person = Person(*this); - copied_person.m_location = &location; + copied_person.m_location = location.get_id(); location.add_person(*this); return copied_person; } @@ -87,14 +87,19 @@ void Person::add_new_infection(Infection&& inf) m_infections.push_back(std::move(inf)); } -Location& Person::get_location() +LocationId& Person::get_location() { - return *m_location; + return m_location; } -const Location& Person::get_location() const +const LocationId& Person::get_location() const { - return *m_location; + return m_location; +} + +void Person::set_location(const Location& location) +{ + set_location(location.get_id()); } const Infection& Person::get_infection() const diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index e3a9b5f5f5..26fed7db2f 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -201,13 +201,17 @@ class Person * @brief Get the current Location of the Person. * @return Current Location of the Person. */ - Location& get_location(); + LocationId& get_location(); - const Location& get_location() const; + const LocationId& get_location() const; - void set_location(Location& location) + // set new location, e.g. when migrating + void set_location(const Location& location); + + // set new location, e.g. when migrating + void set_location(LocationId id) { - m_location = &location; + m_location = id; m_time_at_location = TimeSpan(0); } @@ -475,7 +479,7 @@ class Person void serialize(IOContext& io) const { auto obj = io.create_object("Person"); - obj.add_element("Location", *m_location); + obj.add_element("Location", m_location); obj.add_element("age", m_age); obj.add_element("id", m_person_id); } @@ -488,7 +492,7 @@ class Person static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("Person"); - auto loc = obj.expect_element("Location", mio::Tag{}); + auto loc = obj.expect_element("Location", mio::Tag{}); auto age = obj.expect_element("age", Tag{}); auto id = obj.expect_element("id", Tag{}); return apply( @@ -500,7 +504,7 @@ class Person } private: - observer_ptr m_location; ///< Current Location of the Person. + LocationId m_location; ///< Current Location of the Person. 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 Vaccination%s the Person has received. diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 4ecbeee0c4..eba11c6862 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -70,7 +70,7 @@ void World::interaction(TimePoint t, TimeSpan dt) auto&& person = m_persons[i]; // auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); // person->interact(personal_rng, t, dt, parameters); - interact(*person, person->get_location(), t, dt); + interact(*person, t, dt); } } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 791cd1d220..5bce8cee57 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -34,6 +34,7 @@ #include "memilio/utils/stl_util.h" #include +#include #include #include #include @@ -67,6 +68,19 @@ class World { } + /** + * @brief Create a World. + * @param[in] params Initial simulation parameters. + */ + World(const Parameters& params) + : parameters(params.get_num_groups()) + , m_trip_list() + , m_use_migration_rules(true) + , m_cemetery_id(add_location(LocationType::Cemetery)) + { + parameters = params; + } + /** * @brief Create a copied World. * @param[in] other The World that needs to be copied. @@ -86,10 +100,9 @@ class World } for (auto& person : other.get_persons()) { // If a person is in this location, copy this person and add it to this location. - if (person.get_location() == origin_loc) { - LocationId origin_id = {origin_loc.get_index(), origin_loc.get_type()}; + if (person.get_location() == origin_loc.get_id()) { m_persons.push_back( - std::make_unique(person.copy_person(get_individualized_location(origin_id)))); + std::make_unique(person.copy_person(get_individualized_location(origin_loc.get_id())))); } } } @@ -305,27 +318,39 @@ class World // std::unordered_map> m_cache_local_population; - static void migrate(Person& person, Location& destination, TransportMode mode = TransportMode::Unknown, - const std::vector& cells = {0}) + // move a person to another location. this requires that location is part of this world. + void migrate(Person& person, Location& destination, TransportMode mode = TransportMode::Unknown, + const std::vector& cells = {0}) { - if (person.get_location() == destination) { + assert(get_location(destination.get_id()) == destination && + "Destination is outside of World."); // always true, but may trigger asserts in get_location + if (person.get_location() == destination.get_id()) { return; } - person.get_location().remove_person(person); + get_location(person).remove_person(person); person.set_location(destination); person.get_cells() = cells; destination.add_person(person, cells); person.set_last_transport_mode(mode); } - void interact(Person& person, Location& location, TimePoint t, TimeSpan dt) + // let a person interact with its current location + void interact(Person& person, TimePoint t, TimeSpan dt) { auto personal_rng = Person::RandomNumberGenerator(m_rng, person); - interact(person, location, t, dt, personal_rng, parameters); + interact(person, t, dt, personal_rng, parameters); + } + + // let a person interact with its current location + void interact(Person& person, TimePoint t, TimeSpan dt, Person::RandomNumberGenerator& personal_rng, + const Parameters& global_parameters) + { + interact(person, get_location(person), t, dt, personal_rng, global_parameters); } + // let a person interact with a location for and at some time static void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, - Person::RandomNumberGenerator personal_rng, Parameters global_parameters) + Person::RandomNumberGenerator& personal_rng, const Parameters& global_parameters) { if (person.get_infection_state(t) == InfectionState::Susceptible) { auto& local_parameters = location.get_infection_parameters(); @@ -363,6 +388,24 @@ class World person.add_time_at_location(dt); } + // get location by id + Location& get_location(LocationId id) + { + assert(id.index != INVALID_LOCATION_INDEX); + for (auto&& location : m_locations) { + if (location->get_id() == id) { + return *location; + } + } + assert(false && "Location id not found"); + } + + // get current location of the Person + Location& get_location(const Person& p) + { + return get_location(p.get_location()); + } + private: /** * @brief Person%s interact at their Location and may become infected. diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 904a732f40..10d1bdd078 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -65,8 +65,9 @@ TEST(TestLocation, getIndex) TEST(TestLocation, addRemovePerson) { - mio::abm::Location home(mio::abm::LocationType::Home, 0, 6, 1); - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 3); + mio::abm::World world(0); // num_agegroups is not needed in this test + auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); + auto& location = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 3)); auto person1 = make_test_person(home, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); auto person2 = make_test_person(home, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); @@ -76,9 +77,9 @@ TEST(TestLocation, addRemovePerson) home.add_person(person2, {0}); home.add_person(person3, {0}); - mio::abm::World::migrate(person1, location, mio::abm::TransportMode::Unknown, {0, 1}); - mio::abm::World::migrate(person2, location, mio::abm::TransportMode::Unknown, {0}); - mio::abm::World::migrate(person3, location, mio::abm::TransportMode::Unknown, {0, 1}); + world.migrate(person1, location, mio::abm::TransportMode::Unknown, {0, 1}); + world.migrate(person2, location, mio::abm::TransportMode::Unknown, {0}); + world.migrate(person3, location, mio::abm::TransportMode::Unknown, {0, 1}); auto t = mio::abm::TimePoint(0); ASSERT_EQ(home.get_number_persons(), 0u); @@ -113,20 +114,21 @@ TEST(TestLocation, CacheExposureRate) auto dt = mio::abm::seconds(10000); mio::abm::Parameters params = mio::abm::Parameters(NUM_AGE_GROUPS); + mio::abm::World world(params); // setup a location with some chance of exposure - mio::abm::Location home(mio::abm::LocationType::Home, 0, NUM_AGE_GROUPS, 1); - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, NUM_AGE_GROUPS, 3); + auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); + auto& location = world.get_location(world.add_location(mio::abm::LocationType::Home, 3)); auto infected1 = mio::abm::Person(rng, home, age); auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); infected1.add_new_infection( mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - mio::abm::World::migrate(infected1, location, mio::abm::TransportMode::Unknown, {0}); + world.migrate(infected1, location, mio::abm::TransportMode::Unknown, {0}); auto infected2 = mio::abm::Person(rng, home, age); auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); infected2.add_new_infection( mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - mio::abm::World::migrate(infected2, location, mio::abm::TransportMode::Unknown, {0, 1}); + world.migrate(infected2, location, mio::abm::TransportMode::Unknown, {0, 1}); //cache precomputed results location.cache_exposure_rates(t, dt, NUM_AGE_GROUPS); @@ -202,8 +204,8 @@ TEST(TestLocation, reachCapacity) world.evolve(t, dt); - ASSERT_EQ(p1.get_location(), school); - ASSERT_EQ(p2.get_location(), home); // p2 should not be able to enter the school + ASSERT_EQ(p1.get_location(), school.get_id()); + ASSERT_EQ(p2.get_location(), home.get_id()); // p2 should not be able to enter the school ASSERT_EQ(school.get_number_persons(), 1); ASSERT_EQ(home.get_number_persons(), 1); } diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 7528c043ad..5ecb784059 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -391,14 +391,15 @@ TEST(TestMigrationRules, shop_return) auto params = mio::abm::Parameters(NUM_AGE_GROUPS); auto t = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(9); auto dt = mio::abm::hours(1); + mio::abm::World world(params); - mio::abm::Location home(mio::abm::LocationType::Home, 0, NUM_AGE_GROUPS); - mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0, NUM_AGE_GROUPS); + auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home)); + auto& shop = world.get_location(world.add_location(mio::abm::LocationType::BasicsShop)); auto p = make_test_person(home, AGE_GROUP_15_TO_34, mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); home.add_person(p); - mio::abm::World::migrate(p, shop); - mio::abm::World::interact(p, p.get_location(), t, dt, rng_p, params); + world.migrate(p, shop); + world.interact(p, world.get_location(p), t, dt, rng_p, params); ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(NUM_AGE_GROUPS)), mio::abm::LocationType::Home); @@ -441,14 +442,15 @@ TEST(TestMigrationRules, event_return) auto params = mio::abm::Parameters(NUM_AGE_GROUPS); auto t = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(21); auto dt = mio::abm::hours(3); + mio::abm::World world(params); - mio::abm::Location home(mio::abm::LocationType::Home, 0, NUM_AGE_GROUPS); - mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 0, NUM_AGE_GROUPS); - auto p = mio::abm::Person(rng, home, AGE_GROUP_15_TO_34); - auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); + auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home)); + auto& social_event = world.get_location(world.add_location(mio::abm::LocationType::SocialEvent)); + auto p = mio::abm::Person(rng, home, AGE_GROUP_15_TO_34); + auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); home.add_person(p); - mio::abm::World::migrate(p, social_event); - mio::abm::World::interact(p, p.get_location(), t, dt, rng_p, params); + world.migrate(p, social_event); + world.interact(p, t, dt, rng_p, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(NUM_AGE_GROUPS)), mio::abm::LocationType::Home); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index a8f077b49e..8432520dfb 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -20,7 +20,7 @@ #include "abm/location_type.h" #include "abm/movement_data.h" #include "abm/person.h" - +#include "abm/world.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" #include @@ -34,7 +34,7 @@ TEST(TestPerson, init) auto person = mio::abm::Person(rng, location, AGE_GROUP_60_TO_79); ASSERT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); - ASSERT_EQ(person.get_location(), location); + ASSERT_EQ(person.get_location(), location.get_id()); ASSERT_EQ(person.get_person_id(), mio::abm::INVALID_PERSON_ID); } @@ -48,37 +48,40 @@ TEST(TestPerson, copyPerson) auto copied_person = person.copy_person(copied_location); ASSERT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); - ASSERT_EQ(copied_person.get_location(), copied_location); + ASSERT_EQ(copied_person.get_location(), copied_location.get_id()); ASSERT_EQ(copied_person.get_person_id(), mio::abm::INVALID_PERSON_ID); } TEST(TestPerson, migrate) { + mio::abm::World world(NUM_AGE_GROUPS); auto rng = mio::RandomNumberGenerator(); auto t = mio::abm::TimePoint(0); - mio::abm::Location home(mio::abm::LocationType::Home, 0, NUM_AGE_GROUPS); - mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 0, 6, 1); - mio::abm::Location loc2(mio::abm::LocationType::School, 0, NUM_AGE_GROUPS); - mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 0, 6, 2); + + auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home)); + auto& loc1 = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 1)); + auto& loc2 = world.get_location(world.add_location(mio::abm::LocationType::School)); + auto& loc3 = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 2)); + auto person = make_test_person(home, AGE_GROUP_0_TO_4, mio::abm::InfectionState::Recovered); - mio::abm::World::migrate(person, loc1); + world.migrate(person, loc1); - ASSERT_EQ(person.get_location(), loc1); + ASSERT_EQ(person.get_location(), loc1.get_id()); ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); ASSERT_EQ(home.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); ASSERT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); - mio::abm::World::migrate(person, loc2, mio::abm::TransportMode::Walking); + world.migrate(person, loc2, mio::abm::TransportMode::Walking); - ASSERT_EQ(person.get_location(), loc2); + ASSERT_EQ(person.get_location(), loc2.get_id()); ASSERT_EQ(loc2.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); ASSERT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); - mio::abm::World::migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1}); + world.migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1}); ASSERT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); ASSERT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); @@ -192,11 +195,15 @@ TEST(TestPerson, get_tested) TEST(TestPerson, getCells) { - mio::abm::Location home(mio::abm::LocationType::Home, 0, 6, 1); - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 2); + mio::abm::World world(NUM_AGE_GROUPS); + + auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); + auto& location = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 2)); + auto person = make_test_person(home, AGE_GROUP_15_TO_34, mio::abm::InfectionState::InfectedNoSymptoms); home.add_person(person); - mio::abm::World::migrate(person, location, mio::abm::TransportMode::Unknown, {0, 1}); + + world.migrate(person, location, mio::abm::TransportMode::Unknown, {0, 1}); ASSERT_EQ(person.get_cells().size(), 2); } @@ -211,7 +218,7 @@ TEST(TestPerson, interact) auto person = mio::abm::Person(rng, loc, AGE_GROUP_15_TO_34); auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days - mio::abm::World::interact(person, person.get_location(), t, dt, rng_person, infection_parameters); + mio::abm::World::interact(person, loc, t, dt, rng_person, infection_parameters); EXPECT_EQ(person.get_time_at_location(), dt); } diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 59ed12c0aa..8cbb85d33a 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -219,8 +219,8 @@ TEST(TestWorld, evolveMigration) world.evolve(t, dt); - EXPECT_EQ(p1.get_location(), work); - EXPECT_EQ(p2.get_location(), school); + EXPECT_EQ(p1.get_location(), work.get_id()); + EXPECT_EQ(p2.get_location(), school.get_id()); EXPECT_EQ(school.get_number_persons(), 1); EXPECT_EQ(work.get_number_persons(), 1); } @@ -300,30 +300,30 @@ TEST(TestWorld, evolveMigration) auto& home = world.get_individualized_location(home_id); auto& hospital = world.get_individualized_location(hospital_id); - EXPECT_EQ(p1.get_location(), work); - EXPECT_EQ(p2.get_location(), event); - EXPECT_EQ(p3.get_location(), hospital); - EXPECT_EQ(p4.get_location(), home); - EXPECT_EQ(p5.get_location(), event); + EXPECT_EQ(p1.get_location(), work.get_id()); + EXPECT_EQ(p2.get_location(), event.get_id()); + EXPECT_EQ(p3.get_location(), hospital.get_id()); + EXPECT_EQ(p4.get_location(), home.get_id()); + EXPECT_EQ(p5.get_location(), event.get_id()); EXPECT_EQ(event.get_number_persons(), 2); EXPECT_EQ(work.get_number_persons(), 1); EXPECT_EQ(home.get_number_persons(), 1); EXPECT_EQ(hospital.get_number_persons(), 1); - mio::abm::World::migrate(p1, home); - mio::abm::World::migrate(p2, home); - mio::abm::World::migrate(p5, home); + world.migrate(p1, home); + world.migrate(p2, home); + world.migrate(p5, home); t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); world.get_trip_list().reset_index(); world.evolve(t, dt); - EXPECT_EQ(p1.get_location(), work); - EXPECT_EQ(p2.get_location(), event); - EXPECT_EQ(p3.get_location(), home); - EXPECT_EQ(p4.get_location(), home); - EXPECT_EQ(p5.get_location(), event); + EXPECT_EQ(p1.get_location(), work.get_id()); + EXPECT_EQ(p2.get_location(), event.get_id()); + EXPECT_EQ(p3.get_location(), home.get_id()); + EXPECT_EQ(p4.get_location(), home.get_id()); + EXPECT_EQ(p5.get_location(), event.get_id()); EXPECT_EQ(event.get_number_persons(), 2); EXPECT_EQ(work.get_number_persons(), 1); EXPECT_EQ(home.get_number_persons(), 2); @@ -343,11 +343,11 @@ TEST(TestWorld, evolveMigration) world.evolve(t, dt); - EXPECT_EQ(p1.get_location(), event); - EXPECT_EQ(p2.get_location(), home); - EXPECT_EQ(p3.get_location(), home); - EXPECT_EQ(p4.get_location(), home); - EXPECT_EQ(p5.get_location(), work); + EXPECT_EQ(p1.get_location(), event.get_id()); + EXPECT_EQ(p2.get_location(), home.get_id()); + EXPECT_EQ(p3.get_location(), home.get_id()); + EXPECT_EQ(p4.get_location(), home.get_id()); + EXPECT_EQ(p5.get_location(), work.get_id()); EXPECT_EQ(event.get_number_persons(), 1); EXPECT_EQ(work.get_number_persons(), 1); EXPECT_EQ(home.get_number_persons(), 3); @@ -392,15 +392,15 @@ TEST(TestWorld, evolveMigration) // Check the dead person got burried and the severely infected person starts in Hospital world.evolve(t, dt); - EXPECT_EQ(p_dead.get_location().get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_dead).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(p_severe.get_location().get_type(), mio::abm::LocationType::Hospital); + EXPECT_EQ(world.get_location(p_severe).get_type(), mio::abm::LocationType::Hospital); // Check the dead person is still in Cemetery and the severely infected person dies and got burried world.evolve(t + dt, dt); - EXPECT_EQ(p_dead.get_location().get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_dead).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t + dt), mio::abm::InfectionState::Dead); - EXPECT_EQ(p_severe.get_location().get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_severe).get_type(), mio::abm::LocationType::Cemetery); } } @@ -658,14 +658,14 @@ TEST(TestWorld, copyWorld) world.get_locations()[2].get_cells()[0].m_persons[0]); ASSERT_EQ(copied_world.get_persons().size(), world.get_persons().size()); - ASSERT_EQ(copied_world.get_persons()[0].get_location().get_index(), - world.get_persons()[0].get_location().get_index()); - ASSERT_EQ(copied_world.get_persons()[1].get_location().get_index(), - world.get_persons()[1].get_location().get_index()); - ASSERT_EQ(copied_world.get_persons()[0].get_location().get_type(), - world.get_persons()[0].get_location().get_type()); - ASSERT_EQ(copied_world.get_persons()[1].get_location().get_type(), - world.get_persons()[1].get_location().get_type()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[0]).get_index(), + world.get_location(world.get_persons()[0]).get_index()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[1]).get_index(), + world.get_location(world.get_persons()[1]).get_index()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[0]).get_type(), + world.get_location(world.get_persons()[0]).get_type()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[1]).get_type(), + world.get_location(world.get_persons()[1]).get_type()); ASSERT_EQ(copied_world.get_persons()[0].get_infection().get_infection_state(mio::abm::TimePoint(0)), world.get_persons()[0].get_infection().get_infection_state(mio::abm::TimePoint(0))); ASSERT_EQ(copied_world.get_persons()[0].get_mask_compliance(mio::abm::LocationType::Home), @@ -713,10 +713,10 @@ TEST(TestWorld, copyWorld) ASSERT_NE(&(copied_world.get_persons()[1].get_cells()), &world.get_persons()[1].get_cells()); // Evolve the world and check that the copied world has not evolved - mio::abm::World::migrate(copied_world.get_persons()[0], work, mio::abm::TransportMode::Unknown, {0}); - mio::abm::World::migrate(copied_world.get_persons()[1], home, mio::abm::TransportMode::Unknown, {0}); - ASSERT_NE(copied_world.get_persons()[0].get_location().get_type(), - world.get_persons()[0].get_location().get_type()); - ASSERT_NE(copied_world.get_persons()[1].get_location().get_type(), - world.get_persons()[1].get_location().get_type()); + copied_world.migrate(copied_world.get_persons()[0], work, mio::abm::TransportMode::Unknown, {0}); + copied_world.migrate(copied_world.get_persons()[1], home, mio::abm::TransportMode::Unknown, {0}); + ASSERT_NE(copied_world.get_location(copied_world.get_persons()[0]).get_type(), + world.get_location(world.get_persons()[0]).get_type()); + ASSERT_NE(copied_world.get_location(copied_world.get_persons()[1]).get_type(), + world.get_location(world.get_persons()[1]).get_type()); } From ccfbf2d4f278998f1196e160e6b74858678affe9 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:20:47 +0100 Subject: [PATCH 04/54] [wip] remove m_persons from Location --- cpp/models/abm/common_abm_loggers.h | 3 +- cpp/models/abm/location.cpp | 34 +------ cpp/models/abm/location.h | 52 +++-------- cpp/models/abm/person.cpp | 9 +- cpp/models/abm/person.h | 4 +- cpp/models/abm/world.cpp | 15 ++-- cpp/models/abm/world.h | 75 +++++++++++++--- cpp/tests/test_abm_location.cpp | 120 +++++++++++++------------ cpp/tests/test_abm_masks.cpp | 4 +- cpp/tests/test_abm_migration_rules.cpp | 15 ++-- cpp/tests/test_abm_person.cpp | 48 ++++++---- cpp/tests/test_abm_world.cpp | 97 +++++++++++--------- 12 files changed, 255 insertions(+), 221 deletions(-) diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index c5029be6ea..93b1b53ee3 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -175,7 +175,8 @@ struct LogInfectionState : mio::LogAlways { PRAGMA_OMP(for) for (auto&& location : sim.get_world().get_locations()) { for (uint32_t inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { - sum[inf_state] += location.get_subpopulation(curr_time, mio::abm::InfectionState(inf_state)); + sum[inf_state] += + sim.get_world().get_subpopulation(location, curr_time, mio::abm::InfectionState(inf_state)); } } return std::make_pair(curr_time, sum); diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index 0ab4cbf39b..2948b39e1c 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -44,9 +44,8 @@ Location::Location(LocationId loc_id, size_t num_agegroups, uint32_t num_cells) Location Location::copy_location_without_persons(size_t num_agegroups) { - Location copy_loc = Location(*this); - copy_loc.m_persons = std::vector>(); - copy_loc.m_cells = std::vector{num_agegroups}; + Location copy_loc = Location(*this); + copy_loc.m_cells = std::vector{num_agegroups}; for (uint32_t idx = 0; idx < m_cells.size(); idx++) { copy_loc.set_capacity(get_capacity(idx).persons, get_capacity(idx).volume, idx); copy_loc.get_cached_exposure_rate_contacts(idx) = get_cached_exposure_rate_contacts(idx); @@ -98,28 +97,6 @@ void Location::cache_exposure_rates(TimePoint t, TimeSpan dt, size_t num_agegrou } } -void Location::add_person(Person& p, std::vector cells) -{ - std::lock_guard lk(m_mut); - m_persons.push_back(&p); - for (uint32_t cell_idx : cells) - m_cells[cell_idx].m_persons.push_back(&p); -} - -void Location::remove_person(Person& p) -{ - std::lock_guard lk(m_mut); - m_persons.erase(std::remove(m_persons.begin(), m_persons.end(), &p), m_persons.end()); - for (auto&& cell : m_cells) { - cell.m_persons.erase(std::remove(cell.m_persons.begin(), cell.m_persons.end(), &p), cell.m_persons.end()); - } -} - -size_t Location::get_number_persons() const -{ - return m_persons.size(); -} - /* For every cell in a location we have a transmission factor that is nomalized to m_capacity.volume / m_capacity.persons of the location "Home", which is 66. We multiply this rate with the individual size of each cell to obtain a "space per person" factor. @@ -141,12 +118,5 @@ size_t Cell::get_subpopulation(TimePoint t, InfectionState state) const }); } -size_t Location::get_subpopulation(TimePoint t, InfectionState state) const -{ - return count_if(m_persons.begin(), m_persons.end(), [&](observer_ptr p) { - return p->get_infection_state(t) == state; - }); -} - } // namespace abm } // namespace mio diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index d58d233b8d..616217ad7d 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -117,19 +117,14 @@ class Location } /** - * @brief Return a copy of the current Location object. - * @param[in] other The original #Location. + * @brief Construct a copy of a Location with a new ID. + * @param[in] other A Location. + * @param[in] id The ID for the new Location. */ - Location(const Location& other) - : m_id(other.m_id) - , m_capacity_adapted_transmission_risk(other.m_capacity_adapted_transmission_risk) - , m_parameters(other.m_parameters) - , m_persons(other.m_persons) - , m_cells(other.m_cells) - , m_required_mask(other.m_required_mask) - , m_npi_active(other.m_npi_active) - , m_geographical_location(other.m_geographical_location) + explicit Location(const Location& other, LocationId id) + : Location(other) { + m_id = id; } /** @@ -190,19 +185,6 @@ class Location */ ScalarType transmission_air_per_day(uint32_t cell_index, VirusVariant virus, const Parameters& global_params) const; - /** - * @brief Add a Person to the population at this Location. - * @param[in] person The Person arriving. - * @param[in] cell_idx [Default: 0] Index of the Cell the Person shall go to. - */ - void add_person(Person& person, std::vector cells = {0}); - - /** - * @brief Remove a Person from the population of this Location. - * @param[in] person The Person leaving. - */ - void remove_person(Person& person); - /** * @brief Prepare the Location for the next Simulation step. * @param[in] t Current TimePoint of the Simulation. @@ -234,6 +216,12 @@ class Location return m_cells; } + // TODO: remove and/or refactor + std::vector& get_cells() + { + return m_cells; + } + /** * @brief Get the type of Mask that is demanded when entering this Location. * @return Least secure MaskType that is demanded when entering this Location. @@ -353,20 +341,6 @@ class Location index, type); } - /** - * @brief Get the total number of Person%s at the Location. - * @return Number of Person%s. - */ - size_t get_number_persons() const; - - /** - * @brief Get the number of Person%s of a particular #InfectionState for all Cell%s. - * @param[in] t TimePoint of querry. - * @param[in] state #InfectionState of interest. - * @return Amount of Person%s of the #InfectionState in all Cell%s. - */ - size_t get_subpopulation(TimePoint t, InfectionState state) const; - /** * @brief Get the geographical location of the Location. * @return The geographical location of the Location. @@ -392,12 +366,10 @@ class Location } private: - std::mutex m_mut; ///< Mutex to protect the list of persons from concurrent modification. LocationId m_id; ///< Id of the Location including type and index. bool m_capacity_adapted_transmission_risk; /**< If true considers the LocationCapacity for the computation of the transmission risk.*/ LocalInfectionParameters m_parameters; ///< Infection parameters for the Location. - std::vector> m_persons{}; ///< A vector of all Person%s at the Location. std::vector m_cells{}; ///< A vector of all Cell%s that the Location is divided in. MaskType m_required_mask; ///< Least secure type of Mask that is needed to enter the Location. bool m_npi_active; ///< If true requires e.g. Mask%s to enter the Location. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 29b1c98527..a8be0b12c0 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -51,11 +51,16 @@ Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } +Person::Person(const Person& other, PersonID id) + : Person(other) +{ + m_person_id = id; +} + Person Person::copy_person(Location& location) { Person copied_person = Person(*this); copied_person.m_location = location.get_id(); - location.add_person(*this); return copied_person; } @@ -203,7 +208,7 @@ bool Person::get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParam } } -PersonID Person::get_person_id() +PersonID Person::get_person_id() const { return m_person_id; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 26fed7db2f..2524d97ab3 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -133,6 +133,8 @@ class Person explicit Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, PersonID person_id = INVALID_PERSON_ID); + explicit Person(const Person& other, PersonID id); + /** * @brief Create a copy of this #Person object with a new Location. * @param[in, out] location The new #Location of the Person. @@ -344,7 +346,7 @@ class Person * The PersonID should correspond to the index in m_persons in world. * @return The PersonID. */ - PersonID get_person_id(); + PersonID get_person_id() const; /** * @brief Get index of Cell%s of the Person. diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index eba11c6862..f52d8be8b9 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -50,7 +50,10 @@ Person& World::add_person(const LocationId id, AgeGroup age) m_persons.push_back(std::make_unique(m_rng, get_individualized_location(id), age, person_id)); auto& person = *m_persons.back(); person.set_assigned_location(m_cemetery_id); - get_individualized_location(id).add_person(person); + + // TODO: remove and/or refactor + get_location(id).get_cells()[0].m_persons.push_back(&person); + return person; } @@ -88,7 +91,7 @@ void World::migration(TimePoint t, TimeSpan dt) auto& current_location = person->get_location(); if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { if (target_location != current_location && - target_location.get_number_persons() < target_location.get_capacity().persons) { + get_number_persons(target_location.get_id()) < target_location.get_capacity().persons) { bool wears_mask = person->apply_mask_intervention(personal_rng, target_location); if (wears_mask) { migrate(*person, target_location); @@ -194,16 +197,16 @@ Location& World::find_location(LocationType type, const Person& person) size_t World::get_subpopulation_combined(TimePoint t, InfectionState s) const { return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, - [t, s](size_t running_sum, const std::unique_ptr& loc) { - return running_sum + loc->get_subpopulation(t, s); + [t, s, this](size_t running_sum, const std::unique_ptr& loc) { + return running_sum + get_subpopulation(loc->get_id(), t, s); }); } size_t World::get_subpopulation_combined_per_location_type(TimePoint t, InfectionState s, LocationType type) const { return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, - [t, s, type](size_t running_sum, const std::unique_ptr& loc) { - return loc->get_type() == type ? running_sum + loc->get_subpopulation(t, s) + [t, s, type, this](size_t running_sum, const std::unique_ptr& loc) { + return loc->get_type() == type ? running_sum + get_subpopulation(loc->get_id(), t, s) : running_sum; }); } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 5bce8cee57..250f43ee13 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -191,6 +192,19 @@ class World */ Person& add_person(const LocationId id, AgeGroup age); + // adds a copy of person to the world + Person& add_person(const Person& person) + { + uint32_t new_id = static_cast(m_persons.size()); + m_persons.push_back(std::make_unique(person, new_id)); + auto& new_person = m_persons.back(); + + // TODO: remove and/or refactor + get_location(new_person->get_location()).get_cells()[0].m_persons.push_back(&*new_person); + + return *new_person; + } + /** * @brief Get a range of all Location%s in the World. * @return A range of all Location%s. @@ -316,34 +330,71 @@ class World */ void remove_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme); - // std::unordered_map> m_cache_local_population; + Person& get_person(PersonID id) + { + assert(id == m_persons[id]->get_person_id()); + return *m_persons[id]; + } + + const Person& get_person(PersonID id) const + { + assert(id == m_persons[id]->get_person_id()); + return *m_persons[id]; + } + + size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const + { + // return std::count_if(get_local_population_cache_at(location).begin(), + // get_local_population_cache_at(location).end(), [&](const PersonID& pid) { + // return get_person(pid).get_infection_state(t) == state; + // }); + return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { + return p->get_location() == location && p->get_infection_state(t) == state; + }); + } + + inline size_t get_subpopulation(Location location, TimePoint t, InfectionState state) const + { + return get_subpopulation(location.get_id(), t, state); + } + + size_t get_number_persons(LocationId location) const + { + // return get_local_population_cache_at(location).size(); + return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { + return p->get_location() == location; + }); + } + + inline size_t get_number_persons(Location location) const + { + return get_number_persons(location.get_id()); + } // move a person to another location. this requires that location is part of this world. void migrate(Person& person, Location& destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { assert(get_location(destination.get_id()) == destination && - "Destination is outside of World."); // always true, but may trigger asserts in get_location + "Destination is outside of World."); // always true; used to trigger asserts in get_location() if (person.get_location() == destination.get_id()) { return; } - get_location(person).remove_person(person); person.set_location(destination); person.get_cells() = cells; - destination.add_person(person, cells); person.set_last_transport_mode(mode); } // let a person interact with its current location - void interact(Person& person, TimePoint t, TimeSpan dt) + inline void interact(Person& person, TimePoint t, TimeSpan dt) { auto personal_rng = Person::RandomNumberGenerator(m_rng, person); interact(person, t, dt, personal_rng, parameters); } // let a person interact with its current location - void interact(Person& person, TimePoint t, TimeSpan dt, Person::RandomNumberGenerator& personal_rng, - const Parameters& global_parameters) + inline void interact(Person& person, TimePoint t, TimeSpan dt, Person::RandomNumberGenerator& personal_rng, + const Parameters& global_parameters) { interact(person, get_location(person), t, dt, personal_rng, global_parameters); } @@ -391,17 +442,13 @@ class World // get location by id Location& get_location(LocationId id) { + // TODO: make sure this is correct assert(id.index != INVALID_LOCATION_INDEX); - for (auto&& location : m_locations) { - if (location->get_id() == id) { - return *location; - } - } - assert(false && "Location id not found"); + return *m_locations[id.index]; } // get current location of the Person - Location& get_location(const Person& p) + inline Location& get_location(const Person& p) { return get_location(p.get_location()); } diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 10d1bdd078..499b481fb7 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -26,29 +26,30 @@ #include "memilio/utils/random_number_generator.h" #include -TEST(TestLocation, init) -{ - mio::abm::Location location(mio::abm::LocationType::School, 0, NUM_AGE_GROUPS); - for (mio::abm::InfectionState i = mio::abm::InfectionState(0); i < mio::abm::InfectionState::Count; - i = mio::abm::InfectionState(size_t(i) + 1)) { - ASSERT_EQ(location.get_subpopulation(mio::abm::TimePoint(0), i), 0); - } - EXPECT_EQ(location.get_number_persons(), 0); -} +// TODO; this test no longer makes sense here, consider changing it and/or moving its contents to world +// TEST(TestLocation, init) +// { +// mio::abm::Location location(mio::abm::LocationType::School, 0, NUM_AGE_GROUPS); +// for (mio::abm::InfectionState i = mio::abm::InfectionState(0); i < mio::abm::InfectionState::Count; +// i = mio::abm::InfectionState(size_t(i) + 1)) { +// ASSERT_EQ(location.get_subpopulation(mio::abm::TimePoint(0), i), 0); +// } +// EXPECT_EQ(location.get_number_persons(), 0); +// } TEST(TestLocation, copyLocation) { auto location = mio::abm::Location(mio::abm::LocationType::School, 0, NUM_AGE_GROUPS); - auto person = make_test_person(location, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); - EXPECT_EQ(location.get_number_persons(), 0); - location.add_person(person); - EXPECT_EQ(location.get_number_persons(), 1); + // auto person = make_test_person(location, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); + // EXPECT_EQ(location.get_number_persons(), 0); + // location.add_person(person); + // EXPECT_EQ(location.get_number_persons(), 1); - auto copied_location = location.copy_location_without_persons(NUM_AGE_GROUPS); + auto copied_location = location; //.copy_location_without_persons(NUM_AGE_GROUPS); ASSERT_EQ(copied_location.get_type(), mio::abm::LocationType::School); ASSERT_EQ(copied_location.get_index(), location.get_index()); ASSERT_EQ(copied_location.get_cells().size(), location.get_cells().size()); - EXPECT_EQ(copied_location.get_number_persons(), 0); + // EXPECT_EQ(copied_location.get_number_persons(), 0); } TEST(TestLocation, initCell) @@ -63,41 +64,42 @@ TEST(TestLocation, getIndex) ASSERT_EQ((int)location.get_index(), 0); } -TEST(TestLocation, addRemovePerson) -{ - mio::abm::World world(0); // num_agegroups is not needed in this test - auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); - auto& location = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 3)); - - auto person1 = make_test_person(home, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); - auto person2 = make_test_person(home, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); - auto person3 = make_test_person(home, AGE_GROUP_35_TO_59, mio::abm::InfectionState::Exposed); - - home.add_person(person1, {0}); - home.add_person(person2, {0}); - home.add_person(person3, {0}); - - world.migrate(person1, location, mio::abm::TransportMode::Unknown, {0, 1}); - world.migrate(person2, location, mio::abm::TransportMode::Unknown, {0}); - world.migrate(person3, location, mio::abm::TransportMode::Unknown, {0, 1}); - - auto t = mio::abm::TimePoint(0); - ASSERT_EQ(home.get_number_persons(), 0u); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 2); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); - ASSERT_EQ(location.get_cells()[0].m_persons.size(), 3u); - ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); - ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); - - location.remove_person(person2); - - EXPECT_EQ(location.get_number_persons(), 2u); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 1); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); - ASSERT_EQ(location.get_cells()[0].m_persons.size(), 2u); - ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); - ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); -} +// TODO: Make sure removing this is correct, move if appropriate. reason: location no longer stores person +// TEST(TestLocation, addRemovePerson) +// { +// mio::abm::World world(0); // num_agegroups is not needed in this test +// auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); +// auto& location = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 3)); + +// auto person1 = make_test_person(home, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); +// auto person2 = make_test_person(home, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms); +// auto person3 = make_test_person(home, AGE_GROUP_35_TO_59, mio::abm::InfectionState::Exposed); + +// home.add_person(person1, {0}); +// home.add_person(person2, {0}); +// home.add_person(person3, {0}); + +// world.migrate(person1, location, mio::abm::TransportMode::Unknown, {0, 1}); +// world.migrate(person2, location, mio::abm::TransportMode::Unknown, {0}); +// world.migrate(person3, location, mio::abm::TransportMode::Unknown, {0, 1}); + +// auto t = mio::abm::TimePoint(0); +// ASSERT_EQ(home.get_number_persons(), 0u); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 2); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); +// ASSERT_EQ(location.get_cells()[0].m_persons.size(), 3u); +// ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); +// ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); + +// location.remove_person(person2); + +// EXPECT_EQ(location.get_number_persons(), 2u); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 1); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); +// ASSERT_EQ(location.get_cells()[0].m_persons.size(), 2u); +// ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); +// ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); +// } TEST(TestLocation, CacheExposureRate) { @@ -130,6 +132,11 @@ TEST(TestLocation, CacheExposureRate) mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); world.migrate(infected2, location, mio::abm::TransportMode::Unknown, {0, 1}); + // TODO: cells + location.get_cells()[0].m_persons.emplace_back(&infected1); + location.get_cells()[0].m_persons.emplace_back(&infected2); + location.get_cells()[1].m_persons.emplace_back(&infected2); + //cache precomputed results location.cache_exposure_rates(t, dt, NUM_AGE_GROUPS); @@ -206,8 +213,8 @@ TEST(TestLocation, reachCapacity) ASSERT_EQ(p1.get_location(), school.get_id()); ASSERT_EQ(p2.get_location(), home.get_id()); // p2 should not be able to enter the school - ASSERT_EQ(school.get_number_persons(), 1); - ASSERT_EQ(home.get_number_persons(), 1); + ASSERT_EQ(world.get_number_persons(school.get_id()), 1); + ASSERT_EQ(world.get_number_persons(home.get_id()), 1); } TEST(TestLocation, computeSpacePerPersonRelative) @@ -258,9 +265,12 @@ TEST(TestLocation, interact) auto infected3 = make_test_person(location, AGE_GROUP_5_TO_14, mio::abm::InfectionState::InfectedSymptoms, t, params); - location.add_person(infected1, {0}); - location.add_person(infected2, {0}); - location.add_person(infected3, {0}); + // TODO: is this really superseeded? + // location.add_person(infected1, {0}); + // location.add_person(infected2, {0}); + // location.add_person(infected3, {0}); + // TODO: no, not yet + location.get_cells()[0].m_persons = {&infected1, &infected2, &infected3}; //cache precomputed results location.cache_exposure_rates(t, dt, NUM_AGE_GROUPS); diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index 9964c893a3..8e5ca58dd4 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -70,7 +70,9 @@ TEST(TestMasks, maskProtection) auto infected1 = make_test_person(infection_location, AGE_GROUP_15_TO_34, mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior - infection_location.add_person(infected1); + // infection_location.add_person(infected1); + // TODO: Change this with cells + infection_location.get_cells()[0].m_persons.push_back(&infected1); //cache precomputed results auto dt = mio::abm::days(1); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 5ecb784059..b1a64d38c9 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -397,9 +397,10 @@ TEST(TestMigrationRules, shop_return) auto& shop = world.get_location(world.add_location(mio::abm::LocationType::BasicsShop)); auto p = make_test_person(home, AGE_GROUP_15_TO_34, mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); - home.add_person(p); - world.migrate(p, shop); - world.interact(p, world.get_location(p), t, dt, rng_p, params); + + auto& person = world.add_person(p); + world.migrate(person, shop); + world.interact(person, world.get_location(p), t, dt, rng_p, params); ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(NUM_AGE_GROUPS)), mio::abm::LocationType::Home); @@ -448,9 +449,11 @@ TEST(TestMigrationRules, event_return) auto& social_event = world.get_location(world.add_location(mio::abm::LocationType::SocialEvent)); auto p = mio::abm::Person(rng, home, AGE_GROUP_15_TO_34); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); - home.add_person(p); - world.migrate(p, social_event); - world.interact(p, t, dt, rng_p, params); + + // TODO: do we need a different add_person? e.g. something that behaves like emplace? + auto& person = world.add_person(p); + world.migrate(person, social_event); + world.interact(person, t, dt, rng_p, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(NUM_AGE_GROUPS)), mio::abm::LocationType::Home); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 8432520dfb..c4dbe3b8eb 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -52,6 +52,7 @@ TEST(TestPerson, copyPerson) ASSERT_EQ(copied_person.get_person_id(), mio::abm::INVALID_PERSON_ID); } +// TODO: move migration testing to world TEST(TestPerson, migrate) { mio::abm::World world(NUM_AGE_GROUPS); @@ -64,31 +65,40 @@ TEST(TestPerson, migrate) auto& loc2 = world.get_location(world.add_location(mio::abm::LocationType::School)); auto& loc3 = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 2)); - auto person = make_test_person(home, AGE_GROUP_0_TO_4, mio::abm::InfectionState::Recovered); + auto& person = world.add_person(make_test_person(home, AGE_GROUP_0_TO_4, mio::abm::InfectionState::Recovered)); world.migrate(person, loc1); + // TODO: cells + loc1.get_cells()[0].m_persons.emplace_back(&person); - ASSERT_EQ(person.get_location(), loc1.get_id()); - ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); - ASSERT_EQ(home.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); - ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); - ASSERT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); + EXPECT_EQ(person.get_location(), loc1.get_id()); + EXPECT_EQ(world.get_subpopulation(loc1, t, mio::abm::InfectionState::Recovered), 1); + EXPECT_EQ(world.get_subpopulation(home, t, mio::abm::InfectionState::Recovered), 0); + EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); + EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); world.migrate(person, loc2, mio::abm::TransportMode::Walking); + // TODO: cells + loc1.get_cells()[0].m_persons.clear(); + loc2.get_cells()[0].m_persons.emplace_back(&person); - ASSERT_EQ(person.get_location(), loc2.get_id()); - ASSERT_EQ(loc2.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); - ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); - ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); - ASSERT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); + EXPECT_EQ(person.get_location(), loc2.get_id()); + EXPECT_EQ(world.get_subpopulation(loc2, t, mio::abm::InfectionState::Recovered), 1); + EXPECT_EQ(world.get_subpopulation(loc1, t, mio::abm::InfectionState::Recovered), 0); + EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); + EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); world.migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1}); - - ASSERT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); - ASSERT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); - ASSERT_EQ(person.get_cells().size(), 2); - ASSERT_EQ(person.get_cells()[0], 0u); - ASSERT_EQ(person.get_cells()[1], 1u); - ASSERT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Bike); + // TODO: cells + loc3.get_cells()[0].m_persons.clear(); + loc3.get_cells()[0].m_persons.emplace_back(&person); + loc3.get_cells()[1].m_persons.emplace_back(&person); + + EXPECT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); + EXPECT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); + EXPECT_EQ(person.get_cells().size(), 2); + EXPECT_EQ(person.get_cells()[0], 0u); + EXPECT_EQ(person.get_cells()[1], 1u); + EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Bike); } TEST(TestPerson, setGetAssignedLocation) @@ -201,7 +211,7 @@ TEST(TestPerson, getCells) auto& location = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 2)); auto person = make_test_person(home, AGE_GROUP_15_TO_34, mio::abm::InfectionState::InfectedNoSymptoms); - home.add_person(person); + world.add_person(person); world.migrate(person, location, mio::abm::TransportMode::Unknown, {0, 1}); ASSERT_EQ(person.get_cells().size(), 2); diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 8cbb85d33a..56f8a336c7 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -221,8 +221,8 @@ TEST(TestWorld, evolveMigration) EXPECT_EQ(p1.get_location(), work.get_id()); EXPECT_EQ(p2.get_location(), school.get_id()); - EXPECT_EQ(school.get_number_persons(), 1); - EXPECT_EQ(work.get_number_persons(), 1); + EXPECT_EQ(world.get_number_persons(school), 1); + EXPECT_EQ(world.get_number_persons(work), 1); } { @@ -305,10 +305,10 @@ TEST(TestWorld, evolveMigration) EXPECT_EQ(p3.get_location(), hospital.get_id()); EXPECT_EQ(p4.get_location(), home.get_id()); EXPECT_EQ(p5.get_location(), event.get_id()); - EXPECT_EQ(event.get_number_persons(), 2); - EXPECT_EQ(work.get_number_persons(), 1); - EXPECT_EQ(home.get_number_persons(), 1); - EXPECT_EQ(hospital.get_number_persons(), 1); + EXPECT_EQ(world.get_number_persons(event), 2); + EXPECT_EQ(world.get_number_persons(work), 1); + EXPECT_EQ(world.get_number_persons(home), 1); + EXPECT_EQ(world.get_number_persons(hospital), 1); world.migrate(p1, home); world.migrate(p2, home); @@ -324,9 +324,9 @@ TEST(TestWorld, evolveMigration) EXPECT_EQ(p3.get_location(), home.get_id()); EXPECT_EQ(p4.get_location(), home.get_id()); EXPECT_EQ(p5.get_location(), event.get_id()); - EXPECT_EQ(event.get_number_persons(), 2); - EXPECT_EQ(work.get_number_persons(), 1); - EXPECT_EQ(home.get_number_persons(), 2); + EXPECT_EQ(world.get_number_persons(event), 2); + EXPECT_EQ(world.get_number_persons(work), 1); + EXPECT_EQ(world.get_number_persons(home), 2); bool weekend = true; mio::abm::Trip tripweekend1(p1.get_person_id(), @@ -348,9 +348,9 @@ TEST(TestWorld, evolveMigration) EXPECT_EQ(p3.get_location(), home.get_id()); EXPECT_EQ(p4.get_location(), home.get_id()); EXPECT_EQ(p5.get_location(), work.get_id()); - EXPECT_EQ(event.get_number_persons(), 1); - EXPECT_EQ(work.get_number_persons(), 1); - EXPECT_EQ(home.get_number_persons(), 3); + EXPECT_EQ(world.get_number_persons(event), 1); + EXPECT_EQ(world.get_number_persons(work), 1); + EXPECT_EQ(world.get_number_persons(home), 3); } // Test that a dead person cannot make a movement @@ -610,10 +610,14 @@ TEST(TestWorld, copyWorld) ASSERT_EQ(copied_world.get_locations()[2].get_index(), world.get_locations()[2].get_index()); ASSERT_EQ(copied_world.get_locations()[3].get_index(), world.get_locations()[3].get_index()); ASSERT_EQ(copied_world.get_locations()[4].get_index(), world.get_locations()[4].get_index()); - ASSERT_EQ(copied_world.get_locations()[1].get_number_persons(), world.get_locations()[1].get_number_persons()); - ASSERT_EQ(copied_world.get_locations()[2].get_number_persons(), world.get_locations()[2].get_number_persons()); - ASSERT_EQ(copied_world.get_locations()[3].get_number_persons(), world.get_locations()[3].get_number_persons()); - ASSERT_EQ(copied_world.get_locations()[4].get_number_persons(), world.get_locations()[4].get_number_persons()); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[1]), + world.get_number_persons(world.get_locations()[1])); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[2]), + world.get_number_persons(world.get_locations()[2])); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[3]), + world.get_number_persons(world.get_locations()[3])); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[4]), + world.get_number_persons(world.get_locations()[4])); ASSERT_EQ(copied_world.get_locations()[1].get_npi_active(), world.get_locations()[1].get_npi_active()); ASSERT_EQ(copied_world.get_locations()[2].get_npi_active(), world.get_locations()[2].get_npi_active()); ASSERT_EQ(copied_world.get_locations()[3].get_npi_active(), world.get_locations()[3].get_npi_active()); @@ -621,41 +625,46 @@ TEST(TestWorld, copyWorld) ASSERT_EQ(copied_world.get_locations()[1].get_required_mask(), world.get_locations()[1].get_required_mask()); ASSERT_EQ(copied_world.get_locations()[2].get_required_mask(), world.get_locations()[2].get_required_mask()); ASSERT_EQ( - copied_world.get_locations()[1].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed), - world.get_locations()[1].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); + copied_world.get_subpopulation(copied_world.get_locations()[1], mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[1], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[1], mio::abm::TimePoint(0), + mio::abm::InfectionState::Susceptible), + world.get_subpopulation(world.get_locations()[1], mio::abm::TimePoint(0), + mio::abm::InfectionState::Susceptible)); ASSERT_EQ( - copied_world.get_locations()[1].get_subpopulation(mio::abm::TimePoint(0), - mio::abm::InfectionState::Susceptible), - world.get_locations()[1].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Susceptible)); + copied_world.get_subpopulation(copied_world.get_locations()[2], mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[2], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[2], mio::abm::TimePoint(0), + mio::abm::InfectionState::Susceptible), + world.get_subpopulation(world.get_locations()[2], mio::abm::TimePoint(0), + mio::abm::InfectionState::Susceptible)); ASSERT_EQ( - copied_world.get_locations()[2].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed), - world.get_locations()[2].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); + copied_world.get_subpopulation(copied_world.get_locations()[3], mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[3], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); ASSERT_EQ( - copied_world.get_locations()[2].get_subpopulation(mio::abm::TimePoint(0), - mio::abm::InfectionState::Susceptible), - world.get_locations()[2].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Susceptible)); - ASSERT_EQ( - copied_world.get_locations()[3].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed), - world.get_locations()[3].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); - ASSERT_EQ( - copied_world.get_locations()[4].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed), - world.get_locations()[4].get_subpopulation(mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); + copied_world.get_subpopulation(copied_world.get_locations()[4], mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[4], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); ASSERT_EQ(copied_world.get_locations()[1].get_cells().size(), world.get_locations()[1].get_cells().size()); ASSERT_EQ(copied_world.get_locations()[2].get_cells().size(), world.get_locations()[2].get_cells().size()); ASSERT_EQ(copied_world.get_locations()[3].get_cells().size(), world.get_locations()[2].get_cells().size()); ASSERT_EQ(copied_world.get_locations()[4].get_cells().size(), world.get_locations()[2].get_cells().size()); - ASSERT_EQ(copied_world.get_locations()[1].get_cells()[0].m_persons.size(), - world.get_locations()[1].get_cells()[0].m_persons.size()); - ASSERT_EQ(copied_world.get_locations()[2].get_cells()[0].m_persons.size(), - world.get_locations()[2].get_cells()[0].m_persons.size()); - ASSERT_EQ(copied_world.get_locations()[3].get_cells()[0].m_persons.size(), - world.get_locations()[3].get_cells()[0].m_persons.size()); - ASSERT_EQ(copied_world.get_locations()[4].get_cells()[0].m_persons.size(), - world.get_locations()[4].get_cells()[0].m_persons.size()); - ASSERT_EQ(copied_world.get_locations()[1].get_cells()[0].m_persons[0], - world.get_locations()[1].get_cells()[0].m_persons[0]); - ASSERT_EQ(copied_world.get_locations()[2].get_cells()[0].m_persons[0], - world.get_locations()[2].get_cells()[0].m_persons[0]); + // TODO: cells are broken in World copy ctor + // ASSERT_EQ(copied_world.get_locations()[1].get_cells()[0].m_persons.size(), + // world.get_locations()[1].get_cells()[0].m_persons.size()); + // ASSERT_EQ(copied_world.get_locations()[2].get_cells()[0].m_persons.size(), + // world.get_locations()[2].get_cells()[0].m_persons.size()); + // ASSERT_EQ(copied_world.get_locations()[3].get_cells()[0].m_persons.size(), + // world.get_locations()[3].get_cells()[0].m_persons.size()); + // ASSERT_EQ(copied_world.get_locations()[4].get_cells()[0].m_persons.size(), + // world.get_locations()[4].get_cells()[0].m_persons.size()); + // ASSERT_EQ(copied_world.get_locations()[1].get_cells()[0].m_persons[0], + // world.get_locations()[1].get_cells()[0].m_persons[0]); + // ASSERT_EQ(copied_world.get_locations()[2].get_cells()[0].m_persons[0], + // world.get_locations()[2].get_cells()[0].m_persons[0]); ASSERT_EQ(copied_world.get_persons().size(), world.get_persons().size()); ASSERT_EQ(copied_world.get_location(world.get_persons()[0]).get_index(), From 89a574c0cdf6114adc1e5e25d651eed933de3ef1 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:25:09 +0100 Subject: [PATCH 05/54] Person::get_location returns id by value --- cpp/models/abm/person.cpp | 10 +++------- cpp/models/abm/person.h | 9 ++------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index a8be0b12c0..942d98d3e7 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -21,9 +21,10 @@ #include "abm/location_type.h" #include "abm/mask_type.h" #include "abm/parameters.h" -#include "abm/world.h" +#include "abm/infection.h" #include "abm/location.h" #include "memilio/utils/random_number_generator.h" + #include namespace mio @@ -92,12 +93,7 @@ void Person::add_new_infection(Infection&& inf) m_infections.push_back(std::move(inf)); } -LocationId& Person::get_location() -{ - return m_location; -} - -const LocationId& Person::get_location() const +LocationId Person::get_location() const { return m_location; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 2524d97ab3..b6c13a1ded 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -25,13 +25,10 @@ #include "abm/parameters.h" #include "abm/time.h" #include "abm/vaccine.h" -#include "abm/mask_type.h" #include "abm/mask.h" +#include "abm/movement_data.h" #include "memilio/epidemiology/age_group.h" #include "memilio/utils/random_number_generator.h" -#include "memilio/utils/memory.h" -#include "abm/movement_data.h" -#include namespace mio { @@ -203,9 +200,7 @@ class Person * @brief Get the current Location of the Person. * @return Current Location of the Person. */ - LocationId& get_location(); - - const LocationId& get_location() const; + LocationId get_location() const; // set new location, e.g. when migrating void set_location(const Location& location); From 124567df23cb99d7af39e64982f8cf017864f4df Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:03:47 +0100 Subject: [PATCH 06/54] add caching to replace person pointers in location also needed to reformat tests to use IDs instead of references --- cpp/models/abm/world.cpp | 32 ++++---- cpp/models/abm/world.h | 103 +++++++++++++++++++++---- cpp/tests/test_abm_location.cpp | 59 +++++++------- cpp/tests/test_abm_migration_rules.cpp | 28 +++---- cpp/tests/test_abm_world.cpp | 4 +- 5 files changed, 152 insertions(+), 74 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index d3903a8366..852bc4e9b0 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -19,15 +19,11 @@ */ #include "abm/world.h" #include "abm/location_type.h" -#include "abm/mask_type.h" #include "abm/person.h" #include "abm/location.h" #include "abm/migration_rules.h" -#include "abm/infection.h" -#include "abm/vaccine.h" #include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" -#include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" namespace mio @@ -40,21 +36,16 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) LocationId id = {static_cast(m_locations.size()), type}; m_locations.emplace_back(std::make_unique(id, parameters.get_num_groups(), num_cells)); m_has_locations[size_t(type)] = true; + + if (m_local_populations_cache.is_valid()) { + m_local_populations_cache.data[id]; + } return id; } Person& World::add_person(const LocationId id, AgeGroup age) { - assert(age.get() < parameters.get_num_groups()); - uint32_t person_id = static_cast(m_persons.size()); - m_persons.push_back(std::make_unique(m_rng, get_individualized_location(id), age, person_id)); - auto& person = *m_persons.back(); - person.set_assigned_location(m_cemetery_id); - - // TODO: remove and/or refactor - get_location(id).get_cells()[0].m_persons.push_back(&person); - - return person; + return add_person(Person(m_rng, get_location(id), age)); } void World::evolve(TimePoint t, TimeSpan dt) @@ -86,9 +77,9 @@ void World::migration(TimePoint t, TimeSpan dt) auto try_migration_rule = [&](auto rule) -> bool { //run migration rule and check if migration can actually happen - auto target_type = rule(personal_rng, *person, t, dt, parameters); - auto& target_location = find_location(target_type, *person); - auto& current_location = person->get_location(); + auto target_type = rule(personal_rng, *person, t, dt, parameters); + auto& target_location = find_location(target_type, *person); + auto current_location = person->get_location(); if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { if (target_location != current_location && get_number_persons(target_location.get_id()) < target_location.get_capacity().persons) { @@ -153,6 +144,13 @@ void World::migration(TimePoint t, TimeSpan dt) void World::begin_step(TimePoint t, TimeSpan dt) { m_testing_strategy.update_activity_status(t); + + if (!m_local_populations_cache.is_valid()) { + rebuild(); + m_local_populations_cache.validate(); + } + // PRAGMA_OMP(parallel for) + PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_locations.size(); ++i) { auto&& location = m_locations[i]; diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index b915c553ad..388dd7dbf8 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -26,7 +26,6 @@ #include "abm/parameters.h" #include "abm/location.h" #include "abm/person.h" -#include "abm/lockdown_rules.h" #include "abm/trip_list.h" #include "abm/random_events.h" #include "abm/testing_strategy.h" @@ -36,7 +35,6 @@ #include #include -#include #include #include #include @@ -197,13 +195,20 @@ class World // adds a copy of person to the world Person& add_person(const Person& person) { - uint32_t new_id = static_cast(m_persons.size()); + assert(person.get_age().get() < parameters.get_num_groups()); + assert(get_location(person).get_id() == person.get_location()); // asserts that location is on world + + uint32_t new_id = static_cast(m_persons.size()); // TODO: this breaks when removing a person m_persons.push_back(std::make_unique(person, new_id)); auto& new_person = m_persons.back(); + new_person->set_assigned_location(m_cemetery_id); // TODO: remove and/or refactor get_location(new_person->get_location()).get_cells()[0].m_persons.push_back(&*new_person); + if (m_local_populations_cache.is_valid()) { + m_local_populations_cache.data.at(new_person->get_location()).emplace(new_id); + } return *new_person; } @@ -219,6 +224,11 @@ class World */ Range> get_persons() const; + auto get_persons() -> Range> + { + return std::make_pair(PersonIterator(m_persons.begin()), PersonIterator(m_persons.end())); + } + /** * @brief Get an individualized Location. * @param[in] id LocationId of the Location. @@ -334,6 +344,7 @@ class World Person& get_person(PersonID id) { + assert(id < m_persons.size()); assert(id == m_persons[id]->get_person_id()); return *m_persons[id]; } @@ -346,10 +357,13 @@ class World size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { - // return std::count_if(get_local_population_cache_at(location).begin(), - // get_local_population_cache_at(location).end(), [&](const PersonID& pid) { - // return get_person(pid).get_infection_state(t) == state; - // }); + if (m_local_populations_cache.is_valid()) { + return std::count_if(m_local_populations_cache.data.at(location).begin(), + m_local_populations_cache.data.at(location).end(), [&](PersonID p) { + return get_person(p).get_infection_state(t) == state; + }); + } + return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p->get_location() == location && p->get_infection_state(t) == state; }); @@ -362,7 +376,9 @@ class World size_t get_number_persons(LocationId location) const { - // return get_local_population_cache_at(location).size(); + if (m_local_populations_cache.is_valid()) { + return m_local_populations_cache.data.at(location).size(); + } return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p->get_location() == location; }); @@ -374,17 +390,25 @@ class World } // move a person to another location. this requires that location is part of this world. + inline void migrate(PersonID person, LocationId destination, TransportMode mode = TransportMode::Unknown, + const std::vector& cells = {0}) + { + migrate(get_person(person), get_location(destination), mode, cells); + } + void migrate(Person& person, Location& destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { - assert(get_location(destination.get_id()) == destination && - "Destination is outside of World."); // always true; used to trigger asserts in get_location() - if (person.get_location() == destination.get_id()) { - return; + // TODO: this could be static without caching + if (person.get_location() != destination.get_id()) { + if (m_local_populations_cache.is_valid()) { + m_local_populations_cache.data.at(person.get_location()).erase(person.get_person_id()); + m_local_populations_cache.data.at(destination.get_id()).emplace(person.get_person_id()); + } + person.set_location(destination); + person.get_cells() = cells; + person.set_last_transport_mode(mode); } - person.set_location(destination); - person.get_cells() = cells; - person.set_last_transport_mode(mode); } // let a person interact with its current location @@ -469,6 +493,55 @@ class World */ void migration(TimePoint t, TimeSpan dt); + template + struct Cache { + T data; + bool m_is_valid = false; + mutable size_t m_hits = 0; + mutable size_t m_misses = 0; + + bool is_valid() const + { + if (m_is_valid) { + m_hits++; + } + else { + m_misses++; + } + return m_is_valid; + } + + void invalidate() + { + m_is_valid = false; + } + + void validate() + { + m_is_valid = true; + } + + // ~Cache() + // { + // std::cout << "hits: " << m_hits << " misses: " << m_misses << "\n"; + // } + }; + + void rebuild() + { + m_local_populations_cache.data.clear(); + for (size_t i = 0; i < m_locations.size(); i++) { + m_local_populations_cache.data[m_locations[i]->get_id()].clear(); + } + for (Person& person : get_persons()) { + m_local_populations_cache.data.at(person.get_location()).emplace(person.get_person_id()); + } + } + + Cache>> m_local_populations_cache; + // Cache>> m_air_exposure_rates_cache; + // Cache>> m_contact_exposure_rates_cache; + std::vector> m_persons; ///< Vector with pointers to every Person. std::vector> m_locations; ///< Vector with pointers to every Location. std::bitset diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 86c31fc8c4..81d878c183 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -24,7 +24,6 @@ #include "abm/world.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" -#include // TODO; this test no longer makes sense here, consider changing it and/or moving its contents to world // TEST(TestLocation, init) @@ -119,45 +118,51 @@ TEST(TestLocation, CacheExposureRate) mio::abm::World world(params); // setup a location with some chance of exposure - auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); - auto& location = world.get_location(world.add_location(mio::abm::LocationType::Home, 3)); - auto infected1 = mio::abm::Person(rng, home, age); + auto home = world.add_location(mio::abm::LocationType::Home, 1); + auto location = world.add_location(mio::abm::LocationType::Home, 3); + auto infected1 = mio::abm::Person(rng, world.get_location(home), age); auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); infected1.add_new_infection( mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - world.migrate(infected1, location, mio::abm::TransportMode::Unknown, {0}); - auto infected2 = mio::abm::Person(rng, home, age); + world.migrate(infected1, world.get_location(location), mio::abm::TransportMode::Unknown, {0}); + auto infected2 = mio::abm::Person(rng, world.get_location(home), age); auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); infected2.add_new_infection( mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - world.migrate(infected2, location, mio::abm::TransportMode::Unknown, {0, 1}); + world.migrate(infected2, world.get_location(location), mio::abm::TransportMode::Unknown, {0, 1}); // TODO: cells - location.get_cells()[0].m_persons.emplace_back(&infected1); - location.get_cells()[0].m_persons.emplace_back(&infected2); - location.get_cells()[1].m_persons.emplace_back(&infected2); + world.get_location(location).get_cells()[0].m_persons.emplace_back(&infected1); + world.get_location(location).get_cells()[0].m_persons.emplace_back(&infected2); + world.get_location(location).get_cells()[1].m_persons.emplace_back(&infected2); //cache precomputed results - location.cache_exposure_rates(t, dt, num_age_groups); - - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, 1e-14); - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.015015859523894731, 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), 0.0075079297619473654, + world.get_location(location).cache_exposure_rates(t, dt, num_age_groups); + + EXPECT_NEAR((world.get_location(location).get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), + 0.015015859523894731, 1e-14); + EXPECT_NEAR((world.get_location(location).get_cells()[0].m_cached_exposure_rate_air[{variant}]), + 0.015015859523894731, 1e-14); + EXPECT_NEAR((world.get_location(location).get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), + 0.0075079297619473654, 1e-14); + EXPECT_NEAR((world.get_location(location).get_cells()[1].m_cached_exposure_rate_air[{variant}]), + 0.0075079297619473654, 1e-14); + EXPECT_NEAR((world.get_location(location).get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.0075079297619473654, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); + EXPECT_NEAR((world.get_location(location).get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); // should also work with capacities - location.set_capacity_adapted_transmission_risk_flag(true); - location.set_capacity(2, 22, 0); // Capacity for Cell 1 - location.set_capacity(2, 22, 1); // Capacity for Cell 2 - location.set_capacity(2, 22, 2); // Capacity for Cell 3 - location.cache_exposure_rates(t, dt, num_age_groups); - - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); + world.get_location(location).set_capacity_adapted_transmission_risk_flag(true); + world.get_location(location).set_capacity(2, 22, 0); // Capacity for Cell 1 + world.get_location(location).set_capacity(2, 22, 1); // Capacity for Cell 2 + world.get_location(location).set_capacity(2, 22, 2); // Capacity for Cell 3 + world.get_location(location).cache_exposure_rates(t, dt, num_age_groups); + + EXPECT_NEAR((world.get_location(location).get_cells()[0].m_cached_exposure_rate_air[{variant}]), + 0.045047578571684191, 1e-14); + EXPECT_NEAR((world.get_location(location).get_cells()[1].m_cached_exposure_rate_air[{variant}]), + 0.022523789285842095, 1e-14); + EXPECT_NEAR((world.get_location(location).get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); } TEST(TestLocation, reachCapacity) diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 25d6fce4cd..2e020e0f63 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -411,16 +411,17 @@ TEST(TestMigrationRules, shop_return) auto dt = mio::abm::hours(1); mio::abm::World world(params); - auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home)); - auto& shop = world.get_location(world.add_location(mio::abm::LocationType::BasicsShop)); - auto p = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + auto home = world.add_location(mio::abm::LocationType::Home); + auto shop = world.add_location(mio::abm::LocationType::BasicsShop); + auto p = + make_test_person(world.get_location(home), age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); - auto& person = world.add_person(p); + auto person = world.add_person(p).get_person_id(); world.migrate(person, shop); - world.interact(person, world.get_location(p), t, dt, rng_p, params); + world.interact(world.get_person(person), t, dt, rng_p, params); - ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), + ASSERT_EQ(mio::abm::go_to_shop(rng_p, world.get_person(person), t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } @@ -463,17 +464,18 @@ TEST(TestMigrationRules, event_return) auto dt = mio::abm::hours(3); mio::abm::World world(params); - auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home)); - auto& social_event = world.get_location(world.add_location(mio::abm::LocationType::SocialEvent)); - auto p = mio::abm::Person(rng, home, age_group_15_to_34); - auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); + auto home = world.add_location(mio::abm::LocationType::Home); + auto social_event = world.add_location(mio::abm::LocationType::SocialEvent); + auto p = + mio::abm::Person(rng, world.get_location(home), age_group_15_to_34); // TODO: change Person ctor to LocationID + auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); // TODO: do we need a different add_person? e.g. something that behaves like emplace? - auto& person = world.add_person(p); + auto person = world.add_person(p).get_person_id(); world.migrate(person, social_event); - world.interact(person, t, dt, rng_p, params); + world.interact(world.get_person(person), t, dt, rng_p, params); - ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), + ASSERT_EQ(mio::abm::go_to_event(rng_p, world.get_person(person), t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index d75ea44cc9..38cfffec96 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -707,8 +707,8 @@ TEST(TestWorld, copyWorld) ASSERT_NE(&copied_world.get_persons()[0], &world.get_persons()[0]); ASSERT_NE(&copied_world.get_persons()[1], &world.get_persons()[1]); - ASSERT_NE(&(copied_world.get_persons()[0].get_location()), &world.get_persons()[0].get_location()); - ASSERT_NE(&(copied_world.get_persons()[1].get_location()), &world.get_persons()[1].get_location()); + ASSERT_NE(&copied_world.get_location((copied_world.get_persons()[0])), &world.get_location(world.get_persons()[0])); + ASSERT_NE(&copied_world.get_location((copied_world.get_persons()[1])), &world.get_location(world.get_persons()[1])); ASSERT_NE(&(copied_world.get_locations()[1]), &(world.get_locations()[1])); ASSERT_NE(&(copied_world.get_locations()[2]), &(world.get_locations()[2])); ASSERT_NE(&(copied_world.get_persons()[0].get_assigned_locations()), From f0d70a9274bbdf76d158d42cb5287a44e2e31858 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:03:58 +0100 Subject: [PATCH 07/54] change world interface to use mostly ids instead of references provide global interact/migrate functions, independent of world World::m_persons no longer uses pointers adjust tests and other files accordingly --- cpp/models/abm/CMakeLists.txt | 2 + cpp/models/abm/functions.cpp | 64 ++++++++++ cpp/models/abm/functions.h | 26 ++++ cpp/models/abm/household.cpp | 4 +- cpp/models/abm/location_type.h | 2 +- cpp/models/abm/person.cpp | 6 +- cpp/models/abm/person.h | 28 +++-- cpp/models/abm/world.cpp | 45 +++---- cpp/models/abm/world.h | 138 +++++++-------------- cpp/simulations/abm_braunschweig.cpp | 7 +- cpp/tests/abm_helpers.cpp | 13 +- cpp/tests/abm_helpers.h | 8 +- cpp/tests/test_abm_location.cpp | 91 +++++++------- cpp/tests/test_abm_masks.cpp | 4 +- cpp/tests/test_abm_migration_rules.cpp | 40 +++---- cpp/tests/test_abm_person.cpp | 120 ++++++++----------- cpp/tests/test_abm_simulation.cpp | 25 ++-- cpp/tests/test_abm_world.cpp | 158 +++++++++++++++---------- cpp/tests/test_json_serializer.cpp | 10 +- 19 files changed, 413 insertions(+), 378 deletions(-) create mode 100644 cpp/models/abm/functions.cpp create mode 100644 cpp/models/abm/functions.h diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index 60cb0b1ed9..59de1e7a5c 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(abm + functions.cpp + functions.h location.cpp location.h household.cpp diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp new file mode 100644 index 0000000000..6754deac00 --- /dev/null +++ b/cpp/models/abm/functions.cpp @@ -0,0 +1,64 @@ +#include "functions.h" + +#include "abm/random_events.h" +#include "abm/infection.h" + +namespace mio +{ + +namespace abm +{ + +void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, const Parameters& global_parameters, + Person::RandomNumberGenerator& personal_rng) +{ + if (person.get_infection_state(t) == InfectionState::Susceptible) { + auto& local_parameters = location.get_infection_parameters(); + // TODO: we need to define what a cell is used for, as the loop may lead to incorrect results for multiple cells + auto age_receiver = person.get_age(); + ScalarType mask_protection = person.get_mask_protective_factor(global_parameters); + assert(person.get_cells().size() && "Person is in multiple cells. Interact logic is incorrect at the moment."); + for (auto cell_index : + person.get_cells()) { // TODO: the logic here is incorrect in case a person is in multiple cells + std::pair local_indiv_trans_prob[static_cast(VirusVariant::Count)]; + for (uint32_t v = 0; v != static_cast(VirusVariant::Count); ++v) { + VirusVariant virus = static_cast(v); + ScalarType local_indiv_trans_prob_v = + (std::min(local_parameters.get(), + location.transmission_contacts_per_day(cell_index, virus, age_receiver, + global_parameters.get_num_groups())) + + location.transmission_air_per_day(cell_index, virus, global_parameters)) * + (1 - mask_protection) * dt.days() * (1 - person.get_protection_factor(t, virus, global_parameters)); + + local_indiv_trans_prob[v] = std::make_pair(virus, local_indiv_trans_prob_v); + } + VirusVariant virus = + random_transition(personal_rng, VirusVariant::Count, dt, + local_indiv_trans_prob); // use VirusVariant::Count for no virus submission + if (virus != VirusVariant::Count) { + person.add_new_infection(Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2, + mio::abm::InfectionState::Exposed, person.get_latest_protection(), + false)); // Starting time in first approximation + } + } + } + person.add_time_at_location(dt); +} + +bool migrate(Person& person, Location& destination, const std::vector& cells, TransportMode mode) +{ + if (person.get_location() != destination.get_id()) { + person.set_location(destination); + person.get_cells() = cells; + person.set_last_transport_mode(mode); + + return true; + } + else { + return false; + } +} + +} // namespace abm + +} // namespace mio \ No newline at end of file diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h new file mode 100644 index 0000000000..4f9b9b6e0c --- /dev/null +++ b/cpp/models/abm/functions.h @@ -0,0 +1,26 @@ +#ifndef FUNCTIONS_H_ +#define FUNCTIONS_H_ + +// TODO: find a meaningfull header name + +#include "abm/person.h" +#include "abm/location.h" + +namespace mio +{ + +namespace abm +{ + +// let a person interact with a location for and at some time +void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, const Parameters& global_parameters, + Person::RandomNumberGenerator& personal_rng); + +// move a person to another location. returns false if the person was at the target location already, true otherwise +bool migrate(Person& person, Location& destination, const std::vector& cells = {0}, + TransportMode mode = TransportMode::Unknown); +} // namespace abm + +} // namespace mio + +#endif \ No newline at end of file diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index 78c85ceeca..83bca30052 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -21,9 +21,7 @@ #include "abm/household.h" #include "abm/person.h" #include "abm/location.h" -#include "memilio/math/eigen.h" #include "memilio/utils/random_number_generator.h" -#include namespace mio { @@ -72,7 +70,7 @@ void add_household_to_world(World& world, const Household& household) std::tie(member, count) = memberTouple; for (int j = 0; j < count; j++) { auto age_group = pick_age_group_from_age_distribution(world.get_rng(), member.get_age_weights()); - auto& person = world.add_person(home, age_group); + auto& person = world.get_person(world.add_person(home, age_group)); person.set_assigned_location(home); } } diff --git a/cpp/models/abm/location_type.h b/cpp/models/abm/location_type.h index c583ba24db..417b024c85 100644 --- a/cpp/models/abm/location_type.h +++ b/cpp/models/abm/location_type.h @@ -58,7 +58,7 @@ static constexpr uint32_t INVALID_LOCATION_INDEX = std::numeric_limits */ struct LocationId { uint32_t index; - LocationType type; + LocationType type; // TODO: move to location bool operator==(const LocationId& rhs) const { diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 592e36b37f..d80422c0c5 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -31,7 +31,7 @@ namespace mio namespace abm { -Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, PersonID person_id) +Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, PersonId person_id) : m_location(location.get_id()) , m_assigned_locations((uint32_t)LocationType::Count, INVALID_LOCATION_INDEX) , m_quarantine_start(TimePoint(-(std::numeric_limits::max() / 2))) @@ -51,7 +51,7 @@ Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } -Person::Person(const Person& other, PersonID id) +Person::Person(const Person& other, PersonId id) : Person(other) { m_person_id = id; @@ -193,7 +193,7 @@ bool Person::get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParam } } -PersonID Person::get_person_id() const +PersonId Person::get_person_id() const { return m_person_id; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index db40c2374d..59d3946252 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -35,13 +35,13 @@ namespace mio namespace abm { -using PersonID = uint32_t; +using PersonId = uint32_t; // TODO: use type safe struct LocationId; class Location; class Infection; -static constexpr PersonID INVALID_PERSON_ID = std::numeric_limits::max(); +static constexpr PersonId INVALID_PERSON_ID = std::numeric_limits::max(); /** * @brief Agents in the simulated World that can carry and spread the Infection. @@ -72,7 +72,7 @@ class Person * @param id Id of the Person. * @param counter Reference to the Person's RNG Counter. */ - RandomNumberGenerator(Key key, PersonID id, Counter& counter) + RandomNumberGenerator(Key key, PersonId id, Counter& counter) : m_key(key) , m_person_id(id) , m_counter(counter) @@ -116,7 +116,7 @@ class Person private: Key m_key; ///< Global RNG Key - PersonID m_person_id; ///< Id of the Person + PersonId m_person_id; ///< Id of the Person Counter& m_counter; ///< Reference to the Person's rng counter }; @@ -128,9 +128,11 @@ class Person * @param[in] person_id Index of the Person. */ explicit Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, - PersonID person_id = INVALID_PERSON_ID); + PersonId person_id = INVALID_PERSON_ID); - explicit Person(const Person& other, PersonID id); + explicit Person(const Person& other, PersonId id); + + // TODO: re-add migrate_to, using mio::abm::migrate? /** * @brief Create a copy of this #Person object with a new Location. @@ -315,7 +317,7 @@ class Person { return t < m_quarantine_start + params.get(); } - + /** * @brief Removes the quarantine status of the Person. */ @@ -333,11 +335,11 @@ class Person bool get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParameters& params); /** - * @brief Get the PersonID of the Person. - * The PersonID should correspond to the index in m_persons in world. - * @return The PersonID. + * @brief Get the PersonId of the Person. + * The PersonId should correspond to the index in m_persons in world. + * @return The PersonId. */ - PersonID get_person_id() const; + PersonId get_person_id() const; /** * @brief Get index of Cell%s of the Person. @@ -487,7 +489,7 @@ class Person auto obj = io.expect_object("Person"); auto loc = obj.expect_element("Location", mio::Tag{}); auto age = obj.expect_element("age", Tag{}); - auto id = obj.expect_element("id", Tag{}); + auto id = obj.expect_element("id", Tag{}); return apply( io, [](auto&& loc_, auto&& age_, auto&& id_) { @@ -513,7 +515,7 @@ class Person Mask m_mask; ///< The Mask of the Person. bool m_wears_mask = false; ///< Whether the Person currently wears a Mask. std::vector m_mask_compliance; ///< Vector of Mask compliance values for all #LocationType%s. - PersonID m_person_id; ///< Id of the Person. + PersonId m_person_id; ///< Id of the 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. Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index ac5cc3db45..4f94f06fa0 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -43,7 +43,7 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) return id; } -Person& World::add_person(const LocationId id, AgeGroup age) +PersonId World::add_person(const LocationId id, AgeGroup age) { return add_person(Person(m_rng, get_location(id), age)); } @@ -61,10 +61,8 @@ void World::interaction(TimePoint t, TimeSpan dt) { PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { - auto&& person = m_persons[i]; - // auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); - // person->interact(personal_rng, t, dt, parameters); - interact(*person, t, dt); + // TODO: i == get_person(i).get_person_id(), but this does not have to stay true + interact(i, t, dt); } } @@ -73,19 +71,19 @@ void World::migration(TimePoint t, TimeSpan dt) PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; - auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); + auto personal_rng = Person::RandomNumberGenerator(m_rng, person); auto try_migration_rule = [&](auto rule) -> bool { //run migration rule and check if migration can actually happen - auto target_type = rule(personal_rng, *person, t, dt, parameters); - auto& target_location = find_location(target_type, *person); - auto current_location = person->get_location(); - if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { + auto target_type = rule(personal_rng, person, t, dt, parameters); + auto target_location = find_location(target_type, person); + auto current_location = person.get_location(); + if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { if (target_location != current_location && - get_number_persons(target_location.get_id()) < target_location.get_capacity().persons) { - bool wears_mask = person->apply_mask_intervention(personal_rng, target_location); + get_number_persons(target_location) < get_location(target_location).get_capacity().persons) { + bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { - migrate(*person, target_location); + migrate(person.get_person_id(), target_location); // TODO: i == PersonId, use? } return true; } @@ -125,12 +123,12 @@ void World::migration(TimePoint t, TimeSpan dt) m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { auto& trip = m_trip_list.get_next_trip(weekend); auto& person = m_persons[trip.person_id]; - auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); - if (!person->is_in_quarantine(t, parameters) && person->get_infection_state(t) != InfectionState::Dead) { + auto personal_rng = Person::RandomNumberGenerator(m_rng, person); + if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { auto& target_location = get_individualized_location(trip.migration_destination); - if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { - person->apply_mask_intervention(personal_rng, target_location); - migrate(*person, target_location, trip.trip_mode); + if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + person.apply_mask_intervention(personal_rng, target_location); + migrate(person.get_person_id(), target_location.get_id(), trip.trip_mode); } } m_trip_list.increase_index(); @@ -178,18 +176,11 @@ Location& World::get_individualized_location(LocationId id) return *m_locations[id.index]; } -const Location& World::find_location(LocationType type, const Person& person) const +LocationId World::find_location(LocationType type, const Person& person) const { auto index = person.get_assigned_location_index(type); assert(index != INVALID_LOCATION_INDEX && "unexpected error."); - return get_individualized_location({index, type}); -} - -Location& World::find_location(LocationType type, const Person& person) -{ - auto index = person.get_assigned_location_index(type); - assert(index != INVALID_LOCATION_INDEX && "unexpected error."); - return get_individualized_location({index, type}); + return {index, type}; } size_t World::get_subpopulation_combined(TimePoint t, InfectionState s) const diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 388dd7dbf8..4f327ee58a 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -29,10 +29,13 @@ #include "abm/trip_list.h" #include "abm/random_events.h" #include "abm/testing_strategy.h" +#include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/pointer_dereferencing_iterator.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" +#include "functions.h" + #include #include #include @@ -53,8 +56,8 @@ class World public: using LocationIterator = PointerDereferencingIterator>::iterator>; using ConstLocationIterator = PointerDereferencingIterator>::const_iterator>; - using PersonIterator = PointerDereferencingIterator>::iterator>; - using ConstPersonIterator = PointerDereferencingIterator>::const_iterator>; + using PersonIterator = std::vector::iterator; + using ConstPersonIterator = std::vector::const_iterator; /** * @brief Create a World. @@ -102,8 +105,9 @@ class World for (auto& person : other.get_persons()) { // If a person is in this location, copy this person and add it to this location. if (person.get_location() == origin_loc.get_id()) { - m_persons.push_back( - std::make_unique(person.copy_person(get_individualized_location(origin_loc.get_id())))); + const auto id = m_persons.size(); + m_persons.emplace_back(person, id); + // std::make_unique(person.copy_person(get_individualized_location(origin_loc.get_id())))); } } } @@ -190,26 +194,27 @@ class World * @param[in] age AgeGroup of the person. * @return Reference to the newly created Person. */ - Person& add_person(const LocationId id, AgeGroup age); + PersonId add_person(const LocationId id, AgeGroup age); // adds a copy of person to the world - Person& add_person(const Person& person) + PersonId add_person(const Person& person) { + mio::unused(get_location( + person.get_location())); // assert that location is in world // TODO: check if this acts like an assert assert(person.get_age().get() < parameters.get_num_groups()); - assert(get_location(person).get_id() == person.get_location()); // asserts that location is on world uint32_t new_id = static_cast(m_persons.size()); // TODO: this breaks when removing a person - m_persons.push_back(std::make_unique(person, new_id)); + m_persons.emplace_back(person, new_id); auto& new_person = m_persons.back(); - new_person->set_assigned_location(m_cemetery_id); + new_person.set_assigned_location(m_cemetery_id); // TODO: remove and/or refactor - get_location(new_person->get_location()).get_cells()[0].m_persons.push_back(&*new_person); + get_location(new_person.get_location()).get_cells()[0].m_persons.push_back(&new_person); if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(new_person->get_location()).emplace(new_id); + m_local_populations_cache.data.at(new_person.get_location()).emplace(new_id); } - return *new_person; + return new_id; } /** @@ -223,10 +228,9 @@ class World * @return A range of all Person%s. */ Range> get_persons() const; - - auto get_persons() -> Range> + Range> get_persons() { - return std::make_pair(PersonIterator(m_persons.begin()), PersonIterator(m_persons.end())); + return make_range(m_persons.begin(), m_persons.end()); } /** @@ -244,9 +248,7 @@ class World * @param[in] person The Person. * @return Reference to the assigned Location. */ - const Location& find_location(LocationType type, const Person& person) const; - - Location& find_location(LocationType type, const Person& person); + LocationId find_location(LocationType type, const Person& person) const; /** * @brief Get the number of Persons in one #InfectionState at all Location%s. @@ -342,30 +344,31 @@ class World */ void remove_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme); - Person& get_person(PersonID id) + Person& get_person(PersonId id) { - assert(id < m_persons.size()); - assert(id == m_persons[id]->get_person_id()); - return *m_persons[id]; + assert(id == m_persons[id].get_person_id()); + assert((size_t)id < m_persons.size()); + return m_persons[id]; } - const Person& get_person(PersonID id) const + const Person& get_person(PersonId id) const { - assert(id == m_persons[id]->get_person_id()); - return *m_persons[id]; + assert(id == m_persons[id].get_person_id()); + assert((size_t)id < m_persons.size()); + return m_persons[id]; } size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { if (m_local_populations_cache.is_valid()) { return std::count_if(m_local_populations_cache.data.at(location).begin(), - m_local_populations_cache.data.at(location).end(), [&](PersonID p) { + m_local_populations_cache.data.at(location).end(), [&](PersonId p) { return get_person(p).get_infection_state(t) == state; }); } return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { - return p->get_location() == location && p->get_infection_state(t) == state; + return p.get_location() == location && p.get_infection_state(t) == state; }); } @@ -380,7 +383,7 @@ class World return m_local_populations_cache.data.at(location).size(); } return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { - return p->get_location() == location; + return p.get_location() == location; }); } @@ -390,79 +393,29 @@ class World } // move a person to another location. this requires that location is part of this world. - inline void migrate(PersonID person, LocationId destination, TransportMode mode = TransportMode::Unknown, + inline void migrate(PersonId person, LocationId destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { - migrate(get_person(person), get_location(destination), mode, cells); - } - - void migrate(Person& person, Location& destination, TransportMode mode = TransportMode::Unknown, - const std::vector& cells = {0}) - { - // TODO: this could be static without caching - if (person.get_location() != destination.get_id()) { - if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(person.get_location()).erase(person.get_person_id()); - m_local_populations_cache.data.at(destination.get_id()).emplace(person.get_person_id()); - } - person.set_location(destination); - person.get_cells() = cells; - person.set_last_transport_mode(mode); + LocationId origin = get_location(person).get_id(); + const bool has_moved = mio::abm::migrate(get_person(person), get_location(destination), cells, mode); + if (has_moved && m_local_populations_cache.is_valid()) { + m_local_populations_cache.data.at(origin).erase(person); + m_local_populations_cache.data.at(destination).emplace(person); } } // let a person interact with its current location - inline void interact(Person& person, TimePoint t, TimeSpan dt) + inline void interact(PersonId person, TimePoint t, TimeSpan dt) { - auto personal_rng = Person::RandomNumberGenerator(m_rng, person); + auto personal_rng = Person::RandomNumberGenerator(m_rng, get_person(person)); interact(person, t, dt, personal_rng, parameters); } // let a person interact with its current location - inline void interact(Person& person, TimePoint t, TimeSpan dt, Person::RandomNumberGenerator& personal_rng, + inline void interact(PersonId person, TimePoint t, TimeSpan dt, Person::RandomNumberGenerator& personal_rng, const Parameters& global_parameters) { - interact(person, get_location(person), t, dt, personal_rng, global_parameters); - } - - // let a person interact with a location for and at some time - static void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, - Person::RandomNumberGenerator& personal_rng, const Parameters& global_parameters) - { - if (person.get_infection_state(t) == InfectionState::Susceptible) { - auto& local_parameters = location.get_infection_parameters(); - // TODO: we need to define what a cell is used for, as the loop may lead to incorrect results for multiple cells - auto age_receiver = person.get_age(); - ScalarType mask_protection = person.get_mask_protective_factor(global_parameters); - assert(person.get_cells().size() && - "Person is in multiple cells. Interact logic is incorrect at the moment."); - for (auto cell_index : - person.get_cells()) { // TODO: the logic here is incorrect in case a person is in multiple cells - std::pair local_indiv_trans_prob[static_cast(VirusVariant::Count)]; - for (uint32_t v = 0; v != static_cast(VirusVariant::Count); ++v) { - VirusVariant virus = static_cast(v); - ScalarType local_indiv_trans_prob_v = - (std::min(local_parameters.get(), - location.transmission_contacts_per_day(cell_index, virus, age_receiver, - global_parameters.get_num_groups())) + - location.transmission_air_per_day(cell_index, virus, global_parameters)) * - (1 - mask_protection) * dt.days() * - (1 - person.get_protection_factor(t, virus, global_parameters)); - - local_indiv_trans_prob[v] = std::make_pair(virus, local_indiv_trans_prob_v); - } - VirusVariant virus = - random_transition(personal_rng, VirusVariant::Count, dt, - local_indiv_trans_prob); // use VirusVariant::Count for no virus submission - if (virus != VirusVariant::Count) { - person.add_new_infection(Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2, - mio::abm::InfectionState::Exposed, - person.get_latest_protection(), - false)); // Starting time in first approximation - } - } - } - person.add_time_at_location(dt); + mio::abm::interact(get_person(person), get_location(person), t, dt, global_parameters, personal_rng); } // get location by id @@ -474,9 +427,9 @@ class World } // get current location of the Person - inline Location& get_location(const Person& p) + inline Location& get_location(PersonId id) { - return get_location(p.get_location()); + return get_location(get_person(id).get_location()); } private: @@ -538,11 +491,12 @@ class World } } - Cache>> m_local_populations_cache; + Cache>> + m_local_populations_cache; // TODO: change this to storing only (sub)population(s) in numbers, using atomic ints // Cache>> m_air_exposure_rates_cache; // Cache>> m_contact_exposure_rates_cache; - std::vector> m_persons; ///< Vector with pointers to every Person. + std::vector m_persons; ///< Vector with pointers to every Person. std::vector> m_locations; ///< Vector with pointers to every Location. std::bitset m_has_locations; ///< Flags for each LocationType, set if a Location of that type exists. diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index d56b9d6ec4..d92cb51fc9 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -364,12 +364,13 @@ void create_world_from_data(mio::abm::World& world, const std::string& filename, } auto first_location_id = it_first_location_id->second.first; auto first_location = locations.find(first_location_id)->second; - auto& person = world.add_person(first_location, determine_age_group(age)); - auto home = locations.find(home_id)->second; + auto& person = + world.get_person(world.add_person(first_location, determine_age_group(age))); // TODO: is this safe? + auto home = locations.find(home_id)->second; person.set_assigned_location(home); person.set_assigned_location(hospital); person.set_assigned_location(icu); - persons.insert({person_id, person}); + persons.insert({person_id, person}); // TODO: why? what? it_person = persons.find(person_id); } diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 5c10c4828b..e0ceb1142d 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -36,15 +36,8 @@ mio::abm::Person make_test_person(mio::abm::Location& location, mio::AgeGroup ag return p; } -mio::abm::Person& add_test_person(mio::abm::World& world, mio::abm::LocationId loc_id, mio::AgeGroup age, - mio::abm::InfectionState infection_state, mio::abm::TimePoint t) +mio::abm::PersonId add_test_person(mio::abm::World& world, mio::abm::LocationId loc_id, mio::AgeGroup age, + mio::abm::InfectionState infection_state, mio::abm::TimePoint t) { - assert(age.get() < world.parameters.get_num_groups()); - mio::abm::Person& p = world.add_person(loc_id, age); - if (infection_state != mio::abm::InfectionState::Susceptible) { - auto rng_p = mio::abm::Person::RandomNumberGenerator(world.get_rng(), p); - p.add_new_infection(mio::abm::Infection(rng_p, static_cast(0), age, world.parameters, t, - infection_state)); - } - return p; + return world.add_person(make_test_person(world.get_location(loc_id), age, infection_state, t, world.parameters)); } diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 787b4cadde..8a4b9c8177 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -105,9 +105,9 @@ mio::abm::Person make_test_person(mio::abm::Location& location, mio::AgeGroup ag /** * @brief Add a Person to the World. Intended for simple use in tests. */ -mio::abm::Person& add_test_person(mio::abm::World& world, mio::abm::LocationId loc_id, - 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::PersonId add_test_person(mio::abm::World& world, mio::abm::LocationId loc_id, + 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)); #endif //ABM_HELPERS_H diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 81d878c183..bfb9e5c696 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/functions.h" #include "abm/infection.h" #include "abm/movement_data.h" #include "abm/person.h" @@ -115,54 +116,47 @@ TEST(TestLocation, CacheExposureRate) auto dt = mio::abm::seconds(10000); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); - mio::abm::World world(params); // setup a location with some chance of exposure - auto home = world.add_location(mio::abm::LocationType::Home, 1); - auto location = world.add_location(mio::abm::LocationType::Home, 3); - auto infected1 = mio::abm::Person(rng, world.get_location(home), age); + mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups, 1); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, num_age_groups, 3); + auto infected1 = mio::abm::Person(rng, home, age); auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); infected1.add_new_infection( mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - world.migrate(infected1, world.get_location(location), mio::abm::TransportMode::Unknown, {0}); - auto infected2 = mio::abm::Person(rng, world.get_location(home), age); + mio::abm::migrate(infected1, location, {0}); + auto infected2 = mio::abm::Person(rng, home, age); auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); infected2.add_new_infection( mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - world.migrate(infected2, world.get_location(location), mio::abm::TransportMode::Unknown, {0, 1}); + mio::abm::migrate(infected2, location, {0, 1}); // TODO: cells - world.get_location(location).get_cells()[0].m_persons.emplace_back(&infected1); - world.get_location(location).get_cells()[0].m_persons.emplace_back(&infected2); - world.get_location(location).get_cells()[1].m_persons.emplace_back(&infected2); + location.get_cells()[0].m_persons.emplace_back(&infected1); + location.get_cells()[0].m_persons.emplace_back(&infected2); + location.get_cells()[1].m_persons.emplace_back(&infected2); //cache precomputed results - world.get_location(location).cache_exposure_rates(t, dt, num_age_groups); - - EXPECT_NEAR((world.get_location(location).get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), - 0.015015859523894731, 1e-14); - EXPECT_NEAR((world.get_location(location).get_cells()[0].m_cached_exposure_rate_air[{variant}]), - 0.015015859523894731, 1e-14); - EXPECT_NEAR((world.get_location(location).get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), - 0.0075079297619473654, 1e-14); - EXPECT_NEAR((world.get_location(location).get_cells()[1].m_cached_exposure_rate_air[{variant}]), - 0.0075079297619473654, 1e-14); - EXPECT_NEAR((world.get_location(location).get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, + location.cache_exposure_rates(t, dt, num_age_groups); + + EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, 1e-14); + EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.015015859523894731, 1e-14); + EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), 0.0075079297619473654, 1e-14); - EXPECT_NEAR((world.get_location(location).get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); + EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.0075079297619473654, 1e-14); + EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); + EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); // should also work with capacities - world.get_location(location).set_capacity_adapted_transmission_risk_flag(true); - world.get_location(location).set_capacity(2, 22, 0); // Capacity for Cell 1 - world.get_location(location).set_capacity(2, 22, 1); // Capacity for Cell 2 - world.get_location(location).set_capacity(2, 22, 2); // Capacity for Cell 3 - world.get_location(location).cache_exposure_rates(t, dt, num_age_groups); - - EXPECT_NEAR((world.get_location(location).get_cells()[0].m_cached_exposure_rate_air[{variant}]), - 0.045047578571684191, 1e-14); - EXPECT_NEAR((world.get_location(location).get_cells()[1].m_cached_exposure_rate_air[{variant}]), - 0.022523789285842095, 1e-14); - EXPECT_NEAR((world.get_location(location).get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); + location.set_capacity_adapted_transmission_risk_flag(true); + location.set_capacity(2, 22, 0); // Capacity for Cell 1 + location.set_capacity(2, 22, 1); // Capacity for Cell 2 + location.set_capacity(2, 22, 2); // Capacity for Cell 3 + location.cache_exposure_rates(t, dt, num_age_groups); + + EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); + EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); + EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); } TEST(TestLocation, reachCapacity) @@ -197,18 +191,15 @@ TEST(TestLocation, reachCapacity) .WillOnce(testing::Return(0.8)) // draw random school hour .WillRepeatedly(testing::Return(1.0)); - auto& p1 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::InfectedNoSymptoms); - auto& p2 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible); - - auto& home = world.get_individualized_location(home_id); - auto& school = world.get_individualized_location(school_id); + auto p1 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::InfectedNoSymptoms); + auto p2 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible); - p1.set_assigned_location(school_id); - p2.set_assigned_location(school_id); - p1.set_assigned_location(home_id); - p2.set_assigned_location(home_id); + world.get_person(p1).set_assigned_location(school_id); + world.get_person(p2).set_assigned_location(school_id); + world.get_person(p1).set_assigned_location(home_id); + world.get_person(p2).set_assigned_location(home_id); - school.set_capacity(1, 66); + world.get_location(school_id).set_capacity(1, 66); ScopedMockDistribution>>> mock_exponential_dist; @@ -216,10 +207,10 @@ TEST(TestLocation, reachCapacity) world.evolve(t, dt); - ASSERT_EQ(p1.get_location(), school.get_id()); - ASSERT_EQ(p2.get_location(), home.get_id()); // p2 should not be able to enter the school - ASSERT_EQ(world.get_number_persons(school.get_id()), 1); - ASSERT_EQ(world.get_number_persons(home.get_id()), 1); + ASSERT_EQ(world.get_person(p1).get_location(), school_id); + ASSERT_EQ(world.get_person(p2).get_location(), home_id); // p2 should not be able to enter the school + ASSERT_EQ(world.get_number_persons(school_id), 1); + ASSERT_EQ(world.get_number_persons(home_id), 1); } TEST(TestLocation, computeSpacePerPersonRelative) @@ -274,7 +265,7 @@ TEST(TestLocation, interact) // location.add_person(infected1, {0}); // location.add_person(infected2, {0}); // location.add_person(infected3, {0}); - // TODO: no, not yet + // TODO: no, not yet : cells location.get_cells()[0].m_persons = {&infected1, &infected2, &infected3}; //cache precomputed results @@ -287,12 +278,12 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); auto person_rng = mio::abm::Person::RandomNumberGenerator(rng, susceptible); - mio::abm::World::interact(susceptible, location, t, dt, person_rng, params); + mio::abm::interact(susceptible, location, t, dt, params, person_rng); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - mio::abm::World::interact(susceptible, location, t, dt, person_rng, params); + mio::abm::interact(susceptible, location, t, dt, params, person_rng); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); } diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index 0c09b81a63..c2f9de7ffd 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -87,10 +87,10 @@ TEST(TestMasks, maskProtection) mock_exponential_dist; auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person1); - mio::abm::World::interact(susc_person1, infection_location, t, dt, p1_rng, params); + mio::abm::interact(susc_person1, infection_location, t, dt, params, p1_rng); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person2); - mio::abm::World::interact(susc_person2, infection_location, t, dt, p2_rng, params); + mio::abm::interact(susc_person2, infection_location, t, dt, params, p2_rng); // The person susc_person1 should have full protection against an infection, susc_person2 not ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 80a3f51dc8..7f8acbf2eb 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/functions.h" #include "abm/person.h" #include "abm/world.h" #include "abm_helpers.h" @@ -329,10 +330,10 @@ TEST(TestMigrationRules, work_return) TEST(TestMigrationRules, quarantine) { - auto rng = mio::RandomNumberGenerator(); - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); - auto test_params = mio::abm::TestParameters{1.0,1.0}; + auto rng = mio::RandomNumberGenerator(); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); + auto test_params = mio::abm::TestParameters{1.0, 1.0}; mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); @@ -410,19 +411,16 @@ TEST(TestMigrationRules, shop_return) auto params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(9); auto dt = mio::abm::hours(1); - mio::abm::World world(params); - auto home = world.add_location(mio::abm::LocationType::Home); - auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - auto p = - make_test_person(world.get_location(home), age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); + mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0, num_age_groups); + auto p = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); - auto person = world.add_person(p).get_person_id(); - world.migrate(person, shop); - world.interact(world.get_person(person), t, dt, rng_p, params); + mio::abm::migrate(p, shop); + mio::abm::interact(p, shop, t, dt, params, rng_p); //person only returns home after some time passed - ASSERT_EQ(mio::abm::go_to_shop(rng_p, world.get_person(person), t, dt, mio::abm::Parameters(num_age_groups)), + ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } @@ -463,20 +461,16 @@ TEST(TestMigrationRules, event_return) auto params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(21); auto dt = mio::abm::hours(3); - mio::abm::World world(params); - auto home = world.add_location(mio::abm::LocationType::Home); - auto social_event = world.add_location(mio::abm::LocationType::SocialEvent); - auto p = - mio::abm::Person(rng, world.get_location(home), age_group_15_to_34); // TODO: change Person ctor to LocationID + mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); + mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 0, num_age_groups); + auto p = mio::abm::Person(rng, home, age_group_15_to_34); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); - // TODO: do we need a different add_person? e.g. something that behaves like emplace? - auto person = world.add_person(p).get_person_id(); - world.migrate(person, social_event); - world.interact(world.get_person(person), t, dt, rng_p, params); + mio::abm::migrate(p, social_event); + mio::abm::interact(p, social_event, t, dt, params, rng_p); - ASSERT_EQ(mio::abm::go_to_event(rng_p, world.get_person(person), t, dt, mio::abm::Parameters(num_age_groups)), + ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 98232a7557..50863fdc33 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -17,10 +17,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/functions.h" #include "abm/location_type.h" -#include "abm/movement_data.h" #include "abm/person.h" -#include "abm/world.h" + #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" #include @@ -29,13 +29,13 @@ TEST(TestPerson, init) { auto rng = mio::RandomNumberGenerator(); - mio::abm::Location location(mio::abm::LocationType::Work, 0, num_age_groups); + mio::abm::Location location(mio::abm::LocationType::Work, 7, num_age_groups); auto t = mio::abm::TimePoint(0); auto person = mio::abm::Person(rng, location, age_group_60_to_79); - ASSERT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); - ASSERT_EQ(person.get_location(), location.get_id()); - ASSERT_EQ(person.get_person_id(), mio::abm::INVALID_PERSON_ID); + EXPECT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); + EXPECT_EQ(person.get_location(), location.get_id()); + EXPECT_EQ(person.get_person_id(), mio::abm::INVALID_PERSON_ID); } TEST(TestPerson, copyPerson) @@ -47,54 +47,37 @@ TEST(TestPerson, copyPerson) auto copied_location = location.copy_location_without_persons(num_age_groups); auto copied_person = person.copy_person(copied_location); - ASSERT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); - ASSERT_EQ(copied_person.get_location(), copied_location.get_id()); - ASSERT_EQ(copied_person.get_person_id(), mio::abm::INVALID_PERSON_ID); + EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); + EXPECT_EQ(copied_person.get_location(), copied_location.get_id()); + EXPECT_EQ(copied_person.get_person_id(), mio::abm::INVALID_PERSON_ID); } -// TODO: move migration testing to world TEST(TestPerson, migrate) { - mio::abm::World world(num_age_groups); auto rng = mio::RandomNumberGenerator(); - auto t = mio::abm::TimePoint(0); - - auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home)); - auto& loc1 = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 1)); - auto& loc2 = world.get_location(world.add_location(mio::abm::LocationType::School)); - auto& loc3 = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 2)); + mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); + mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 0, 6, 1); + mio::abm::Location loc2(mio::abm::LocationType::School, 0, num_age_groups); + mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 0, 6, 2); + auto person = make_test_person(home, age_group_0_to_4, mio::abm::InfectionState::Recovered); - auto& person = world.add_person(make_test_person(home, age_group_0_to_4, mio::abm::InfectionState::Recovered)); - world.migrate(person, loc1); - // TODO: cells - loc1.get_cells()[0].m_persons.emplace_back(&person); + mio::abm::migrate(person, loc1, {0}); EXPECT_EQ(person.get_location(), loc1.get_id()); - EXPECT_EQ(world.get_subpopulation(loc1, t, mio::abm::InfectionState::Recovered), 1); - EXPECT_EQ(world.get_subpopulation(home, t, mio::abm::InfectionState::Recovered), 0); - EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); + // EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); // TODO: cells EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); - world.migrate(person, loc2, mio::abm::TransportMode::Walking); - // TODO: cells - loc1.get_cells()[0].m_persons.clear(); - loc2.get_cells()[0].m_persons.emplace_back(&person); + mio::abm::migrate(person, loc2, {0}, mio::abm::TransportMode::Walking); EXPECT_EQ(person.get_location(), loc2.get_id()); - EXPECT_EQ(world.get_subpopulation(loc2, t, mio::abm::InfectionState::Recovered), 1); - EXPECT_EQ(world.get_subpopulation(loc1, t, mio::abm::InfectionState::Recovered), 0); - EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); + // EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); - world.migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1}); - // TODO: cells - loc3.get_cells()[0].m_persons.clear(); - loc3.get_cells()[0].m_persons.emplace_back(&person); - loc3.get_cells()[1].m_persons.emplace_back(&person); + mio::abm::migrate(person, loc3, {0, 1}, mio::abm::TransportMode::Bike); - EXPECT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); - EXPECT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); + // EXPECT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); + // EXPECT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); EXPECT_EQ(person.get_cells().size(), 2); EXPECT_EQ(person.get_cells()[0], 0u); EXPECT_EQ(person.get_cells()[1], 1u); @@ -107,17 +90,17 @@ TEST(TestPerson, setGetAssignedLocation) mio::abm::Location location(mio::abm::LocationType::Work, 2, num_age_groups); auto person = mio::abm::Person(rng, location, age_group_35_to_59); person.set_assigned_location(location); - ASSERT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 2); + EXPECT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 2); person.set_assigned_location({4, mio::abm::LocationType::Work}); - ASSERT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 4); + EXPECT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 4); } TEST(TestPerson, quarantine) { using testing::Return; - auto rng = mio::RandomNumberGenerator(); - auto test_params = mio::abm::TestParameters{1.01,1.01}; //100% safe test + auto rng = mio::RandomNumberGenerator(); + auto test_params = mio::abm::TestParameters{1.01, 1.01}; //100% safe test auto infection_parameters = mio::abm::Parameters(num_age_groups); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); @@ -142,15 +125,15 @@ TEST(TestPerson, quarantine) auto person = make_test_person(home, age_group_35_to_59, mio::abm::InfectionState::InfectedSymptoms, t_morning, infection_parameters); auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); - + person.get_tested(rng_person, t_morning, test_params); - ASSERT_EQ(person.get_infection_state(t_morning), mio::abm::InfectionState::InfectedSymptoms); - ASSERT_EQ(mio::abm::go_to_work(rng_person, person, t_morning, dt, mio::abm::Parameters(num_age_groups)), + 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, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); - ASSERT_EQ(person.get_infection_state(t_morning + dt), mio::abm::InfectionState::Recovered); + EXPECT_EQ(person.get_infection_state(t_morning + dt), mio::abm::InfectionState::Recovered); person.remove_quarantine(); - ASSERT_EQ(mio::abm::go_to_work(rng_person, person, t_morning, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_work(rng_person, person, t_morning, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); } @@ -179,16 +162,16 @@ TEST(TestPerson, get_tested) .WillOnce(Return(0.95)) .WillOnce(Return(0.6)) .WillOnce(Return(0.999)); - ASSERT_EQ(infected.get_tested(rng_infected, t, pcr_test.get_default()), true); - ASSERT_EQ(infected.is_in_quarantine(t, params), true); + EXPECT_EQ(infected.get_tested(rng_infected, t, pcr_test.get_default()), true); + EXPECT_EQ(infected.is_in_quarantine(t, params), true); infected.remove_quarantine(); - ASSERT_EQ(infected.get_tested(rng_infected, t, pcr_test.get_default()), false); - ASSERT_EQ(infected.is_in_quarantine(t, params), false); - ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, pcr_test.get_default()), false); - ASSERT_EQ(susceptible.is_in_quarantine(t, params), false); - ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, pcr_test.get_default()), true); - ASSERT_EQ(susceptible.is_in_quarantine(t, params), true); - ASSERT_EQ(susceptible.get_time_of_last_test(), mio::abm::TimePoint(0)); + EXPECT_EQ(infected.get_tested(rng_infected, t, pcr_test.get_default()), false); + EXPECT_EQ(infected.is_in_quarantine(t, params), false); + EXPECT_EQ(susceptible.get_tested(rng_suscetible, t, pcr_test.get_default()), false); + EXPECT_EQ(susceptible.is_in_quarantine(t, params), false); + EXPECT_EQ(susceptible.get_tested(rng_suscetible, t, pcr_test.get_default()), true); + EXPECT_EQ(susceptible.is_in_quarantine(t, params), true); + EXPECT_EQ(susceptible.get_time_of_last_test(), mio::abm::TimePoint(0)); // Test antigen test ScopedMockDistribution>>> @@ -199,25 +182,22 @@ TEST(TestPerson, get_tested) .WillOnce(Return(0.95)) .WillOnce(Return(0.6)) .WillOnce(Return(0.999)); - ASSERT_EQ(infected.get_tested(rng_infected, t, antigen_test.get_default()), true); - ASSERT_EQ(infected.get_tested(rng_infected, t, antigen_test.get_default()), false); - ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, antigen_test.get_default()), false); - ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, antigen_test.get_default()), true); - ASSERT_EQ(susceptible.get_time_of_last_test(), mio::abm::TimePoint(0)); + EXPECT_EQ(infected.get_tested(rng_infected, t, antigen_test.get_default()), true); + EXPECT_EQ(infected.get_tested(rng_infected, t, antigen_test.get_default()), false); + EXPECT_EQ(susceptible.get_tested(rng_suscetible, t, antigen_test.get_default()), false); + EXPECT_EQ(susceptible.get_tested(rng_suscetible, t, antigen_test.get_default()), true); + EXPECT_EQ(susceptible.get_time_of_last_test(), mio::abm::TimePoint(0)); } TEST(TestPerson, getCells) { - mio::abm::World world(num_age_groups); - - auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); - auto& location = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 2)); - + mio::abm::Location home(mio::abm::LocationType::Home, 0, 6, 1); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 2); auto person = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - world.migrate(person, location, mio::abm::TransportMode::Unknown, {0, 1}); + mio::abm::migrate(person, location, {0, 1}); - ASSERT_EQ(person.get_cells().size(), 2); + EXPECT_EQ(person.get_cells().size(), 2); // TODO: is this a meaningfull test? } TEST(TestPerson, interact) @@ -231,7 +211,7 @@ TEST(TestPerson, interact) auto person = mio::abm::Person(rng, loc, age_group_15_to_34); auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days - mio::abm::World::interact(person, loc, t, dt, rng_person, infection_parameters); + mio::abm::interact(person, loc, t, dt, infection_parameters, rng_person); EXPECT_EQ(person.get_time_at_location(), dt); } @@ -345,4 +325,4 @@ TEST(Person, rng) p_rng(); ASSERT_EQ(p.get_rng_counter(), mio::Counter(1)); ASSERT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{1})); -} +} \ No newline at end of file diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index fff7311d5d..5cd6370c59 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -26,10 +26,16 @@ TEST(TestSimulation, advance_random) auto world = mio::abm::World(num_age_groups); auto location1 = world.add_location(mio::abm::LocationType::School); auto location2 = world.add_location(mio::abm::LocationType::School); - auto& p1 = world.add_person(location1, age_group_5_to_14); - auto& p2 = world.add_person(location1, age_group_5_to_14); - auto& p3 = world.add_person(location2, age_group_5_to_14); - auto& p4 = world.add_person(location2, age_group_5_to_14); + auto pid1 = world.add_person(location1, age_group_5_to_14); + auto pid2 = world.add_person(location1, age_group_5_to_14); + auto pid3 = world.add_person(location2, age_group_5_to_14); + auto pid4 = world.add_person(location2, age_group_5_to_14); + + auto& p1 = world.get_person(pid1); + auto& p2 = world.get_person(pid2); + auto& p3 = world.get_person(pid3); + auto& p4 = world.get_person(pid4); + p1.set_assigned_location(location1); p2.set_assigned_location(location1); p3.set_assigned_location(location2); @@ -75,9 +81,14 @@ TEST(TestSimulation, advanceWithCommonHistory) auto basics_id = world.add_location(mio::abm::LocationType::BasicsShop); auto public_id = world.add_location(mio::abm::LocationType::PublicTransport); - auto& person1 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Exposed); - auto& person2 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::Exposed); - auto& person3 = add_test_person(world, home_id, age_group_35_to_59, mio::abm::InfectionState::Dead); + auto pid1 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Exposed); + auto pid2 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::Exposed); + auto pid3 = add_test_person(world, home_id, age_group_35_to_59, mio::abm::InfectionState::Dead); + + auto& person1 = world.get_person(pid1); + auto& person2 = world.get_person(pid2); + auto& person3 = world.get_person(pid3); + person1.set_assigned_location(home_id); person2.set_assigned_location(home_id); person3.set_assigned_location(home_id); diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 38cfffec96..0ff7d68247 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -66,12 +66,12 @@ TEST(TestWorld, addPerson) auto world = mio::abm::World(num_age_groups); auto location = world.add_location(mio::abm::LocationType::School); - auto& p1 = world.add_person(location, age_group_15_to_34); - auto& p2 = world.add_person(location, age_group_35_to_59); + world.add_person(location, age_group_15_to_34); + world.add_person(location, age_group_35_to_59); ASSERT_EQ(world.get_persons().size(), 2); - ASSERT_EQ(&world.get_persons()[0], &p1); - ASSERT_EQ(&world.get_persons()[1], &p2); + // ASSERT_EQ(&world.get_persons()[0], &p1); // TODO: rethink these, add new add_person fcts + // ASSERT_EQ(&world.get_persons()[1], &p2); } TEST(TestWorld, getSubpopulationCombined) @@ -104,22 +104,20 @@ TEST(TestWorld, findLocation) auto home_id = world.add_location(mio::abm::LocationType::Home); auto school_id = world.add_location(mio::abm::LocationType::School); auto work_id = world.add_location(mio::abm::LocationType::Work); - auto& home = world.get_individualized_location(home_id); - auto& school = world.get_individualized_location(school_id); - auto& work = world.get_individualized_location(work_id); - auto person = make_test_person(home); - person.set_assigned_location(home); - person.set_assigned_location(work); - person.set_assigned_location(school); + auto person = world.get_person(add_test_person(world, home_id)); + + person.set_assigned_location(home_id); + person.set_assigned_location(work_id); + person.set_assigned_location(school_id); - ASSERT_EQ(world.find_location(mio::abm::LocationType::Work, person), work); - ASSERT_EQ(world.find_location(mio::abm::LocationType::School, person), school); - ASSERT_EQ(world.find_location(mio::abm::LocationType::Home, person), home); + ASSERT_EQ(world.find_location(mio::abm::LocationType::Work, person), work_id); + ASSERT_EQ(world.find_location(mio::abm::LocationType::School, person), school_id); + ASSERT_EQ(world.find_location(mio::abm::LocationType::Home, person), home_id); auto&& world_test = std::as_const(world); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Work, person), work); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::School, person), school); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Home, person), home); + ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Work, person), work_id); + ASSERT_EQ(world_test.find_location(mio::abm::LocationType::School, person), school_id); + ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Home, person), home_id); } TEST(TestWorld, evolveStateTransition) @@ -146,14 +144,23 @@ TEST(TestWorld, evolveStateTransition) 2 * dt.days(); auto location1 = world.add_location(mio::abm::LocationType::School); - auto& p1 = add_test_person(world, location1, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - auto& p2 = add_test_person(world, location1, age_group_15_to_34, mio::abm::InfectionState::Susceptible); auto location2 = world.add_location(mio::abm::LocationType::Work); - auto& p3 = add_test_person(world, location2, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); + + add_test_person(world, location1, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); + add_test_person(world, location1, age_group_15_to_34, mio::abm::InfectionState::Susceptible); + add_test_person(world, location2, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); + + auto& p1 = world.get_persons()[0]; + auto& p2 = world.get_persons()[1]; + auto& p3 = world.get_persons()[2]; + p1.set_assigned_location(location1); p2.set_assigned_location(location1); p3.set_assigned_location(location2); + // TODO: CELLS! this includes reworking Location::transmission_X_per_day (especially for mio::abm::interact) + world.get_location(location1).get_cells()[0].m_persons.push_back(&p1); + //setup mock so p2 becomes infected ScopedMockDistribution>>> mock_exponential_dist; @@ -166,7 +173,7 @@ TEST(TestWorld, evolveStateTransition) EXPECT_EQ(p3.get_infection_state(t + dt), mio::abm::InfectionState::InfectedSymptoms); } -TEST(TestWorld, evolveMigration) +TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a unit of a test! { using testing::Return; @@ -200,8 +207,12 @@ TEST(TestWorld, evolveMigration) .WillOnce(testing::Return(0.8)) // draw random school hour .WillRepeatedly(testing::Return(1.0)); - auto& p2 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t); - auto& p1 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + auto pid2 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t); + auto pid1 = + add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + + auto& p1 = world.get_person(pid1); + auto& p2 = world.get_person(pid2); p1.set_assigned_location(school_id); p2.set_assigned_location(school_id); @@ -210,19 +221,16 @@ TEST(TestWorld, evolveMigration) p1.set_assigned_location(home_id); p2.set_assigned_location(home_id); - auto& school = world.get_individualized_location(school_id); - auto& work = world.get_individualized_location(work_id); - ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillRepeatedly(Return(1.)); //no state transitions world.evolve(t, dt); - EXPECT_EQ(p1.get_location(), work.get_id()); - EXPECT_EQ(p2.get_location(), school.get_id()); - EXPECT_EQ(world.get_number_persons(school), 1); - EXPECT_EQ(world.get_number_persons(work), 1); + EXPECT_EQ(p1.get_location(), work_id); + EXPECT_EQ(p2.get_location(), school_id); + EXPECT_EQ(world.get_number_persons(school_id), 1); + EXPECT_EQ(world.get_number_persons(work_id), 1); } { @@ -260,11 +268,19 @@ TEST(TestWorld, evolveMigration) .WillOnce(testing::Return(0.8)) // draw random school hour .WillRepeatedly(testing::Return(1.0)); // this forces p1 and p3 to recover - auto& p1 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); - auto& p2 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t); - auto& p3 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::InfectedSevere, t); - auto& p4 = add_test_person(world, hospital_id, age_group_5_to_14, mio::abm::InfectionState::Recovered, t); - auto& p5 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t); + auto pid1 = + add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + auto pid2 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t); + auto pid3 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::InfectedSevere, t); + auto pid4 = add_test_person(world, hospital_id, age_group_5_to_14, mio::abm::InfectionState::Recovered, t); + auto pid5 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t); + + auto& p1 = world.get_person(pid1); + auto& p2 = world.get_person(pid2); + auto& p3 = world.get_person(pid3); + auto& p4 = world.get_person(pid4); + auto& p5 = world.get_person(pid5); + p1.set_assigned_location(event_id); p2.set_assigned_location(event_id); p1.set_assigned_location(work_id); @@ -310,9 +326,9 @@ TEST(TestWorld, evolveMigration) EXPECT_EQ(world.get_number_persons(home), 1); EXPECT_EQ(world.get_number_persons(hospital), 1); - world.migrate(p1, home); - world.migrate(p2, home); - world.migrate(p5, home); + world.migrate(p1.get_person_id(), home.get_id()); + world.migrate(p2.get_person_id(), home.get_id()); + world.migrate(p5.get_person_id(), home.get_id()); t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); world.get_trip_list().reset_index(); @@ -370,10 +386,13 @@ TEST(TestWorld, evolveMigration) auto icu_id = world.add_location(mio::abm::LocationType::ICU); auto hospital_id = world.add_location(mio::abm::LocationType::Hospital); // Create a person that is dead at time t - auto& p_dead = add_test_person(world, icu_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t); + add_test_person(world, icu_id, 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 - auto& p_severe = - add_test_person(world, hospital_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); + add_test_person(world, hospital_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); + + auto& p_dead = world.get_persons()[0]; + auto& p_severe = world.get_persons()[1]; + p_dead.set_assigned_location(icu_id); p_dead.set_assigned_location(work_id); p_dead.set_assigned_location(home_id); @@ -392,15 +411,15 @@ TEST(TestWorld, evolveMigration) // Check the dead person got burried and the severely infected person starts in Hospital world.evolve(t, dt); - EXPECT_EQ(world.get_location(p_dead).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_dead.get_person_id()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(world.get_location(p_severe).get_type(), mio::abm::LocationType::Hospital); + EXPECT_EQ(world.get_location(p_severe.get_person_id()).get_type(), mio::abm::LocationType::Hospital); // Check the dead person is still in Cemetery and the severely infected person dies and got burried world.evolve(t + dt, dt); - EXPECT_EQ(world.get_location(p_dead).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_dead.get_person_id()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t + dt), mio::abm::InfectionState::Dead); - EXPECT_EQ(world.get_location(p_severe).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_severe.get_person_id()).get_type(), mio::abm::LocationType::Cemetery); } } @@ -419,8 +438,9 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) auto& home = world.get_individualized_location(home_id); auto& work = world.get_individualized_location(work_id); auto current_time = mio::abm::TimePoint(0); - auto person = + auto pid = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time); + auto& person = world.get_person(pid); auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); person.set_assigned_location(home); person.set_assigned_location(work); @@ -545,7 +565,7 @@ TEST(TestWorld, checkParameterConstraints) ASSERT_EQ(params.check_constraints(), true); } -TEST(TestWorld, copyWorld) +TEST(TestWorld, copyWorld) // TODO: this needs either a rewrite or to be removed { auto world = mio::abm::World(num_age_groups); auto rng = mio::RandomNumberGenerator(); @@ -566,11 +586,15 @@ TEST(TestWorld, copyWorld) auto& work = world.get_individualized_location(work_id); auto& home = world.get_individualized_location(home_id); - auto& p1 = world.add_person(school_id1, age_group_0_to_4); + auto pid1 = world.add_person(school_id1, age_group_0_to_4); + auto pid2 = world.add_person(school_id2, age_group_15_to_34); + + auto& p1 = world.get_person(pid1); + auto& p2 = world.get_person(pid2); + auto rng_p1 = mio::abm::Person::RandomNumberGenerator(rng, p1); p1.add_new_infection(mio::abm::Infection(rng_p1, mio::abm::VirusVariant::Wildtype, p1.get_age(), world.parameters, mio::abm::TimePoint(0))); - auto& p2 = world.add_person(school_id2, age_group_15_to_34); p2.set_mask_preferences(std::vector(15, 0.2)); mio::abm::TripList& trip_data = world.get_trip_list(); @@ -667,14 +691,14 @@ TEST(TestWorld, copyWorld) // world.get_locations()[2].get_cells()[0].m_persons[0]); ASSERT_EQ(copied_world.get_persons().size(), world.get_persons().size()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[0]).get_index(), - world.get_location(world.get_persons()[0]).get_index()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[1]).get_index(), - world.get_location(world.get_persons()[1]).get_index()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[0]).get_type(), - world.get_location(world.get_persons()[0]).get_type()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[1]).get_type(), - world.get_location(world.get_persons()[1]).get_type()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[0].get_person_id()).get_index(), + world.get_location(world.get_persons()[0].get_person_id()).get_index()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[1].get_person_id()).get_index(), + world.get_location(world.get_persons()[1].get_person_id()).get_index()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[0].get_person_id()).get_type(), + world.get_location(world.get_persons()[0].get_person_id()).get_type()); + ASSERT_EQ(copied_world.get_location(world.get_persons()[1].get_person_id()).get_type(), + world.get_location(world.get_persons()[1].get_person_id()).get_type()); ASSERT_EQ(copied_world.get_persons()[0].get_infection().get_infection_state(mio::abm::TimePoint(0)), world.get_persons()[0].get_infection().get_infection_state(mio::abm::TimePoint(0))); ASSERT_EQ(copied_world.get_persons()[0].get_mask_compliance(mio::abm::LocationType::Home), @@ -707,8 +731,10 @@ TEST(TestWorld, copyWorld) ASSERT_NE(&copied_world.get_persons()[0], &world.get_persons()[0]); ASSERT_NE(&copied_world.get_persons()[1], &world.get_persons()[1]); - ASSERT_NE(&copied_world.get_location((copied_world.get_persons()[0])), &world.get_location(world.get_persons()[0])); - ASSERT_NE(&copied_world.get_location((copied_world.get_persons()[1])), &world.get_location(world.get_persons()[1])); + ASSERT_NE(&copied_world.get_location(copied_world.get_persons()[0].get_person_id()), + &world.get_location(world.get_persons()[0].get_person_id())); + ASSERT_NE(&copied_world.get_location(copied_world.get_persons()[1].get_person_id()), + &world.get_location(world.get_persons()[1].get_person_id())); ASSERT_NE(&(copied_world.get_locations()[1]), &(world.get_locations()[1])); ASSERT_NE(&(copied_world.get_locations()[2]), &(world.get_locations()[2])); ASSERT_NE(&(copied_world.get_persons()[0].get_assigned_locations()), @@ -722,10 +748,12 @@ TEST(TestWorld, copyWorld) ASSERT_NE(&(copied_world.get_persons()[1].get_cells()), &world.get_persons()[1].get_cells()); // Evolve the world and check that the copied world has not evolved - copied_world.migrate(copied_world.get_persons()[0], work, mio::abm::TransportMode::Unknown, {0}); - copied_world.migrate(copied_world.get_persons()[1], home, mio::abm::TransportMode::Unknown, {0}); - ASSERT_NE(copied_world.get_location(copied_world.get_persons()[0]).get_type(), - world.get_location(world.get_persons()[0]).get_type()); - ASSERT_NE(copied_world.get_location(copied_world.get_persons()[1]).get_type(), - world.get_location(world.get_persons()[1]).get_type()); + copied_world.migrate(copied_world.get_persons()[0].get_person_id(), work.get_id(), mio::abm::TransportMode::Unknown, + {0}); + copied_world.migrate(copied_world.get_persons()[1].get_person_id(), home.get_id(), mio::abm::TransportMode::Unknown, + {0}); + ASSERT_NE(copied_world.get_location(copied_world.get_persons()[0].get_person_id()).get_type(), + world.get_location(world.get_persons()[0].get_person_id()).get_type()); + ASSERT_NE(copied_world.get_location(copied_world.get_persons()[1].get_person_id()).get_type(), + world.get_location(world.get_persons()[1].get_person_id()).get_type()); } diff --git a/cpp/tests/test_json_serializer.cpp b/cpp/tests/test_json_serializer.cpp index b9a1f1b4de..e81242b22f 100644 --- a/cpp/tests/test_json_serializer.cpp +++ b/cpp/tests/test_json_serializer.cpp @@ -533,20 +533,20 @@ TEST(TestJsonSerializer, abmWorld) auto home_id = world.add_location(mio::abm::LocationType::Home); auto work_id = world.add_location(mio::abm::LocationType::Work); auto person = world.add_person(home_id, age_group_15_to_34); - mio::abm::Trip trip1(person.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), work_id, home_id); - mio::abm::Trip trip2(person.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(11), work_id, home_id); + mio::abm::Trip trip1(person, mio::abm::TimePoint(0) + mio::abm::hours(8), work_id, home_id); + mio::abm::Trip trip2(person, mio::abm::TimePoint(0) + mio::abm::hours(11), work_id, home_id); world.get_trip_list().add_trip(trip1, false); world.get_trip_list().add_trip(trip2, true); auto js = mio::serialize_json(world); Json::Value expected_json; expected_json["num_agegroups"] = Json::UInt(num_age_groups); - expected_json["trips"][0]["person_id"] = Json::UInt(person.get_person_id()); + expected_json["trips"][0]["person_id"] = Json::UInt(person); expected_json["trips"][0]["time"] = Json::Int(mio::abm::hours(8).seconds()); expected_json["trips"][0]["destination_index"] = Json::UInt(work_id.index); expected_json["trips"][0]["destination_type"] = Json::UInt(work_id.type); expected_json["trips"][0]["origin_index"] = Json::UInt(home_id.index); expected_json["trips"][0]["origin_type"] = Json::UInt(home_id.type); - expected_json["trips"][1]["person_id"] = Json::UInt(person.get_person_id()); + expected_json["trips"][1]["person_id"] = Json::UInt(person); expected_json["trips"][1]["time"] = Json::Int(mio::abm::hours(11).seconds()); expected_json["trips"][1]["destination_index"] = Json::UInt(work_id.index); expected_json["trips"][1]["destination_type"] = Json::UInt(work_id.type); @@ -561,7 +561,7 @@ TEST(TestJsonSerializer, abmWorld) expected_json["persons"][0]["Location"]["index"] = Json::UInt(1); expected_json["persons"][0]["Location"]["type"] = Json::UInt(mio::abm::LocationType::Home); expected_json["persons"][0]["age"] = Json::UInt(2); - expected_json["persons"][0]["id"] = Json::UInt(person.get_person_id()); + expected_json["persons"][0]["id"] = Json::UInt(person); expected_json["use_migration_rules"] = Json::Value(true); ASSERT_EQ(js.value(), expected_json); From a1bc2d4adc8e6c5d1ccf718c669feb427cf1002b Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:31:03 +0100 Subject: [PATCH 08/54] remove some unused headers (according to clangd) --- cpp/models/abm/location.cpp | 6 +----- cpp/models/abm/location.h | 8 +------- cpp/tests/abm_helpers.h | 5 ----- cpp/tests/test_abm_location.cpp | 1 - cpp/tests/test_abm_simulation.cpp | 1 + 5 files changed, 3 insertions(+), 18 deletions(-) diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index f498aec5f9..a943034de3 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -18,13 +18,9 @@ * limitations under the License. */ #include "abm/mask_type.h" -#include "abm/mask.h" #include "abm/location.h" #include "abm/random_events.h" #include "abm/infection.h" -#include "memilio/utils/random_number_generator.h" -#include -#include namespace mio { @@ -42,7 +38,7 @@ Location::Location(LocationId loc_id, size_t num_agegroups, uint32_t num_cells) assert(num_cells > 0 && "Number of cells has to be larger than 0."); } -Location Location::copy_location_without_persons(size_t num_agegroups) +Location Location::copy_location_without_persons(size_t num_agegroups) const { Location copy_loc = Location(*this); copy_loc.m_cells = std::vector{num_agegroups}; diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 616217ad7d..8b8a6e505a 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -25,15 +25,9 @@ #include "abm/parameters.h" #include "abm/location_type.h" #include "abm/infection_state.h" -#include "abm/vaccine.h" #include "memilio/epidemiology/age_group.h" -#include "memilio/math/eigen.h" #include "memilio/utils/custom_index_array.h" -#include "memilio/utils/time_series.h" #include "memilio/utils/memory.h" -#include -#include -#include namespace mio { @@ -131,7 +125,7 @@ class Location * @brief Return a copy of this #Location object with an empty m_persons. * @param[in] num_agegroups The number of age groups in the model. */ - Location copy_location_without_persons(size_t num_agegroups); + Location copy_location_without_persons(size_t num_agegroups) const; /** * @brief Compare two Location%s. diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 8a4b9c8177..4386b53fe6 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -21,11 +21,6 @@ #define ABM_HELPERS_H #include "abm/abm.h" -#include "abm/virus_variant.h" -#include "memilio/math/eigen_util.h" -#include "memilio/epidemiology/age_group.h" -#include "matchers.h" -#include "gtest/gtest.h" #include "gmock/gmock.h" #include diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index bfb9e5c696..fa2ab70562 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -20,7 +20,6 @@ #include "abm/functions.h" #include "abm/infection.h" -#include "abm/movement_data.h" #include "abm/person.h" #include "abm/world.h" #include "abm_helpers.h" diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index 5cd6370c59..e3334907a6 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -19,6 +19,7 @@ */ #include "abm_helpers.h" #include "abm/common_abm_loggers.h" +#include "matchers.h" #include "memilio/io/history.h" TEST(TestSimulation, advance_random) From aeb171d778dba34ef8118d1544383b7490351a66 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:31:37 +0100 Subject: [PATCH 09/54] World::m_locations no longer uses pointers --- cpp/models/abm/world.cpp | 21 ++++++++++----------- cpp/models/abm/world.h | 15 +++++++-------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 4f94f06fa0..51fb2e0cc3 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -34,7 +34,7 @@ namespace abm LocationId World::add_location(LocationType type, uint32_t num_cells) { LocationId id = {static_cast(m_locations.size()), type}; - m_locations.emplace_back(std::make_unique(id, parameters.get_num_groups(), num_cells)); + m_locations.emplace_back(id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; if (m_local_populations_cache.is_valid()) { @@ -152,7 +152,7 @@ void World::begin_step(TimePoint t, TimeSpan dt) PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_locations.size(); ++i) { auto&& location = m_locations[i]; - location->cache_exposure_rates(t, dt, parameters.get_num_groups()); + location.cache_exposure_rates(t, dt, parameters.get_num_groups()); } } @@ -168,12 +168,12 @@ auto World::get_persons() const -> Range& loc) { - return running_sum + get_subpopulation(loc->get_id(), t, s); + [t, s, this](size_t running_sum, const Location& loc) { + return running_sum + get_subpopulation(loc.get_id(), t, s); }); } size_t World::get_subpopulation_combined_per_location_type(TimePoint t, InfectionState s, LocationType type) const { - return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, - [t, s, type, this](size_t running_sum, const std::unique_ptr& loc) { - return loc->get_type() == type ? running_sum + get_subpopulation(loc->get_id(), t, s) - : running_sum; - }); + return std::accumulate( + m_locations.begin(), m_locations.end(), (size_t)0, [t, s, type, this](size_t running_sum, const Location& loc) { + return loc.get_type() == type ? running_sum + get_subpopulation(loc.get_id(), t, s) : running_sum; + }); } TripList& World::get_trip_list() diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 4f327ee58a..132ca7b602 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -54,8 +54,8 @@ namespace abm class World { public: - using LocationIterator = PointerDereferencingIterator>::iterator>; - using ConstLocationIterator = PointerDereferencingIterator>::const_iterator>; + using LocationIterator = std::vector::iterator; + using ConstLocationIterator = std::vector::const_iterator; using PersonIterator = std::vector::iterator; using ConstPersonIterator = std::vector::const_iterator; @@ -99,8 +99,7 @@ class World for (auto& origin_loc : other.get_locations()) { if (origin_loc.get_type() != LocationType::Cemetery) { // Copy a location - m_locations.emplace_back( - std::make_unique(origin_loc.copy_location_without_persons(parameters.get_num_groups()))); + m_locations.push_back(origin_loc.copy_location_without_persons(parameters.get_num_groups())); } for (auto& person : other.get_persons()) { // If a person is in this location, copy this person and add it to this location. @@ -423,7 +422,7 @@ class World { // TODO: make sure this is correct assert(id.index != INVALID_LOCATION_INDEX); - return *m_locations[id.index]; + return m_locations[id.index]; } // get current location of the Person @@ -484,7 +483,7 @@ class World { m_local_populations_cache.data.clear(); for (size_t i = 0; i < m_locations.size(); i++) { - m_local_populations_cache.data[m_locations[i]->get_id()].clear(); + m_local_populations_cache.data[m_locations[i].get_id()].clear(); } for (Person& person : get_persons()) { m_local_populations_cache.data.at(person.get_location()).emplace(person.get_person_id()); @@ -496,8 +495,8 @@ class World // Cache>> m_air_exposure_rates_cache; // Cache>> m_contact_exposure_rates_cache; - std::vector m_persons; ///< Vector with pointers to every Person. - std::vector> m_locations; ///< Vector with pointers to every Location. + std::vector m_persons; ///< Vector of every Person. + std::vector m_locations; ///< Vector of every Location. std::bitset m_has_locations; ///< Flags for each LocationType, set if a Location of that type exists. TestingStrategy m_testing_strategy; ///< List of TestingScheme%s that are checked for testing. From 57613a3c3bdc7b95b9355156cdf63ec9c130be91 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:42:37 +0100 Subject: [PATCH 10/54] update includes and include guards --- cpp/memilio/utils/random_number_generator.h | 15 +++++++++------ cpp/models/abm/abm.h | 4 ++-- cpp/models/abm/common_abm_loggers.h | 5 ++++- cpp/models/abm/household.h | 6 +++--- cpp/models/abm/infection.h | 4 ++-- cpp/models/abm/infection_state.h | 4 ++-- cpp/models/abm/location.h | 4 ++-- cpp/models/abm/location_type.h | 4 ++-- cpp/models/abm/lockdown_rules.h | 4 ++-- cpp/models/abm/mask.h | 4 ++-- cpp/models/abm/mask_type.h | 4 ++-- cpp/models/abm/migration_rules.h | 6 +++--- cpp/models/abm/parameters.h | 4 ++-- cpp/models/abm/person.h | 4 ++-- cpp/models/abm/simulation.cpp | 3 --- cpp/models/abm/simulation.h | 7 ++----- cpp/models/abm/testing_strategy.h | 4 ++-- cpp/models/abm/time.h | 4 ++-- cpp/models/abm/trip_list.h | 4 ++-- cpp/models/abm/vaccine.h | 4 ++-- cpp/models/abm/world.h | 6 ++---- cpp/tests/abm_helpers.h | 1 - 22 files changed, 51 insertions(+), 54 deletions(-) diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index c5883e7465..2ab4cadd48 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef EPI_ABM_RANDOM_NUMBER_GENERATOR_H -#define EPI_ABM_RANDOM_NUMBER_GENERATOR_H +#ifndef MIO_RANDOM_NUMBER_GENERATOR_H +#define MIO_RANDOM_NUMBER_GENERATOR_H #include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/logging.h" @@ -182,7 +182,9 @@ static_assert(sizeof(Key) == sizeof(uint32_t), "Empty Base Optimizatio * @tparam an unsigned integer type that determines the size of the counter, i.e., the length of the random sequence. */ template -struct MEMILIO_ENABLE_EBO Counter : TypeSafe>, OperatorComparison>, OperatorAdditionSubtraction> { +struct MEMILIO_ENABLE_EBO Counter : TypeSafe>, + OperatorComparison>, + OperatorAdditionSubtraction> { static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); using TypeSafe>::TypeSafe; }; @@ -243,12 +245,13 @@ Counter rng_totalsequence_counter(UIntN subsequence_idx, CounterS counter static_assert(N_BITS <= C_BITS, "Subsequence index must not be bigger than total sequence counter."); static_assert(N_BITS <= sizeof(UIntN) * BITS_PER_BYTE, "Subsequence index must be at least N bits"); - assert(UIntC(subsequence_idx) <= (UIntC(1) << N_BITS) && "Subsequence index is too large."); //(1 << N) is the same as (2^N) + assert(UIntC(subsequence_idx) <= (UIntC(1) << N_BITS) && + "Subsequence index is too large."); //(1 << N) is the same as (2^N) //N high bits: subsequence idx //S low bits: subsequence counter //=> C = N + S bits: total sequence counter - //example: + //example: //subsequence index uint32_t(181) = 0x000000B5 //subsequence counter uint32_t(41309) = 0x0000A15D //total sequence counter = 0x000000B50000A15D @@ -271,7 +274,7 @@ Counter rng_totalsequence_counter(UIntN subsequence_idx, CounterS counter template Counter rng_subsequence_counter(CounterC counter) { - using UIntC = typename CounterC::ValueType; + using UIntC = typename CounterC::ValueType; static const UIntC C_BYTES = sizeof(UIntC); static const UIntC S_BYTES = sizeof(UIntS); diff --git a/cpp/models/abm/abm.h b/cpp/models/abm/abm.h index aa24659a1f..4a53ff52c8 100644 --- a/cpp/models/abm/abm.h +++ b/cpp/models/abm/abm.h @@ -19,8 +19,8 @@ */ /** single include header for ABM */ -#ifndef EPI_ABM_H -#define EPI_ABM_H +#ifndef MIO_ABM_H +#define MIO_ABM_H #include "abm/parameters.h" #include "abm/simulation.h" diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 93b1b53ee3..438ae0948b 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -21,11 +21,14 @@ #ifndef ABM_COMMON_LOGGERS_H #define ABM_COMMON_LOGGERS_H +#include "abm/infection_state.h" +#include "abm/simulation.h" #include "memilio/io/history.h" +#include "memilio/utils/time_series.h" #include "models/abm/location_type.h" #include "abm/movement_data.h" -#include "abm/abm.h" #include "memilio/utils/mioomp.h" + namespace mio { namespace abm diff --git a/cpp/models/abm/household.h b/cpp/models/abm/household.h index 06b7d0a7c2..67af6de67a 100644 --- a/cpp/models/abm/household.h +++ b/cpp/models/abm/household.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef EPI_ABM_HOUSEHOLD_H -#define EPI_ABM_HOUSEHOLD_H +#ifndef MIO_ABM_HOUSEHOLD_H +#define MIO_ABM_HOUSEHOLD_H #include "abm/world.h" #include "memilio/epidemiology/age_group.h" @@ -212,4 +212,4 @@ void add_household_group_to_world(World& world, const HouseholdGroup& household_ } // namespace abm } // namespace mio -#endif //EPI_ABM_HOUSEHOLD_H +#endif //MIO_ABM_HOUSEHOLD_H diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index f882d22af2..f0f1b1ecba 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_INFECTION_H -#define EPI_ABM_INFECTION_H +#ifndef MIO_ABM_INFECTION_H +#define MIO_ABM_INFECTION_H #include "abm/time.h" #include "abm/infection_state.h" diff --git a/cpp/models/abm/infection_state.h b/cpp/models/abm/infection_state.h index eb32ab984c..284602dcdb 100644 --- a/cpp/models/abm/infection_state.h +++ b/cpp/models/abm/infection_state.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_INFECTION_STATE_H -#define EPI_ABM_INFECTION_STATE_H +#ifndef MIO_ABM_INFECTION_STATE_H +#define MIO_ABM_INFECTION_STATE_H #include diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 8b8a6e505a..6066371cb5 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_LOCATION_H -#define EPI_ABM_LOCATION_H +#ifndef MIO_ABM_LOCATION_H +#define MIO_ABM_LOCATION_H #include "abm/person.h" #include "abm/mask_type.h" diff --git a/cpp/models/abm/location_type.h b/cpp/models/abm/location_type.h index 417b024c85..484f0edcaf 100644 --- a/cpp/models/abm/location_type.h +++ b/cpp/models/abm/location_type.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_LOCATION_TYPE_H -#define EPI_ABM_LOCATION_TYPE_H +#ifndef MIO_ABM_LOCATION_TYPE_H +#define MIO_ABM_LOCATION_TYPE_H #include "memilio/io/io.h" #include diff --git a/cpp/models/abm/lockdown_rules.h b/cpp/models/abm/lockdown_rules.h index 7ab4ab7e68..6780d6daa1 100644 --- a/cpp/models/abm/lockdown_rules.h +++ b/cpp/models/abm/lockdown_rules.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_LOCKDOWN_RULES_H -#define EPI_ABM_LOCKDOWN_RULES_H +#ifndef MIO_ABM_LOCKDOWN_RULES_H +#define MIO_ABM_LOCKDOWN_RULES_H #include "abm/time.h" #include "abm/location_type.h" diff --git a/cpp/models/abm/mask.h b/cpp/models/abm/mask.h index 80894171d4..2b9048925b 100644 --- a/cpp/models/abm/mask.h +++ b/cpp/models/abm/mask.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef EPI_ABM_MASK_H -#define EPI_ABM_MASK_H +#ifndef MIO_ABM_MASK_H +#define MIO_ABM_MASK_H #include "abm/mask_type.h" #include "abm/time.h" diff --git a/cpp/models/abm/mask_type.h b/cpp/models/abm/mask_type.h index 6bac162944..8477e1d354 100644 --- a/cpp/models/abm/mask_type.h +++ b/cpp/models/abm/mask_type.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef EPI_ABM_MASK_TYPE_H -#define EPI_ABM_MASK_TYPE_H +#ifndef MIO_ABM_MASK_TYPE_H +#define MIO_ABM_MASK_TYPE_H #include diff --git a/cpp/models/abm/migration_rules.h b/cpp/models/abm/migration_rules.h index b6f395d197..3384e774f5 100644 --- a/cpp/models/abm/migration_rules.h +++ b/cpp/models/abm/migration_rules.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_MIGRATION_RULES_H -#define EPI_ABM_MIGRATION_RULES_H +#ifndef MIO_ABM_MIGRATION_RULES_H +#define MIO_ABM_MIGRATION_RULES_H #include "abm/location_type.h" #include "abm/parameters.h" @@ -106,4 +106,4 @@ LocationType get_buried(Person::RandomNumberGenerator& rng, const Person& person } // namespace abm } // namespace mio -#endif //EPI_ABM_MIGRATION_RULES_H +#endif //MIO_ABM_MIGRATION_RULES_H diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index 6460777b30..b943fc6bc6 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_PARAMETERS_H -#define EPI_ABM_PARAMETERS_H +#ifndef MIO_ABM_PARAMETERS_H +#define MIO_ABM_PARAMETERS_H #include "abm/mask_type.h" #include "abm/time.h" diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 59d3946252..158ab77675 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_PERSON_H -#define EPI_ABM_PERSON_H +#ifndef MIO_ABM_PERSON_H +#define MIO_ABM_PERSON_H #include "abm/location_type.h" #include "abm/infection_state.h" diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index 54e9024ce7..d716e4e23e 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -18,9 +18,6 @@ * limitations under the License. */ #include "abm/simulation.h" -#include "memilio/utils/logging.h" -#include "memilio/utils/mioomp.h" -#include namespace mio { diff --git a/cpp/models/abm/simulation.h b/cpp/models/abm/simulation.h index 7247a69819..ecc8cd8c74 100644 --- a/cpp/models/abm/simulation.h +++ b/cpp/models/abm/simulation.h @@ -17,14 +17,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_SIMULATOR_H -#define EPI_ABM_SIMULATOR_H +#ifndef MIO_ABM_SIMULATION_H +#define MIO_ABM_SIMULATION_H #include "abm/world.h" #include "abm/time.h" -#include "memilio/utils/time_series.h" -#include "memilio/compartments/compartmentalmodel.h" -#include "memilio/epidemiology/populations.h" #include "memilio/io/history.h" namespace mio diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index e947ec30c7..4374b7fa5b 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_TESTING_SCHEME_H -#define EPI_ABM_TESTING_SCHEME_H +#ifndef MIO_ABM_TESTING_SCHEME_H +#define MIO_ABM_TESTING_SCHEME_H #include "abm/config.h" #include "abm/parameters.h" diff --git a/cpp/models/abm/time.h b/cpp/models/abm/time.h index 7b5f0b8e1b..45ad5495d4 100644 --- a/cpp/models/abm/time.h +++ b/cpp/models/abm/time.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_TIME_H -#define EPI_ABM_TIME_H +#ifndef MIO_ABM_TIME_H +#define MIO_ABM_TIME_H namespace mio { diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index 51ce065667..832d21e6e6 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_TRIP_LIST_H -#define EPI_ABM_TRIP_LIST_H +#ifndef MIO_ABM_TRIP_LIST_H +#define MIO_ABM_TRIP_LIST_H #include "abm/parameters.h" #include "abm/location.h" diff --git a/cpp/models/abm/vaccine.h b/cpp/models/abm/vaccine.h index 29199abb51..72da133516 100644 --- a/cpp/models/abm/vaccine.h +++ b/cpp/models/abm/vaccine.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_VACCINE_H -#define EPI_ABM_VACCINE_H +#ifndef MIO_ABM_VACCINE_H +#define MIO_ABM_VACCINE_H #include "abm/time.h" diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 132ca7b602..699bf0749c 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef EPI_ABM_WORLD_H -#define EPI_ABM_WORLD_H +#ifndef MIO_ABM_WORLD_H +#define MIO_ABM_WORLD_H #include "abm/config.h" #include "abm/location_type.h" @@ -30,7 +30,6 @@ #include "abm/random_events.h" #include "abm/testing_strategy.h" #include "memilio/utils/compiler_diagnostics.h" -#include "memilio/utils/pointer_dereferencing_iterator.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" @@ -40,7 +39,6 @@ #include #include #include -#include namespace mio { diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 4386b53fe6..015eff3011 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -22,7 +22,6 @@ #include "abm/abm.h" #include "gmock/gmock.h" -#include // Assign the name to general age group. const size_t num_age_groups = 6; From 8d37523bad12d3e45b34c7fc9a3afabf9c3dcae5 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 30 Jan 2024 15:56:09 +0100 Subject: [PATCH 11/54] remove m_persons from cells, completely rework caching of exposure rates, change interface of mio::abm::interact --- cpp/models/abm/functions.cpp | 104 ++++++++++++++++++-- cpp/models/abm/functions.h | 17 +++- cpp/models/abm/location.cpp | 66 +------------ cpp/models/abm/location.h | 62 +++--------- cpp/models/abm/parameters.h | 15 ++- cpp/models/abm/person.cpp | 6 +- cpp/models/abm/world.cpp | 8 +- cpp/models/abm/world.h | 81 ++++++++------- cpp/tests/abm_helpers.cpp | 4 +- cpp/tests/test_abm_location.cpp | 130 ++++++++++++------------- cpp/tests/test_abm_masks.cpp | 11 +-- cpp/tests/test_abm_migration_rules.cpp | 4 +- cpp/tests/test_abm_person.cpp | 6 +- cpp/tests/test_abm_world.cpp | 26 +---- 14 files changed, 265 insertions(+), 275 deletions(-) diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index 6754deac00..bf2a775f3e 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -1,7 +1,17 @@ #include "functions.h" +#include "abm/location.h" +#include "abm/person.h" #include "abm/random_events.h" #include "abm/infection.h" +#include "abm/trip_list.h" +#include "abm/virus_variant.h" +#include "memilio/epidemiology/age_group.h" +#include "memilio/utils/compiler_diagnostics.h" +#include "memilio/utils/logging.h" +#include +#include +#include namespace mio { @@ -9,9 +19,42 @@ namespace mio namespace abm { -void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, const Parameters& global_parameters, - Person::RandomNumberGenerator& personal_rng) +// TODO: on argument order: maybe personal_rng first, as it always(?) is a non-const reference + +ScalarType daily_transmissions_by_contacts(const Location::ContactExposureRates& rates, CellIndex cell_index, + VirusVariant virus, AgeGroup age_receiver, + const LocalInfectionParameters& params) +{ + assert(age_receiver < rates.size()); + ScalarType prob = 0; + for (AgeGroup age_transmitter(0); age_transmitter < rates.size(); ++age_transmitter) { + prob += + rates[{cell_index, virus, age_transmitter}] * params.get()[{age_receiver, age_transmitter}]; + } + return prob; +} + +ScalarType daily_transmissions_by_air(const Location::AirExposureRates& rates, CellIndex cell_index, VirusVariant virus, + const Parameters& global_params) { + return rates[{cell_index, virus}] * global_params.get()[{virus}]; +} + +void interact(Person& person, const Location& location, const Location::AirExposureRates& local_air_exposure, + const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, + const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng) +{ + // make sure all dimensions are set correctly and all indices are valid + assert(location.get_cells().size() == local_air_exposure.size().get()); + assert(location.get_cells().size() == local_contact_exposure.size().get()); + assert(local_contact_exposure.size() == local_air_exposure.size()); + assert(local_contact_exposure.size() == VirusVariant::Count); + assert(local_contact_exposure.size().get() == global_parameters.get_num_groups()); + assert(person.get_age() < local_contact_exposure.size()); + assert(std::all_of(person.get_cells().begin(), person.get_cells().end(), [&](const auto& cell) { + return cell < location.get_cells().size(); + })); + if (person.get_infection_state(t) == InfectionState::Susceptible) { auto& local_parameters = location.get_infection_parameters(); // TODO: we need to define what a cell is used for, as the loop may lead to incorrect results for multiple cells @@ -25,10 +68,10 @@ void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, cons VirusVariant virus = static_cast(v); ScalarType local_indiv_trans_prob_v = (std::min(local_parameters.get(), - location.transmission_contacts_per_day(cell_index, virus, age_receiver, - global_parameters.get_num_groups())) + - location.transmission_air_per_day(cell_index, virus, global_parameters)) * - (1 - mask_protection) * dt.days() * (1 - person.get_protection_factor(t, virus, global_parameters)); + daily_transmissions_by_contacts(local_contact_exposure, cell_index, virus, age_receiver, + local_parameters)) + + daily_transmissions_by_air(local_air_exposure, cell_index, virus, global_parameters)) * + dt.days() * (1 - mask_protection) * (1 - person.get_protection_factor(t, virus, global_parameters)); local_indiv_trans_prob[v] = std::make_pair(virus, local_indiv_trans_prob_v); } @@ -45,8 +88,55 @@ void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, cons person.add_time_at_location(dt); } -bool migrate(Person& person, Location& destination, const std::vector& cells, TransportMode mode) +void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, + Location::ContactExposureRates& local_contact_exposure, const Person& person, + Location& location, TimePoint t, TimeSpan dt) +{ // TODO: location should be const, but there is no const cell accessor + assert([&]() { + if (person.get_location() != location.get_id()) { + mio::log_warning("Person with id {} is not at Location with id {}", person.get_person_id(), + location.get_index()); + } + return true; + }()); + if (person.is_infected(t)) { + auto& infection = person.get_infection(); + auto virus = infection.get_virus_variant(); + auto age = person.get_age(); + // average infectivity over the time step to second order accuracy using midpoint rule + for (CellIndex cell : person.get_cells()) { + if (location.get_infection_parameters().get()) { + local_air_exposure[{cell, virus}] += + infection.get_infectivity(t + dt / 2) * + location.get_cells()[cell.get()].compute_space_per_person_relative(); + } + else { + local_air_exposure[{cell, virus}] += infection.get_infectivity(t + dt / 2); + } + local_contact_exposure[{cell, virus, age}] += infection.get_infectivity(t + dt / 2); + } + } +} + +void interact(Person& person, Location& location, const std::vector& local_population, const TimePoint t, + const TimeSpan dt, const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng) +{ // TODO: make location const&, need to fix add_exposure_contribution + Location::AirExposureRates local_air_exposure{{CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.}; + Location::ContactExposureRates local_contact_exposure{ + {CellIndex(location.get_cells().size()), VirusVariant::Count, AgeGroup(global_parameters.get_num_groups())}, + 0.}; + for (const Person& p : local_population) { + add_exposure_contribution(local_air_exposure, local_contact_exposure, p, location, t, dt); + } + interact(person, location, local_air_exposure, local_contact_exposure, t, dt, global_parameters, personal_rng); +} + +bool migrate(Person& person, const Location& destination, const std::vector& cells, const TransportMode mode) { + assert(std::all_of(cells.begin(), cells.end(), [&](const auto& cell) { + return cell < destination.get_cells().size(); + })); // make sure cell indices are valid + if (person.get_location() != destination.get_id()) { person.set_location(destination); person.get_cells() = cells; diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h index 4f9b9b6e0c..a9ff97a3f9 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/functions.h @@ -5,6 +5,7 @@ #include "abm/person.h" #include "abm/location.h" +#include namespace mio { @@ -12,13 +13,21 @@ namespace mio namespace abm { +void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, + Location::ContactExposureRates& local_contact_exposure, const Person& person, + Location& location, TimePoint t, TimeSpan dt); + // let a person interact with a location for and at some time -void interact(Person& person, Location& location, TimePoint t, TimeSpan dt, const Parameters& global_parameters, - Person::RandomNumberGenerator& personal_rng); +void interact(Person& person, const Location& location, const Location::AirExposureRates& local_air_exposure, + const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, + const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng); + +void interact(Person& person, Location& location, const std::vector& local_population, const TimePoint t, + const TimeSpan dt, const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng); // move a person to another location. returns false if the person was at the target location already, true otherwise -bool migrate(Person& person, Location& destination, const std::vector& cells = {0}, - TransportMode mode = TransportMode::Unknown); +bool migrate(Person& person, const Location& destination, const std::vector& cells = {0}, + const TransportMode mode = TransportMode::Unknown); } // namespace abm } // namespace mio diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index a943034de3..ec572a759f 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -19,6 +19,7 @@ */ #include "abm/mask_type.h" #include "abm/location.h" +#include "abm/parameters.h" #include "abm/random_events.h" #include "abm/infection.h" @@ -29,69 +30,17 @@ namespace abm Location::Location(LocationId loc_id, size_t num_agegroups, uint32_t num_cells) : m_id(loc_id) - , m_capacity_adapted_transmission_risk(false) , m_parameters(num_agegroups) - , m_cells(num_cells, num_agegroups) + , m_cells(num_cells) , m_required_mask(MaskType::Community) , m_npi_active(false) { assert(num_cells > 0 && "Number of cells has to be larger than 0."); } -Location Location::copy_location_without_persons(size_t num_agegroups) const +Location Location::copy_location_without_persons(size_t) const { - Location copy_loc = Location(*this); - copy_loc.m_cells = std::vector{num_agegroups}; - for (uint32_t idx = 0; idx < m_cells.size(); idx++) { - copy_loc.set_capacity(get_capacity(idx).persons, get_capacity(idx).volume, idx); - copy_loc.get_cached_exposure_rate_contacts(idx) = get_cached_exposure_rate_contacts(idx); - copy_loc.get_cached_exposure_rate_air(idx) = get_cached_exposure_rate_air(idx); - } - return copy_loc; -} - -ScalarType Location::transmission_contacts_per_day(uint32_t cell_index, VirusVariant virus, AgeGroup age_receiver, - size_t num_agegroups) const -{ - assert(age_receiver.get() < num_agegroups); - ScalarType prob = 0; - for (uint32_t age_transmitter = 0; age_transmitter != num_agegroups; ++age_transmitter) { - prob += m_cells[cell_index].m_cached_exposure_rate_contacts[{virus, static_cast(age_transmitter)}] * - m_parameters.get()[{age_receiver, static_cast(age_transmitter)}]; - } - return prob; -} - -ScalarType Location::transmission_air_per_day(uint32_t cell_index, VirusVariant virus, - const Parameters& global_params) const -{ - return m_cells[cell_index].m_cached_exposure_rate_air[{virus}] * - global_params.get()[{virus}]; -} - -void Location::cache_exposure_rates(TimePoint t, TimeSpan dt, size_t num_agegroups) -{ - //cache for next step so it stays constant during the step while subpopulations change - //otherwise we would have to cache all state changes during a step which uses more memory - for (auto& cell : m_cells) { - cell.m_cached_exposure_rate_contacts = {{VirusVariant::Count, AgeGroup(num_agegroups)}, 0.}; - cell.m_cached_exposure_rate_air = {{VirusVariant::Count}, 0.}; - for (auto&& p : cell.m_persons) { - if (p->is_infected(t)) { - auto& inf = p->get_infection(); - auto virus = inf.get_virus_variant(); - auto age = p->get_age(); - /* average infectivity over the time step - * to second order accuracy using midpoint rule - */ - cell.m_cached_exposure_rate_contacts[{virus, age}] += inf.get_infectivity(t + dt / 2); - cell.m_cached_exposure_rate_air[{virus}] += inf.get_infectivity(t + dt / 2); - } - } - if (m_capacity_adapted_transmission_risk) { - cell.m_cached_exposure_rate_air.array() *= cell.compute_space_per_person_relative(); - } - } + return *this; } /* @@ -108,12 +57,5 @@ ScalarType Cell::compute_space_per_person_relative() } } -size_t Cell::get_subpopulation(TimePoint t, InfectionState state) const -{ - return count_if(m_persons.begin(), m_persons.end(), [&](observer_ptr p) { - return p->get_infection_state(t) == state; - }); -} - } // namespace abm } // namespace mio diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 6066371cb5..7d28a2c781 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -35,6 +35,13 @@ namespace abm { class Person; +struct CellIndex : public mio::Index { + CellIndex(size_t i) + : mio::Index(i) + { + } +}; + /** * @brief CellCapacity describes the size of a Cell. * It consists of a volume and a capacity in Person%s which is an upper bound for the number @@ -55,19 +62,8 @@ struct CellCapacity { * This allows a finer division of the people at the Location. */ struct Cell { - std::vector> m_persons; - CustomIndexArray m_cached_exposure_rate_contacts; - CustomIndexArray m_cached_exposure_rate_air; CellCapacity m_capacity; - Cell(size_t num_agegroups, std::vector> persons = {}) - : m_persons(std::move(persons)) - , m_cached_exposure_rate_contacts({{VirusVariant::Count, AgeGroup(num_agegroups)}, 0.}) - , m_cached_exposure_rate_air({{VirusVariant::Count}, 0.}) - , m_capacity() - { - } - /** * @brief Computes a relative cell size for the Cell. * @return The relative cell size for the Cell. @@ -80,7 +76,7 @@ struct Cell { * @param[in] state #InfectionState of interest. * @return Amount of Person%s of the #InfectionState in the Cell. */ - size_t get_subpopulation(TimePoint t, InfectionState state) const; + // size_t get_subpopulation(TimePoint t, InfectionState state) const; }; // namespace mio @@ -90,6 +86,8 @@ struct Cell { class Location { public: + using ContactExposureRates = CustomIndexArray; + using AirExposureRates = CustomIndexArray; /** * @brief Construct a Location of a certain LocationId. * @param[in] loc_id The #LocationId. @@ -167,8 +165,8 @@ class Location * @param[in] num_agegroups The number of age groups in the model. * @return Amount of average Infection%s with the virus from the AgeGroup of the transmitter per day. */ - ScalarType transmission_contacts_per_day(uint32_t cell_index, VirusVariant virus, AgeGroup age_receiver, - size_t num_agegroups) const; + // ScalarType transmission_contacts_per_day(uint32_t cell_index, VirusVariant virus, AgeGroup age_receiver, + // size_t num_agegroups) const; /** * @brief Compute the transmission factor for a aerosol transmission of the virus in a Cell. @@ -177,7 +175,7 @@ class Location * @param[in] global_params The Parameters set of the World. * @return Amount of average Infection%s with the virus per day. */ - ScalarType transmission_air_per_day(uint32_t cell_index, VirusVariant virus, const Parameters& global_params) const; + // ScalarType transmission_air_per_day(uint32_t cell_index, VirusVariant virus, const Parameters& global_params) const; /** * @brief Prepare the Location for the next Simulation step. @@ -185,7 +183,7 @@ class Location * @param[in] dt The duration of the Simulation step. * @param[in] num_agegroups The number of age groups in the model. */ - void cache_exposure_rates(TimePoint t, TimeSpan dt, size_t num_agegroups); + // void cache_exposure_rates(TimePoint t, TimeSpan dt, size_t num_agegroups); /** * @brief Get the Location specific Infection parameters. @@ -234,26 +232,6 @@ class Location m_required_mask = type; } - /** - * @brief Get the contact exposure rate in the Cell. - * @param[in] cell_idx Cell index of interest. - * @return Air exposure rate in the Cell. - */ - CustomIndexArray get_cached_exposure_rate_contacts(uint32_t cell_idx) const - { - return m_cells[cell_idx].m_cached_exposure_rate_contacts; - } - - /** - * @brief Get the air exposure rate in the Cell. - * @param[in] cell_idx Cell index of interest. - * @return Contact exposure rate in the cell. - */ - CustomIndexArray get_cached_exposure_rate_air(uint32_t cell_idx) const - { - return m_cells[cell_idx].m_cached_exposure_rate_air; - } - /** * @brief Set the CellCapacity of a Cell in the Location in persons and volume. * @param[in] persons Maximum number of Person%s that can visit the Cell at the same time. @@ -276,16 +254,6 @@ class Location return m_cells[cell_idx].m_capacity; } - /** - * @brief Set the capacity adapted transmission risk flag. - * @param[in] consider_capacity If true considers the capacity of the Cell%s of this Location for the computation of - * relative transmission risk. - */ - void set_capacity_adapted_transmission_risk_flag(bool consider_capacity) - { - m_capacity_adapted_transmission_risk = consider_capacity; - } - /** * @brief Get the information whether NPIs are active at this Location. * If true requires e.g. Mask%s when entering a Location. @@ -361,8 +329,6 @@ class Location private: LocationId m_id; ///< Id of the Location including type and index. - bool m_capacity_adapted_transmission_risk; /**< If true considers the LocationCapacity for the computation of the - transmission risk.*/ LocalInfectionParameters m_parameters; ///< Infection parameters for the Location. std::vector m_cells{}; ///< A vector of all Cell%s that the Location is divided in. MaskType m_required_mask; ///< Least secure type of Mask that is needed to enter the Location. diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index b943fc6bc6..d9149daa30 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -583,10 +583,23 @@ struct ContactRates { } }; +// If true, consider the capacity of the Cell%s of this Location for the computation of relative transmission risk. +struct UseLocationCapacityForTransmissions { + using Type = bool; + static Type get_default(AgeGroup) + { + return false; + } + static std::string name() + { + return "UseLocationCapacityForTransmissions"; + } +}; + /** * @brief Parameters of the Infection that depend on the Location. */ -using LocalInfectionParameters = ParameterSet; +using LocalInfectionParameters = ParameterSet; /** * @brief Parameters of the simulation that are the same everywhere within the World. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index d80422c0c5..abb7977614 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -59,9 +59,9 @@ Person::Person(const Person& other, PersonId id) Person Person::copy_person(Location& location) { - Person copied_person = Person(*this); - copied_person.m_location = location.get_id(); - return copied_person; + Person copy = *this; + copy.set_location(location); + return copy; } bool Person::is_infected(TimePoint t) const diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 51fb2e0cc3..5597319e35 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -147,13 +147,7 @@ void World::begin_step(TimePoint t, TimeSpan dt) rebuild(); m_local_populations_cache.validate(); } - // PRAGMA_OMP(parallel for) - - PRAGMA_OMP(parallel for) - for (auto i = size_t(0); i < m_locations.size(); ++i) { - auto&& location = m_locations[i]; - location.cache_exposure_rates(t, dt, parameters.get_num_groups()); - } + recompute_exposure_rates(t, dt); } auto World::get_locations() const -> Range> diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 699bf0749c..2d592f54f5 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -26,10 +26,16 @@ #include "abm/parameters.h" #include "abm/location.h" #include "abm/person.h" +#include "abm/time.h" #include "abm/trip_list.h" #include "abm/random_events.h" #include "abm/testing_strategy.h" +#include "abm/virus_variant.h" +#include "memilio/config.h" +#include "memilio/epidemiology/age_group.h" #include "memilio/utils/compiler_diagnostics.h" +#include "memilio/utils/custom_index_array.h" +#include "memilio/utils/index.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" @@ -37,6 +43,8 @@ #include #include +#include +#include #include #include @@ -83,35 +91,8 @@ class World parameters = params; } - /** - * @brief Create a copied World. - * @param[in] other The World that needs to be copied. - */ - World(const World& other) - : parameters(other.parameters) - , m_persons() - , m_locations() - , m_trip_list(other.m_trip_list) - , m_cemetery_id(add_location(LocationType::Cemetery)) - { - for (auto& origin_loc : other.get_locations()) { - if (origin_loc.get_type() != LocationType::Cemetery) { - // Copy a location - m_locations.push_back(origin_loc.copy_location_without_persons(parameters.get_num_groups())); - } - for (auto& person : other.get_persons()) { - // If a person is in this location, copy this person and add it to this location. - if (person.get_location() == origin_loc.get_id()) { - const auto id = m_persons.size(); - m_persons.emplace_back(person, id); - // std::make_unique(person.copy_person(get_individualized_location(origin_loc.get_id())))); - } - } - } - use_migration_rules(other.m_use_migration_rules); - } - //type is move-only for stable references of persons/locations + World(const World&) = default; World(World&& other) = default; World& operator=(World&& other) = default; World& operator=(const World&) = delete; @@ -196,8 +177,10 @@ class World // adds a copy of person to the world PersonId add_person(const Person& person) { - mio::unused(get_location( - person.get_location())); // assert that location is in world // TODO: check if this acts like an assert + assert([&]() { + get_location(person.get_location()); + return true; + }()); // assert that location is in world assert(person.get_age().get() < parameters.get_num_groups()); uint32_t new_id = static_cast(m_persons.size()); // TODO: this breaks when removing a person @@ -205,9 +188,6 @@ class World auto& new_person = m_persons.back(); new_person.set_assigned_location(m_cemetery_id); - // TODO: remove and/or refactor - get_location(new_person.get_location()).get_cells()[0].m_persons.push_back(&new_person); - if (m_local_populations_cache.is_valid()) { m_local_populations_cache.data.at(new_person.get_location()).emplace(new_id); } @@ -412,14 +392,20 @@ class World inline void interact(PersonId person, TimePoint t, TimeSpan dt, Person::RandomNumberGenerator& personal_rng, const Parameters& global_parameters) { - mio::abm::interact(get_person(person), get_location(person), t, dt, global_parameters, personal_rng); + if (!m_air_exposure_rates_cache.is_valid() || !m_contact_exposure_rates_cache.is_valid()) { + recompute_exposure_rates(t, dt); + } + mio::abm::interact(get_person(person), get_location(person), + m_air_exposure_rates_cache.data.at(get_location(person).get_id()), + m_contact_exposure_rates_cache.data.at(get_location(person).get_id()), t, dt, + global_parameters, personal_rng); } // get location by id Location& get_location(LocationId id) { - // TODO: make sure this is correct assert(id.index != INVALID_LOCATION_INDEX); + assert(id.index < m_locations.size()); return m_locations[id.index]; } @@ -488,10 +474,31 @@ class World } } + void recompute_exposure_rates(TimePoint t, TimeSpan dt) + { + m_air_exposure_rates_cache.data.clear(); + m_contact_exposure_rates_cache.data.clear(); + for (Location& location : m_locations) { + m_air_exposure_rates_cache.data.emplace( + location.get_id(), + Location::AirExposureRates({CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.)); + m_contact_exposure_rates_cache.data.emplace( + location.get_id(), + Location::ContactExposureRates({CellIndex(location.get_cells().size()), VirusVariant::Count, + AgeGroup(parameters.get_num_groups())}, + 0.)); + } + for (Person& person : get_persons()) { + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.data.at(person.get_location()), + m_contact_exposure_rates_cache.data.at(person.get_location()), person, + get_location(person.get_person_id()), t, dt); + } + } + Cache>> m_local_populations_cache; // TODO: change this to storing only (sub)population(s) in numbers, using atomic ints - // Cache>> m_air_exposure_rates_cache; - // Cache>> m_contact_exposure_rates_cache; + Cache> m_air_exposure_rates_cache; + Cache> m_contact_exposure_rates_cache; std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index e0ceb1142d..4444720313 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -26,8 +26,8 @@ mio::abm::Person make_test_person(mio::abm::Location& location, mio::AgeGroup ag mio::abm::Parameters params) { assert(age.get() < params.get_num_groups()); - auto rng = mio::RandomNumberGenerator(); - mio::abm::Person p = mio::abm::Person(rng, location, age); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Person p(rng, location, age); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); p.add_new_infection( diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index fa2ab70562..b046797279 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -20,6 +20,7 @@ #include "abm/functions.h" #include "abm/infection.h" +#include "abm/parameters.h" #include "abm/person.h" #include "abm/world.h" #include "abm_helpers.h" @@ -100,63 +101,63 @@ TEST(TestLocation, getIndex) // ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); // } -TEST(TestLocation, CacheExposureRate) -{ - using testing::Return; - - auto rng = mio::RandomNumberGenerator(); - - mio::AgeGroup age = - mio::AgeGroup(mio::UniformIntDistribution::get_instance()(rng, 0, int(num_age_groups - 1))); - mio::abm::VirusVariant variant = mio::abm::VirusVariant( - mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::VirusVariant::Count) - 1)); - - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::seconds(10000); - - mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); - - // setup a location with some chance of exposure - mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups, 1); - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, num_age_groups, 3); - auto infected1 = mio::abm::Person(rng, home, age); - auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); - infected1.add_new_infection( - mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - mio::abm::migrate(infected1, location, {0}); - auto infected2 = mio::abm::Person(rng, home, age); - auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); - infected2.add_new_infection( - mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - mio::abm::migrate(infected2, location, {0, 1}); - - // TODO: cells - location.get_cells()[0].m_persons.emplace_back(&infected1); - location.get_cells()[0].m_persons.emplace_back(&infected2); - location.get_cells()[1].m_persons.emplace_back(&infected2); - - //cache precomputed results - location.cache_exposure_rates(t, dt, num_age_groups); - - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, 1e-14); - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.015015859523894731, 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), 0.0075079297619473654, - 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.0075079297619473654, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); - - // should also work with capacities - location.set_capacity_adapted_transmission_risk_flag(true); - location.set_capacity(2, 22, 0); // Capacity for Cell 1 - location.set_capacity(2, 22, 1); // Capacity for Cell 2 - location.set_capacity(2, 22, 2); // Capacity for Cell 3 - location.cache_exposure_rates(t, dt, num_age_groups); - - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); -} +// TODO: outdated. maybe repurpose for new global functions +// TEST(TestLocation, CacheExposureRate) +// { +// using testing::Return; + +// auto rng = mio::RandomNumberGenerator(); + +// mio::AgeGroup age = +// mio::AgeGroup(mio::UniformIntDistribution::get_instance()(rng, 0, int(num_age_groups - 1))); +// mio::abm::VirusVariant variant = mio::abm::VirusVariant( +// mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::VirusVariant::Count) - 1)); + +// auto t = mio::abm::TimePoint(0); +// auto dt = mio::abm::seconds(10000); + +// mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); + +// // setup a location with some chance of exposure +// mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups, 1); +// mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, num_age_groups, 3); +// auto infected1 = mio::abm::Person(rng, home, age); +// auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); +// infected1.add_new_infection( +// mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); +// mio::abm::migrate(infected1, location, {0}); +// auto infected2 = mio::abm::Person(rng, home, age); +// auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); +// infected2.add_new_infection( +// mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); +// mio::abm::migrate(infected2, location, {0, 1}); + +// location.get_cells()[0].m_persons.emplace_back(&infected1); +// location.get_cells()[0].m_persons.emplace_back(&infected2); +// location.get_cells()[1].m_persons.emplace_back(&infected2); + +// //cache precomputed results +// location.cache_exposure_rates(t, dt, num_age_groups); + +// EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, 1e-14); +// EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.015015859523894731, 1e-14); +// EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), 0.0075079297619473654, +// 1e-14); +// EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.0075079297619473654, 1e-14); +// EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); +// EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); + +// // should also work with capacities +// location.get_infection_parameters().set(true); +// location.set_capacity(2, 22, 0); // Capacity for Cell 1 +// location.set_capacity(2, 22, 1); // Capacity for Cell 2 +// location.set_capacity(2, 22, 2); // Capacity for Cell 3 +// location.cache_exposure_rates(t, dt, num_age_groups); + +// EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); +// EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); +// EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); +// } TEST(TestLocation, reachCapacity) { @@ -259,16 +260,7 @@ TEST(TestLocation, interact) make_test_person(location, age_group_80_plus, mio::abm::InfectionState::InfectedSymptoms, t, params); auto infected3 = make_test_person(location, age_group_5_to_14, mio::abm::InfectionState::InfectedSymptoms, t, params); - - // TODO: is this really superseeded? - // location.add_person(infected1, {0}); - // location.add_person(infected2, {0}); - // location.add_person(infected3, {0}); - // TODO: no, not yet : cells - location.get_cells()[0].m_persons = {&infected1, &infected2, &infected3}; - - //cache precomputed results - location.cache_exposure_rates(t, dt, num_age_groups); + std::vector local_population{infected1, infected2, infected3}; ScopedMockDistribution>>> mock_exponential_dist; @@ -277,12 +269,12 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); auto person_rng = mio::abm::Person::RandomNumberGenerator(rng, susceptible); - mio::abm::interact(susceptible, location, t, dt, params, person_rng); + mio::abm::interact(susceptible, location, local_population, t, dt, params, person_rng); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - mio::abm::interact(susceptible, location, t, dt, params, person_rng); + mio::abm::interact(susceptible, location, local_population, t, dt, params, person_rng); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); } diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index c2f9de7ffd..ed8cd30627 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -70,13 +70,8 @@ TEST(TestMasks, maskProtection) auto infected1 = make_test_person(infection_location, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior - // infection_location.add_person(infected1); - // TODO: Change this with cells - infection_location.get_cells()[0].m_persons.push_back(&infected1); - //cache precomputed results auto dt = mio::abm::days(1); - infection_location.cache_exposure_rates(t, dt, num_age_groups); // susc_person1 wears a mask, default protection is 1 susc_person1.set_wear_mask(true); // susc_person2 does not wear a mask @@ -87,10 +82,12 @@ TEST(TestMasks, maskProtection) mock_exponential_dist; auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person1); - mio::abm::interact(susc_person1, infection_location, t, dt, params, p1_rng); + mio::abm::interact(susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params, + p1_rng); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person2); - mio::abm::interact(susc_person2, infection_location, t, dt, params, p2_rng); + mio::abm::interact(susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params, + p2_rng); // The person susc_person1 should have full protection against an infection, susc_person2 not ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 7f8acbf2eb..9a2f852e6f 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -418,7 +418,7 @@ TEST(TestMigrationRules, shop_return) auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); mio::abm::migrate(p, shop); - mio::abm::interact(p, shop, t, dt, params, rng_p); //person only returns home after some time passed + mio::abm::interact(p, shop, {p}, t, dt, params, rng_p); //person only returns home after some time passed ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); @@ -468,7 +468,7 @@ TEST(TestMigrationRules, event_return) auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); mio::abm::migrate(p, social_event); - mio::abm::interact(p, social_event, t, dt, params, rng_p); + mio::abm::interact(p, social_event, {p}, t, dt, params, rng_p); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 50863fdc33..6ea3a6296f 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -65,19 +65,15 @@ TEST(TestPerson, migrate) mio::abm::migrate(person, loc1, {0}); EXPECT_EQ(person.get_location(), loc1.get_id()); - // EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); // TODO: cells EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); mio::abm::migrate(person, loc2, {0}, mio::abm::TransportMode::Walking); EXPECT_EQ(person.get_location(), loc2.get_id()); - // EXPECT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); mio::abm::migrate(person, loc3, {0, 1}, mio::abm::TransportMode::Bike); - // EXPECT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); - // EXPECT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); EXPECT_EQ(person.get_cells().size(), 2); EXPECT_EQ(person.get_cells()[0], 0u); EXPECT_EQ(person.get_cells()[1], 1u); @@ -211,7 +207,7 @@ TEST(TestPerson, interact) auto person = mio::abm::Person(rng, loc, age_group_15_to_34); auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days - mio::abm::interact(person, loc, t, dt, infection_parameters, rng_person); + mio::abm::interact(person, loc, {person}, t, dt, infection_parameters, rng_person); EXPECT_EQ(person.get_time_at_location(), dt); } diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 0ff7d68247..0a8f69f47d 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -104,7 +104,7 @@ TEST(TestWorld, findLocation) auto home_id = world.add_location(mio::abm::LocationType::Home); auto school_id = world.add_location(mio::abm::LocationType::School); auto work_id = world.add_location(mio::abm::LocationType::Work); - auto person = world.get_person(add_test_person(world, home_id)); + auto& person = world.get_person(add_test_person(world, home_id)); person.set_assigned_location(home_id); person.set_assigned_location(work_id); @@ -158,9 +158,6 @@ TEST(TestWorld, evolveStateTransition) p2.set_assigned_location(location1); p3.set_assigned_location(location2); - // TODO: CELLS! this includes reworking Location::transmission_X_per_day (especially for mio::abm::interact) - world.get_location(location1).get_cells()[0].m_persons.push_back(&p1); - //setup mock so p2 becomes infected ScopedMockDistribution>>> mock_exponential_dist; @@ -676,19 +673,6 @@ TEST(TestWorld, copyWorld) // TODO: this needs either a rewrite or to be removed ASSERT_EQ(copied_world.get_locations()[2].get_cells().size(), world.get_locations()[2].get_cells().size()); ASSERT_EQ(copied_world.get_locations()[3].get_cells().size(), world.get_locations()[2].get_cells().size()); ASSERT_EQ(copied_world.get_locations()[4].get_cells().size(), world.get_locations()[2].get_cells().size()); - // TODO: cells are broken in World copy ctor - // ASSERT_EQ(copied_world.get_locations()[1].get_cells()[0].m_persons.size(), - // world.get_locations()[1].get_cells()[0].m_persons.size()); - // ASSERT_EQ(copied_world.get_locations()[2].get_cells()[0].m_persons.size(), - // world.get_locations()[2].get_cells()[0].m_persons.size()); - // ASSERT_EQ(copied_world.get_locations()[3].get_cells()[0].m_persons.size(), - // world.get_locations()[3].get_cells()[0].m_persons.size()); - // ASSERT_EQ(copied_world.get_locations()[4].get_cells()[0].m_persons.size(), - // world.get_locations()[4].get_cells()[0].m_persons.size()); - // ASSERT_EQ(copied_world.get_locations()[1].get_cells()[0].m_persons[0], - // world.get_locations()[1].get_cells()[0].m_persons[0]); - // ASSERT_EQ(copied_world.get_locations()[2].get_cells()[0].m_persons[0], - // world.get_locations()[2].get_cells()[0].m_persons[0]); ASSERT_EQ(copied_world.get_persons().size(), world.get_persons().size()); ASSERT_EQ(copied_world.get_location(world.get_persons()[0].get_person_id()).get_index(), @@ -724,10 +708,10 @@ TEST(TestWorld, copyWorld) // TODO: this needs either a rewrite or to be removed ASSERT_NE(&copied_world.get_locations()[4].get_cells(), &world.get_locations()[4].get_cells()); ASSERT_NE(&(copied_world.get_locations()[1].get_cells()[0]), &(world.get_locations()[1].get_cells()[0])); ASSERT_NE(&(copied_world.get_locations()[2].get_cells()[0]), &(world.get_locations()[2].get_cells()[0])); - ASSERT_NE(&(copied_world.get_locations()[1].get_cells()[0].m_persons[0]), - &(world.get_locations()[1].get_cells()[0].m_persons[0])); - ASSERT_NE(&(copied_world.get_locations()[2].get_cells()[0].m_persons[0]), - &(world.get_locations()[2].get_cells()[0].m_persons[0])); + // ASSERT_NE(&(copied_world.get_locations()[1].get_cells()[0].m_persons[0]), + // &(world.get_locations()[1].get_cells()[0].m_persons[0])); + // ASSERT_NE(&(copied_world.get_locations()[2].get_cells()[0].m_persons[0]), + // &(world.get_locations()[2].get_cells()[0].m_persons[0])); ASSERT_NE(&copied_world.get_persons()[0], &world.get_persons()[0]); ASSERT_NE(&copied_world.get_persons()[1], &world.get_persons()[1]); From c7b634343de662f840ef91d529363b0fe1be9e1a Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:14:52 +0100 Subject: [PATCH 12/54] remove unnecessary includes --- cpp/models/abm/functions.cpp | 5 ----- cpp/models/abm/location.cpp | 1 - cpp/models/abm/location.h | 2 -- cpp/models/abm/parameters.h | 2 -- cpp/models/abm/world.h | 24 +++++++++++++++++------- cpp/tests/test_abm_location.cpp | 1 - cpp/tests/test_abm_migration_rules.cpp | 1 - 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index bf2a775f3e..c0e99d650d 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -4,14 +4,9 @@ #include "abm/person.h" #include "abm/random_events.h" #include "abm/infection.h" -#include "abm/trip_list.h" #include "abm/virus_variant.h" #include "memilio/epidemiology/age_group.h" -#include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/logging.h" -#include -#include -#include namespace mio { diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index ec572a759f..d36512e7c2 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -21,7 +21,6 @@ #include "abm/location.h" #include "abm/parameters.h" #include "abm/random_events.h" -#include "abm/infection.h" namespace mio { diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 7d28a2c781..3dd716646d 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -24,10 +24,8 @@ #include "abm/mask_type.h" #include "abm/parameters.h" #include "abm/location_type.h" -#include "abm/infection_state.h" #include "memilio/epidemiology/age_group.h" #include "memilio/utils/custom_index_array.h" -#include "memilio/utils/memory.h" namespace mio { diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index d9149daa30..3baaef46d5 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -26,13 +26,11 @@ #include "abm/vaccine.h" #include "memilio/utils/custom_index_array.h" #include "memilio/utils/uncertain_value.h" -#include "memilio/math/eigen.h" #include "memilio/utils/parameter_set.h" #include "memilio/epidemiology/age_group.h" #include "memilio/epidemiology/damping.h" #include "memilio/epidemiology/contact_matrix.h" #include -#include namespace mio { diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 2d592f54f5..b31a597b79 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -31,9 +31,7 @@ #include "abm/random_events.h" #include "abm/testing_strategy.h" #include "abm/virus_variant.h" -#include "memilio/config.h" #include "memilio/epidemiology/age_group.h" -#include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/custom_index_array.h" #include "memilio/utils/index.h" #include "memilio/utils/random_number_generator.h" @@ -42,11 +40,9 @@ #include "functions.h" #include -#include -#include -#include -#include #include +#include +#include namespace mio { @@ -215,7 +211,7 @@ class World * @param[in] id LocationId of the Location. * @return Reference to the Location. */ - const Location& get_individualized_location(LocationId id) const; + const Location& get_individualized_location(LocationId id) const; // TODO: replace by get_location? Location& get_individualized_location(LocationId id); @@ -401,6 +397,14 @@ class World global_parameters, personal_rng); } + // get location by id + const Location& get_location(LocationId id) const + { + assert(id.index != INVALID_LOCATION_INDEX); + assert(id.index < m_locations.size()); + return m_locations[id.index]; + } + // get location by id Location& get_location(LocationId id) { @@ -415,6 +419,12 @@ class World return get_location(get_person(id).get_location()); } + // get current location of the Person + inline const Location& get_location(PersonId id) const + { + return get_location(get_person(id).get_location()); + } + private: /** * @brief Person%s interact at their Location and may become infected. diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index b046797279..177e8a7e88 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -19,7 +19,6 @@ */ #include "abm/functions.h" -#include "abm/infection.h" #include "abm/parameters.h" #include "abm/person.h" #include "abm/world.h" diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 9a2f852e6f..c8a62d34b4 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -19,7 +19,6 @@ */ #include "abm/functions.h" #include "abm/person.h" -#include "abm/world.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" From 17cc8897467953b9d0c3e40f276ff7d6f75284f7 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:02:16 +0100 Subject: [PATCH 13/54] extract Person::RandomNumberGenerator from Person, renamed to PersonalRandomNumber. make PersonId a TypeSafe, move it to its own header. --- cpp/benchmarks/abm.cpp | 10 +-- cpp/examples/abm_history_object.cpp | 2 +- cpp/examples/abm_minimal.cpp | 6 +- cpp/models/abm/CMakeLists.txt | 3 + cpp/models/abm/common_abm_loggers.h | 5 +- cpp/models/abm/functions.cpp | 16 ++-- cpp/models/abm/functions.h | 10 ++- cpp/models/abm/infection.cpp | 8 +- cpp/models/abm/infection.h | 18 ++--- cpp/models/abm/location.cpp | 2 +- cpp/models/abm/location.h | 12 +-- cpp/models/abm/migration_rules.cpp | 20 ++--- cpp/models/abm/migration_rules.h | 22 +++--- cpp/models/abm/person.cpp | 10 ++- cpp/models/abm/person.h | 97 +++---------------------- cpp/models/abm/person_id.h | 31 ++++++++ cpp/models/abm/personal_rng.cpp | 22 ++++++ cpp/models/abm/personal_rng.h | 80 ++++++++++++++++++++ cpp/models/abm/testing_strategy.cpp | 4 +- cpp/models/abm/testing_strategy.h | 8 +- cpp/models/abm/trip_list.cpp | 3 - cpp/models/abm/trip_list.h | 18 ++--- cpp/models/abm/world.cpp | 10 +-- cpp/models/abm/world.h | 34 +++++---- cpp/simulations/abm.cpp | 4 +- cpp/simulations/abm_braunschweig.cpp | 10 +-- cpp/tests/abm_helpers.cpp | 2 +- cpp/tests/test_abm_infection.cpp | 6 +- cpp/tests/test_abm_location.cpp | 6 +- cpp/tests/test_abm_lockdown_rules.cpp | 32 ++++---- cpp/tests/test_abm_masks.cpp | 4 +- cpp/tests/test_abm_migration_rules.cpp | 60 +++++++-------- cpp/tests/test_abm_person.cpp | 29 ++++---- cpp/tests/test_abm_testing_strategy.cpp | 8 +- cpp/tests/test_abm_world.cpp | 4 +- 35 files changed, 338 insertions(+), 278 deletions(-) create mode 100644 cpp/models/abm/person_id.h create mode 100644 cpp/models/abm/personal_rng.cpp create mode 100644 cpp/models/abm/personal_rng.h diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index e5d7c75216..309aad32d0 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -1,5 +1,5 @@ #include "abm/simulation.h" -#include "memilio/utils/stl_util.h" + #include "benchmark/benchmark.h" mio::abm::Simulation make_simulation(size_t num_persons, std::initializer_list seeds) @@ -23,10 +23,10 @@ mio::abm::Simulation make_simulation(size_t num_persons, std::initializer_list::get_instance()( + auto age = mio::AgeGroup(mio::UniformIntDistribution::get_instance()( world.get_rng(), size_t(0), world.parameters.get_num_groups() - 1)); - auto& person = world.add_person(home, age); - person.set_assigned_location(home); + auto person = world.add_person(home, age); + world.get_person(person).set_assigned_location(home); home_size++; } @@ -49,7 +49,7 @@ mio::abm::Simulation make_simulation(size_t num_persons, std::initializer_list::get_instance()(prng, 0.0, 1.0) < pct_infected) { diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index dd3b1224ad..d310e1fd55 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -135,7 +135,7 @@ int main() // The infection states are chosen randomly. auto persons = world.get_persons(); for (auto& person : persons) { - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(world.get_rng(), person); mio::abm::InfectionState infection_state = (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); if (infection_state != mio::abm::InfectionState::Susceptible) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index e42b48b651..e53fc1dd08 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -41,10 +41,10 @@ int main() world.parameters.get() = 4.; // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) - world.parameters.get() = false; + world.parameters.get() = false; world.parameters.get()[age_group_5_to_14] = true; // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 and 35-59) - world.parameters.get() = false; + world.parameters.get() = false; world.parameters.get()[age_group_15_to_34] = true; world.parameters.get()[age_group_35_to_59] = true; @@ -122,7 +122,7 @@ int main() for (auto& person : world.get_persons()) { mio::abm::InfectionState infection_state = mio::abm::InfectionState( mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), infection_distribution)); - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(world.get_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), world.parameters, start_date, infection_state)); diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index 59de1e7a5c..e8cc12348e 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -1,6 +1,9 @@ add_library(abm functions.cpp functions.h + person_id.h + personal_rng.cpp + personal_rng.h location.cpp location.h household.cpp diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 438ae0948b..145ce9fdab 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -22,6 +22,7 @@ #define ABM_COMMON_LOGGERS_H #include "abm/infection_state.h" +#include "abm/person_id.h" #include "abm/simulation.h" #include "memilio/io/history.h" #include "memilio/utils/time_series.h" @@ -110,7 +111,7 @@ struct LogLocationInformation : mio::LogOnce { * @brief Logger to log the Person%s Information in the simulation. */ struct LogPersonInformation : mio::LogOnce { - using Type = std::vector>; + using Type = std::vector>; /** * @brief Log the LocationInformation of the simulation. * @param[in] sim The simulation of the abm. @@ -135,7 +136,7 @@ struct LogPersonInformation : mio::LogOnce { * @brief Logger to log Movement Data of the agents in the simulation. */ struct LogDataForMovement : mio::LogAlways { - using Type = std::vector>; /** * @brief Log the Movement Data of the agents in the simulation. diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index c0e99d650d..7c5ad43d06 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -16,6 +16,8 @@ namespace abm // TODO: on argument order: maybe personal_rng first, as it always(?) is a non-const reference +// TODO: daily_transmissions functions are only used in interact. expose in header anyways? + ScalarType daily_transmissions_by_contacts(const Location::ContactExposureRates& rates, CellIndex cell_index, VirusVariant virus, AgeGroup age_receiver, const LocalInfectionParameters& params) @@ -37,7 +39,7 @@ ScalarType daily_transmissions_by_air(const Location::AirExposureRates& rates, C void interact(Person& person, const Location& location, const Location::AirExposureRates& local_air_exposure, const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng) + const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng) { // make sure all dimensions are set correctly and all indices are valid assert(location.get_cells().size() == local_air_exposure.size().get()); @@ -85,11 +87,11 @@ void interact(Person& person, const Location& location, const Location::AirExpos void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, Location::ContactExposureRates& local_contact_exposure, const Person& person, - Location& location, TimePoint t, TimeSpan dt) -{ // TODO: location should be const, but there is no const cell accessor + const Location& location, TimePoint t, TimeSpan dt) +{ assert([&]() { if (person.get_location() != location.get_id()) { - mio::log_warning("Person with id {} is not at Location with id {}", person.get_person_id(), + mio::log_warning("Person with id {} is not at Location with id {}", person.get_person_id().get(), location.get_index()); } return true; @@ -113,9 +115,9 @@ void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, } } -void interact(Person& person, Location& location, const std::vector& local_population, const TimePoint t, - const TimeSpan dt, const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng) -{ // TODO: make location const&, need to fix add_exposure_contribution +void interact(Person& person, const Location& location, const std::vector& local_population, const TimePoint t, + const TimeSpan dt, const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng) +{ Location::AirExposureRates local_air_exposure{{CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.}; Location::ContactExposureRates local_contact_exposure{ {CellIndex(location.get_cells().size()), VirusVariant::Count, AgeGroup(global_parameters.get_num_groups())}, diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h index a9ff97a3f9..4e0a1433c9 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/functions.h @@ -13,17 +13,19 @@ namespace mio namespace abm { +// add the contribution of person to the exposure rates at location void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, Location::ContactExposureRates& local_contact_exposure, const Person& person, - Location& location, TimePoint t, TimeSpan dt); + const Location& location, TimePoint t, TimeSpan dt); // let a person interact with a location for and at some time void interact(Person& person, const Location& location, const Location::AirExposureRates& local_air_exposure, const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng); + const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng); -void interact(Person& person, Location& location, const std::vector& local_population, const TimePoint t, - const TimeSpan dt, const Parameters& global_parameters, Person::RandomNumberGenerator& personal_rng); +// interact, but it computes the correct exposures for you +void interact(Person& person, const Location& location, const std::vector& local_population, const TimePoint t, + const TimeSpan dt, const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng); // move a person to another location. returns false if the person was at the target location already, true otherwise bool migrate(Person& person, const Location& destination, const std::vector& cells = {0}, diff --git a/cpp/models/abm/infection.cpp b/cpp/models/abm/infection.cpp index a6c9ffff71..b232dd7747 100644 --- a/cpp/models/abm/infection.cpp +++ b/cpp/models/abm/infection.cpp @@ -26,7 +26,7 @@ namespace mio namespace abm { -Infection::Infection(Person::RandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, +Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState init_state, std::pair latest_exposure, bool detected) : m_virus_variant(virus) @@ -112,7 +112,7 @@ TimePoint Infection::get_start_date() const return m_viral_load.start_date; } -TimePoint Infection::draw_infection_course(Person::RandomNumberGenerator& rng, AgeGroup age, const Parameters& params, +TimePoint Infection::draw_infection_course(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState init_state, std::pair latest_protection) { @@ -122,7 +122,7 @@ TimePoint Infection::draw_infection_course(Person::RandomNumberGenerator& rng, A return start_date; } -void Infection::draw_infection_course_forward(Person::RandomNumberGenerator& rng, AgeGroup age, +void Infection::draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState start_state, std::pair latest_exposure) { @@ -210,7 +210,7 @@ void Infection::draw_infection_course_forward(Person::RandomNumberGenerator& rng } } -TimePoint Infection::draw_infection_course_backward(Person::RandomNumberGenerator& rng, AgeGroup age, +TimePoint Infection::draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState init_state) { diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index f0f1b1ecba..19a86343ef 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -20,11 +20,11 @@ #ifndef MIO_ABM_INFECTION_H #define MIO_ABM_INFECTION_H +#include "abm/personal_rng.h" #include "abm/time.h" #include "abm/infection_state.h" #include "abm/virus_variant.h" #include "abm/parameters.h" -#include "abm/person.h" #include @@ -53,7 +53,7 @@ class Infection /** * @brief Create an Infection for a single Person. * Draws a random infection course. - * @param[inout] rng Person::RandomNumberGenerator for the Person. + * @param[inout] rng PersonalRandomNumberGenerator for the Person. * @param[in] virus Virus type of the Infection. * @param[in] age AgeGroup to determine the ViralLoad course. * @param[in] params Parameters of the Model. @@ -62,7 +62,7 @@ class Infection * @param[in] latest_exposure [Default: {ExposureType::NoProtection, TimePoint(0)}] The pair value of last ExposureType (previous Infection/Vaccination) and TimePoint of that protection. * @param[in] detected [Default: false] If the Infection is detected. */ - Infection(Person::RandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, + Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, TimePoint start_date, InfectionState start_state = InfectionState::Exposed, std::pair latest_exposure = {ExposureType::NoProtection, TimePoint(0)}, bool detected = false); @@ -119,39 +119,39 @@ class Infection * @brief Determine ViralLoad course and Infection course based on init_state. * Calls draw_infection_course_backward for all #InfectionState%s prior and draw_infection_course_forward for all * subsequent #InfectionState%s. - * @param[inout] rng Person::RandomNumberGenerator of the Person. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] age AgeGroup of the Person. * @param[in] params Parameters of the Model. * @param[in] init_date Date of initializing the Infection. * @param[in] init_state #InfectionState at time of initializing the Infection. * @return The starting date of the Infection. */ - TimePoint draw_infection_course(Person::RandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + TimePoint draw_infection_course(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState start_state, std::pair latest_protection); /** * @brief Determine ViralLoad course and Infection course prior to the given start_state. - * @param[inout] rng Person::RandomNumberGenerator of the Person. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] age AgeGroup of the Person. * @param[in] params Parameters of the Model. * @param[in] init_date Date of initializing the Infection. * @param[in] init_state #InfectionState at time of initializing the Infection. */ - void draw_infection_course_forward(Person::RandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + void draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState start_state, std::pair latest_protection); /** * @brief Determine ViralLoad course and Infection course subsequent to the given start_state. - * @param[inout] rng Person::RandomNumberGenerator of the Person. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] age AgeGroup of the person. * @param[in] params Parameters of the Model. * @param[in] init_date Date of initializing the Infection. * @param[in] init_state InfectionState at time of initializing the Infection. * @return The starting date of the Infection. */ - TimePoint draw_infection_course_backward(Person::RandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + TimePoint draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState init_state); std::vector> m_infection_course; ///< Start date of each #InfectionState. diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index d36512e7c2..66e8ceaabb 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -46,7 +46,7 @@ Location Location::copy_location_without_persons(size_t) const For every cell in a location we have a transmission factor that is nomalized to m_capacity.volume / m_capacity.persons of the location "Home", which is 66. We multiply this rate with the individual size of each cell to obtain a "space per person" factor. */ -ScalarType Cell::compute_space_per_person_relative() +ScalarType Cell::compute_space_per_person_relative() const { if (m_capacity.volume != 0) { return 66.0 / m_capacity.volume; diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 3dd716646d..c8604b6693 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -20,7 +20,6 @@ #ifndef MIO_ABM_LOCATION_H #define MIO_ABM_LOCATION_H -#include "abm/person.h" #include "abm/mask_type.h" #include "abm/parameters.h" #include "abm/location_type.h" @@ -31,7 +30,6 @@ namespace mio { namespace abm { -class Person; struct CellIndex : public mio::Index { CellIndex(size_t i) @@ -66,7 +64,7 @@ struct Cell { * @brief Computes a relative cell size for the Cell. * @return The relative cell size for the Cell. */ - ScalarType compute_space_per_person_relative(); + ScalarType compute_space_per_person_relative() const; /** * @brief Get subpopulation of a particular #InfectionState in the Cell. @@ -206,12 +204,6 @@ class Location return m_cells; } - // TODO: remove and/or refactor - std::vector& get_cells() - { - return m_cells; - } - /** * @brief Get the type of Mask that is demanded when entering this Location. * @return Least secure MaskType that is demanded when entering this Location. @@ -238,6 +230,7 @@ class Location */ void set_capacity(uint32_t persons, uint32_t volume, uint32_t cell_idx = 0) { + assert(cell_idx < m_cells.size()); m_cells[cell_idx].m_capacity.persons = persons; m_cells[cell_idx].m_capacity.volume = volume; } @@ -249,6 +242,7 @@ class Location */ CellCapacity get_capacity(uint32_t cell_idx = 0) const { + assert(cell_idx < m_cells.size()); return m_cells[cell_idx].m_capacity; } diff --git a/cpp/models/abm/migration_rules.cpp b/cpp/models/abm/migration_rules.cpp index ab7d4cc6d1..9212dfa7f8 100644 --- a/cpp/models/abm/migration_rules.cpp +++ b/cpp/models/abm/migration_rules.cpp @@ -32,7 +32,7 @@ namespace mio namespace abm { -LocationType random_migration(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +LocationType random_migration(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { auto current_loc = person.get_location().get_type(); @@ -48,7 +48,7 @@ LocationType random_migration(Person::RandomNumberGenerator& rng, const Person& return current_loc; } -LocationType go_to_school(Person::RandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, +LocationType go_to_school(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { auto current_loc = person.get_location().get_type(); @@ -67,7 +67,7 @@ LocationType go_to_school(Person::RandomNumberGenerator& /*rng*/, const Person& return current_loc; } -LocationType go_to_work(Person::RandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, +LocationType go_to_work(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { auto current_loc = person.get_location().get_type(); @@ -86,7 +86,7 @@ LocationType go_to_work(Person::RandomNumberGenerator& /*rng*/, const Person& pe return current_loc; } -LocationType go_to_shop(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +LocationType go_to_shop(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { auto current_loc = person.get_location().get_type(); @@ -105,7 +105,7 @@ LocationType go_to_shop(Person::RandomNumberGenerator& rng, const Person& person return current_loc; } -LocationType go_to_event(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +LocationType go_to_event(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { auto current_loc = person.get_location().get_type(); @@ -127,7 +127,7 @@ LocationType go_to_event(Person::RandomNumberGenerator& rng, const Person& perso return current_loc; } -LocationType go_to_quarantine(Person::RandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, +LocationType go_to_quarantine(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan /*dt*/, const Parameters& params) { auto current_loc = person.get_location().get_type(); @@ -138,7 +138,7 @@ LocationType go_to_quarantine(Person::RandomNumberGenerator& /*rng*/, const Pers return current_loc; } -LocationType go_to_hospital(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, +LocationType go_to_hospital(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { auto current_loc = person.get_location().get_type(); @@ -148,7 +148,7 @@ LocationType go_to_hospital(Person::RandomNumberGenerator& /*rng*/, const Person return current_loc; } -LocationType go_to_icu(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, +LocationType go_to_icu(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { auto current_loc = person.get_location().get_type(); @@ -158,7 +158,7 @@ LocationType go_to_icu(Person::RandomNumberGenerator& /*rng*/, const Person& per return current_loc; } -LocationType return_home_when_recovered(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, +LocationType return_home_when_recovered(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { auto current_loc = person.get_location().get_type(); @@ -169,7 +169,7 @@ LocationType return_home_when_recovered(Person::RandomNumberGenerator& /*rng*/, return current_loc; } -LocationType get_buried(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, +LocationType get_buried(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { auto current_loc = person.get_location().get_type(); diff --git a/cpp/models/abm/migration_rules.h b/cpp/models/abm/migration_rules.h index 3384e774f5..b890e096d8 100644 --- a/cpp/models/abm/migration_rules.h +++ b/cpp/models/abm/migration_rules.h @@ -32,7 +32,7 @@ namespace abm /** * @name Rules for migration between Location%s. - * @param[inout] rng Person::RandomNumberGenerator for the person. + * @param[inout] rng PersonalRandomNumberGenerator for the person. * @param[in] p Person the rule is applied to. * @param[in] t Current time. * @param[in] dt Length of the time step. @@ -45,61 +45,61 @@ namespace abm /** * @brief Completely random migration to any other Location. */ -LocationType random_migration(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +LocationType random_migration(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(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +LocationType 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(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +LocationType 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(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +LocationType go_to_event(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(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +LocationType 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(Person::RandomNumberGenerator& rng, const Person& person, TimePoint /*t*/, +LocationType 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(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +LocationType 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(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, +LocationType 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(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, +LocationType 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(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, +LocationType get_buried(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params); /**@}*/ diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index abb7977614..1d4cfa8ff5 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -102,6 +102,12 @@ void Person::set_location(const Location& location) set_location(location.get_id()); } +void Person::set_location(LocationId id) +{ + m_location = id; + m_time_at_location = TimeSpan(0); +} + const Infection& Person::get_infection() const { return m_infections.back(); @@ -164,7 +170,7 @@ void Person::remove_quarantine() m_quarantine_start = TimePoint(-(std::numeric_limits::max() / 2)); } -bool Person::get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParameters& params) +bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const TestParameters& params) { ScalarType random = UniformDistribution::get_instance()(rng); m_time_of_last_test = t; @@ -218,7 +224,7 @@ ScalarType Person::get_mask_protective_factor(const Parameters& params) const } } -bool Person::apply_mask_intervention(RandomNumberGenerator& rng, const Location& target) +bool Person::apply_mask_intervention(PersonalRandomNumberGenerator& rng, const Location& target) { if (target.get_npi_active() == false) { m_wears_mask = false; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 158ab77675..9c674f4fae 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -20,9 +20,12 @@ #ifndef MIO_ABM_PERSON_H #define MIO_ABM_PERSON_H -#include "abm/location_type.h" +#include "abm/infection.h" #include "abm/infection_state.h" +#include "abm/location_type.h" #include "abm/parameters.h" +#include "abm/person_id.h" +#include "abm/personal_rng.h" #include "abm/time.h" #include "abm/vaccine.h" #include "abm/mask.h" @@ -35,13 +38,8 @@ namespace mio namespace abm { -using PersonId = uint32_t; // TODO: use type safe - struct LocationId; -class Location; -class Infection; - -static constexpr PersonId INVALID_PERSON_ID = std::numeric_limits::max(); +class Location; // TODO: try to remove. include directly, or use id only /** * @brief Agents in the simulated World that can carry and spread the Infection. @@ -49,77 +47,6 @@ static constexpr PersonId INVALID_PERSON_ID = std::numeric_limits::max class Person { public: - /** - * Random number generator of individual persons. - * Increments the random number generator counter of the person when used. - * Does not store its own key or counter. - * Instead the key needs to be provided from the outside, so that the RNG - * for all persons share the same key. - * The counter is taken from the person. - * Person::RandomNumberGenerator is cheap to construct and transparent - * for the compiler to optimize, so we don't store the RNG persistently, only the - * counter, so we don't need to store the key in each person. This increases - * consistency (if the key is changed after the person is created) and - * reduces the memory required per person. - * @see mio::RandomNumberGeneratorBase - */ - class RandomNumberGenerator : public RandomNumberGeneratorBase - { - public: - /** - * Creates a RandomNumberGenerator for a person. - * @param key Key to be used by the generator. - * @param id Id of the Person. - * @param counter Reference to the Person's RNG Counter. - */ - RandomNumberGenerator(Key key, PersonId id, Counter& counter) - : m_key(key) - , m_person_id(id) - , m_counter(counter) - { - } - - /** - * Creates a RandomNumberGenerator for a person. - * Uses the same key as another RandomNumberGenerator. - * @param rng RandomNumberGenerator who's key will be used. - * @param person Reference to the Person who's counter will be used. - */ - RandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) - : RandomNumberGenerator(rng.get_key(), person.get_person_id(), person.get_rng_counter()) - { - } - - /** - * @return Get the key. - */ - Key get_key() const - { - return m_key; - } - - /** - * @return Get the current counter. - */ - Counter get_counter() const - { - return rng_totalsequence_counter(m_person_id, m_counter); - } - - /** - * Increment the counter. - */ - void increment_counter() - { - ++m_counter; - } - - private: - Key m_key; ///< Global RNG Key - PersonId m_person_id; ///< Id of the Person - Counter& m_counter; ///< Reference to the Person's rng counter - }; - /** * @brief Create a Person. * @param[in, out] rng RandomNumberGenerator. @@ -128,7 +55,7 @@ class Person * @param[in] person_id Index of the Person. */ explicit Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, - PersonId person_id = INVALID_PERSON_ID); + PersonId person_id = PersonId::invalid_id()); explicit Person(const Person& other, PersonId id); @@ -208,11 +135,7 @@ class Person void set_location(const Location& location); // set new location, e.g. when migrating - void set_location(LocationId id) - { - m_location = id; - m_time_at_location = TimeSpan(0); - } + void set_location(LocationId id); /** * @brief Get the time the Person has been at its current Location. @@ -332,7 +255,7 @@ class Person * @param[in] params Sensitivity and specificity of the test method. * @return True if the test result of the Person is positive. */ - bool get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParameters& params); + bool get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const TestParameters& params); /** * @brief Get the PersonId of the Person. @@ -399,7 +322,7 @@ class Person * @param[in] target The target Location. * @return Whether a Person wears a Mask at the Location. */ - bool apply_mask_intervention(RandomNumberGenerator& rng, const Location& target); + bool apply_mask_intervention(PersonalRandomNumberGenerator& rng, const Location& target); /** * @brief Decide if a Person is currently wearing a Mask. @@ -454,7 +377,7 @@ class Person /** * Get this persons RandomNumberGenerator counter. - * @see mio::abm::Person::RandomNumberGenerator. + * @see mio::abm::PersonalRandomNumberGenerator. */ Counter& get_rng_counter() { diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h new file mode 100644 index 0000000000..1f947b8c47 --- /dev/null +++ b/cpp/models/abm/person_id.h @@ -0,0 +1,31 @@ +#ifndef MIO_ABM_PERSON_ID_H_ +#define MIO_ABM_PERSON_ID_H_ + +#include "memilio/utils/type_safe.h" +#include + +namespace mio +{ +namespace abm +{ + +struct PersonId : mio::TypeSafe, public OperatorComparison { + PersonId(uint32_t id) + : mio::TypeSafe(id) + { + } + PersonId() + : mio::TypeSafe(std::numeric_limits::max()) + { + } + + const static PersonId invalid_id() + { + return PersonId(); + } +}; + +} // namespace abm +} // namespace mio + +#endif // MIO_ABM_PERSON_ID_H_ \ No newline at end of file diff --git a/cpp/models/abm/personal_rng.cpp b/cpp/models/abm/personal_rng.cpp new file mode 100644 index 0000000000..37e93b5ca2 --- /dev/null +++ b/cpp/models/abm/personal_rng.cpp @@ -0,0 +1,22 @@ +#include "abm/personal_rng.h" +#include "abm/person.h" + +namespace mio +{ +namespace abm +{ +PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key key, PersonId id, + mio::Counter& counter) + : m_key(key) + , m_person_id(id) + , m_counter(counter) +{ +} + +PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) + : PersonalRandomNumberGenerator(rng.get_key(), person.get_person_id(), person.get_rng_counter()) +{ +} + +} // namespace abm +} // namespace mio \ No newline at end of file diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h new file mode 100644 index 0000000000..8bde28cdc8 --- /dev/null +++ b/cpp/models/abm/personal_rng.h @@ -0,0 +1,80 @@ +#ifndef MIO_ABM_PERSONAL_RNG_H_ +#define MIO_ABM_PERSONAL_RNG_H_ + +#include "memilio/utils/random_number_generator.h" +#include "abm/person_id.h" + +namespace mio +{ +namespace abm +{ + +class Person; + +/** + * Random number generator of individual persons. + * Increments the random number generator counter of the person when used. + * Does not store its own key or counter. + * Instead the key needs to be provided from the outside, so that the RNG + * for all persons share the same key. + * The counter is taken from the person. + * PersonalRandomNumberGenerator is cheap to construct and transparent + * for the compiler to optimize, so we don't store the RNG persistently, only the + * counter, so we don't need to store the key in each person. This increases + * consistency (if the key is changed after the person is created) and + * reduces the memory required per person. + * @see mio::RandomNumberGeneratorBase + */ +class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase +{ +public: + /** + * Creates a RandomNumberGenerator for a person. + * @param key Key to be used by the generator. + * @param id Id of the Person. + * @param counter Reference to the Person's RNG Counter. + */ + PersonalRandomNumberGenerator(mio::Key key, PersonId id, mio::Counter& counter); + + /** + * Creates a RandomNumberGenerator for a person. + * Uses the same key as another RandomNumberGenerator. + * @param rng RandomNumberGenerator who's key will be used. + * @param person Reference to the Person who's counter will be used. + */ + PersonalRandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person); + + /** + * @return Get the key. + */ + mio::Key get_key() const + { + return m_key; + } + + /** + * @return Get the current counter. + */ + mio::Counter get_counter() const + { + return mio::rng_totalsequence_counter(m_person_id.get(), m_counter); + } + + /** + * Increment the counter. + */ + void increment_counter() + { + ++m_counter; + } + +private: + mio::Key m_key; ///< Global RNG Key + PersonId m_person_id; ///< Id of the Person + mio::Counter& m_counter; ///< Reference to the Person's rng counter +}; + +} // namespace abm +} // namespace mio + +#endif // MIO_ABM_PERSONAL_RNG_H_ \ No newline at end of file diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index 9e9dff039d..63c67912df 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -100,7 +100,7 @@ void TestingScheme::update_activity_status(TimePoint t) m_is_active = (m_start_date <= t && t <= m_end_date); } -bool TestingScheme::run_scheme(Person::RandomNumberGenerator& rng, Person& person, TimePoint t) const +bool TestingScheme::run_scheme(PersonalRandomNumberGenerator& rng, Person& person, TimePoint t) const { if (t - person.get_time_of_last_test() > m_minimal_time_since_last_test) { if (m_testing_criteria.evaluate(person, t)) { @@ -165,7 +165,7 @@ void TestingStrategy::update_activity_status(TimePoint t) } } -bool TestingStrategy::run_strategy(Person::RandomNumberGenerator& rng, Person& person, const Location& location, +bool TestingStrategy::run_strategy(PersonalRandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t) { // A Person is always allowed to go home and this is never called if a person is not discharged from a hospital or ICU. diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 4374b7fa5b..d54d273f0d 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -136,12 +136,12 @@ class TestingScheme /** * @brief Runs the TestingScheme and potentially tests a Person. - * @param[inout] rng Person::RandomNumberGenerator for the Person being tested. + * @param[inout] rng PersonalRandomNumberGenerator for the Person being tested. * @param[in] person Person to check. * @param[in] t TimePoint when to run the scheme. * @return If the person is allowed to enter the Location by the scheme. */ - bool run_scheme(Person::RandomNumberGenerator& rng, Person& person, TimePoint t) const; + bool run_scheme(PersonalRandomNumberGenerator& rng, Person& person, TimePoint t) const; private: TestingCriteria m_testing_criteria; ///< TestingCriteria of the scheme. @@ -213,13 +213,13 @@ class TestingStrategy /** * @brief Runs the TestingStrategy and potentially tests a Person. - * @param[inout] rng Person::RandomNumberGenerator for the Person being tested. + * @param[inout] rng PersonalRandomNumberGenerator for the Person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the strategy. * @return If the Person is allowed to enter the Location. */ - bool run_strategy(Person::RandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t); + bool run_strategy(PersonalRandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t); private: std::vector>> diff --git a/cpp/models/abm/trip_list.cpp b/cpp/models/abm/trip_list.cpp index 88ef819de4..af8bc806dd 100644 --- a/cpp/models/abm/trip_list.cpp +++ b/cpp/models/abm/trip_list.cpp @@ -18,11 +18,8 @@ * limitations under the License. */ #include "abm/trip_list.h" -#include "abm/location.h" #include "abm/random_events.h" -#include - namespace mio { namespace abm diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index 832d21e6e6..639df7c0e7 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -20,14 +20,8 @@ #ifndef MIO_ABM_TRIP_LIST_H #define MIO_ABM_TRIP_LIST_H -#include "abm/parameters.h" -#include "abm/location.h" -#include "abm/infection.h" #include "abm/location_type.h" - -#include "memilio/math/eigen.h" -#include -#include +#include "abm/person.h" namespace mio { @@ -38,7 +32,7 @@ namespace abm * @brief A trip describes a migration from one Location to another Location. */ struct Trip { - uint32_t person_id; /**< Person that makes the trip and corresponds to the index into the structure m_persons from + PersonId person_id; /**< Person that makes the trip and corresponds to the index into the structure m_persons from World, where all Person%s are saved.*/ TimePoint time; ///< Time at which a Person changes the Location. LocationId migration_destination; ///< Location where the Person migrates to. @@ -58,7 +52,7 @@ struct Trip { * @param[in] origin Location where the person starts the Trip. * @param[in] input_cells The index of the Cell%s the Person migrates to. */ - Trip(uint32_t id, TimePoint time_new, LocationId destination, LocationId origin, TransportMode mode_of_transport, + Trip(PersonId id, TimePoint time_new, LocationId destination, LocationId origin, TransportMode mode_of_transport, ActivityType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) @@ -70,13 +64,13 @@ struct Trip { { } - Trip(uint32_t id, TimePoint time_new, LocationId destination, const std::vector& input_cells = {}) + Trip(PersonId id, TimePoint time_new, LocationId destination, const std::vector& input_cells = {}) : Trip(id, time_new, destination, destination, mio::abm::TransportMode::Unknown, mio::abm::ActivityType::UnknownActivity, input_cells) { } - Trip(uint32_t id, TimePoint time_new, LocationId destination, LocationId origin, + Trip(PersonId id, TimePoint time_new, LocationId destination, LocationId origin, const std::vector& input_cells = {}) : Trip(id, time_new, destination, origin, mio::abm::TransportMode::Unknown, mio::abm::ActivityType::UnknownActivity, input_cells) @@ -116,7 +110,7 @@ struct Trip { static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("Trip"); - auto person_id = obj.expect_element("person_id", Tag{}); + auto person_id = obj.expect_element("person_id", Tag{}); auto time = obj.expect_element("time", Tag{}); auto destination_index = obj.expect_element("destination_index", Tag{}); auto destination_type = obj.expect_element("destination_type", Tag{}); diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 5597319e35..ef55b3d3a9 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -62,7 +62,7 @@ void World::interaction(TimePoint t, TimeSpan dt) PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { // TODO: i == get_person(i).get_person_id(), but this does not have to stay true - interact(i, t, dt); + interact(m_persons[i].get_person_id(), t, dt); } } @@ -70,8 +70,8 @@ void World::migration(TimePoint t, TimeSpan dt) { PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { - auto&& person = m_persons[i]; - auto personal_rng = Person::RandomNumberGenerator(m_rng, person); + auto& person = m_persons[i]; + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); auto try_migration_rule = [&](auto rule) -> bool { //run migration rule and check if migration can actually happen @@ -122,8 +122,8 @@ void World::migration(TimePoint t, TimeSpan dt) while (m_trip_list.get_current_index() < num_trips && m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { auto& trip = m_trip_list.get_next_trip(weekend); - auto& person = m_persons[trip.person_id]; - auto personal_rng = Person::RandomNumberGenerator(m_rng, person); + auto& person = get_person(trip.person_id); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { auto& target_location = get_individualized_location(trip.migration_destination); if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index b31a597b79..fa22bdd688 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -179,7 +179,7 @@ class World }()); // assert that location is in world assert(person.get_age().get() < parameters.get_num_groups()); - uint32_t new_id = static_cast(m_persons.size()); // TODO: this breaks when removing a person + PersonId new_id = static_cast(m_persons.size()); // TODO: this breaks when removing a person m_persons.emplace_back(person, new_id); auto& new_person = m_persons.back(); new_person.set_assigned_location(m_cemetery_id); @@ -195,6 +195,10 @@ class World * @return A range of all Location%s. */ Range> get_locations() const; + Range> get_locations() + { + return make_range(m_locations.begin(), m_locations.end()); + } /** * @brief Get a range of all Person%s in the World. @@ -319,24 +323,24 @@ class World Person& get_person(PersonId id) { - assert(id == m_persons[id].get_person_id()); - assert((size_t)id < m_persons.size()); - return m_persons[id]; + assert(id == m_persons[id.get()].get_person_id()); + assert(id.get() < m_persons.size()); + return m_persons[id.get()]; } const Person& get_person(PersonId id) const { - assert(id == m_persons[id].get_person_id()); - assert((size_t)id < m_persons.size()); - return m_persons[id]; + assert(id == m_persons[id.get()].get_person_id()); + assert(id.get() < m_persons.size()); + return m_persons[id.get()]; } size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { if (m_local_populations_cache.is_valid()) { return std::count_if(m_local_populations_cache.data.at(location).begin(), - m_local_populations_cache.data.at(location).end(), [&](PersonId p) { - return get_person(p).get_infection_state(t) == state; + m_local_populations_cache.data.at(location).end(), [&](uint32_t p) { + return get_person(PersonId(p)).get_infection_state(t) == state; }); } @@ -372,20 +376,20 @@ class World LocationId origin = get_location(person).get_id(); const bool has_moved = mio::abm::migrate(get_person(person), get_location(destination), cells, mode); if (has_moved && m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(origin).erase(person); - m_local_populations_cache.data.at(destination).emplace(person); + m_local_populations_cache.data.at(origin).erase(person.get()); + m_local_populations_cache.data.at(destination).emplace(person.get()); } } // let a person interact with its current location inline void interact(PersonId person, TimePoint t, TimeSpan dt) { - auto personal_rng = Person::RandomNumberGenerator(m_rng, get_person(person)); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person)); interact(person, t, dt, personal_rng, parameters); } // let a person interact with its current location - inline void interact(PersonId person, TimePoint t, TimeSpan dt, Person::RandomNumberGenerator& personal_rng, + inline void interact(PersonId person, TimePoint t, TimeSpan dt, PersonalRandomNumberGenerator& personal_rng, const Parameters& global_parameters) { if (!m_air_exposure_rates_cache.is_valid() || !m_contact_exposure_rates_cache.is_valid()) { @@ -505,7 +509,7 @@ class World } } - Cache>> + Cache>> m_local_populations_cache; // TODO: change this to storing only (sub)population(s) in numbers, using atomic ints Cache> m_air_exposure_rates_cache; Cache> m_contact_exposure_rates_cache; @@ -517,7 +521,7 @@ class World TestingStrategy m_testing_strategy; ///< List of TestingScheme%s that are checked for testing. TripList m_trip_list; ///< List of all Trip%s the Person%s do. bool m_use_migration_rules; ///< Whether migration rules are considered. - std::vector>> m_migration_rules; ///< Rules that govern the migration between Location%s. diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 20a3240904..6f608ccdce 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -54,7 +54,7 @@ void assign_uniform_distribution(mio::UncertainValue& p, ScalarType min, ScalarT * The infection states are chosen randomly. They are distributed according to the probabilites set in the example. * @return random infection state */ -mio::abm::InfectionState determine_infection_state(mio::abm::Person::RandomNumberGenerator& rng, ScalarType exposed, +mio::abm::InfectionState determine_infection_state(mio::abm::PersonalRandomNumberGenerator& rng, ScalarType exposed, ScalarType infected_no_symptoms, ScalarType infected_symptoms, ScalarType recovered) { @@ -448,7 +448,7 @@ void assign_infection_state(mio::abm::World& world, mio::abm::TimePoint t, doubl { auto persons = world.get_persons(); for (auto& person : persons) { - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(world.get_rng(), person); auto infection_state = determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); if (infection_state != mio::abm::InfectionState::Susceptible) { diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index d92cb51fc9..4c93d852d5 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -22,9 +22,9 @@ #include #include #include "abm/abm.h" +#include "abm/person_id.h" #include "memilio/io/result_io.h" #include "memilio/utils/uncertain_value.h" -#include "boost/filesystem.hpp" #include "boost/algorithm/string/split.hpp" #include "boost/algorithm/string/classification.hpp" #include "abm/vaccine.h" @@ -59,7 +59,7 @@ void assign_uniform_distribution(mio::UncertainValue& p, ScalarType min, ScalarT * The infection states are chosen randomly. They are distributed according to the probabilites set in the example. * @return random infection state */ -mio::abm::InfectionState determine_infection_state(mio::abm::Person::RandomNumberGenerator& rng, ScalarType exposed, +mio::abm::InfectionState determine_infection_state(mio::abm::PersonalRandomNumberGenerator& rng, ScalarType exposed, ScalarType infected_no_symptoms, ScalarType infected_symptoms, ScalarType recovered) { @@ -82,7 +82,7 @@ void assign_infection_state(mio::abm::World& world, mio::abm::TimePoint t, doubl { auto persons = world.get_persons(); for (auto& person : persons) { - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(world.get_rng(), person); auto infection_state = determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); if (infection_state != mio::abm::InfectionState::Susceptible) @@ -949,10 +949,10 @@ void write_log_to_file_trip_data(const T& history) for (uint32_t movement_data_index = 2; movement_data_index < movement_data.size(); ++movement_data_index) { myfile3 << "timestep Nr.: " << movement_data_index - 1 << "\n"; for (uint32_t trip_index = 0; trip_index < movement_data[movement_data_index].size(); trip_index++) { - auto agent_id = (int)std::get<0>(movement_data[movement_data_index][trip_index]); + auto agent_id = std::get<0>(movement_data[movement_data_index][trip_index]); int start_index = movement_data_index - 1; - using Type = std::tuple; while (!std::binary_search(std::begin(movement_data[start_index]), std::end(movement_data[start_index]), movement_data[movement_data_index][trip_index], diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 4444720313..4ca62a2025 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -29,7 +29,7 @@ mio::abm::Person make_test_person(mio::abm::Location& location, mio::AgeGroup ag auto rng = mio::RandomNumberGenerator(); mio::abm::Person p(rng, location, age); if (infection_state != mio::abm::InfectionState::Susceptible) { - auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); + auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( mio::abm::Infection(rng_p, static_cast(0), age, params, t, infection_state)); } diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index bb6df0c064..759d9cbfb7 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -34,7 +34,7 @@ TEST(TestInfection, init) //uses uniformdistribution but result doesn't matter, so init before the mock mio::abm::Location loc(mio::abm::LocationType::Hospital, 0); auto counter = mio::Counter(0); - auto rng = mio::abm::Person::RandomNumberGenerator(mio::Key{0}, 0, counter); + auto rng = mio::abm::PersonalRandomNumberGenerator(mio::Key{0}, mio::abm::PersonId(0), counter); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -100,7 +100,7 @@ TEST(TestInfection, init) TEST(TestInfection, getInfectionState) { auto counter = mio::Counter(0); - auto rng = mio::abm::Person::RandomNumberGenerator(mio::Key{0}, 0, counter); + auto rng = mio::abm::PersonalRandomNumberGenerator(mio::Key{0}, mio::abm::PersonId(0), counter); auto params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); auto infection = mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, age_group_15_to_34, params, t, @@ -112,7 +112,7 @@ TEST(TestInfection, getInfectionState) TEST(TestInfection, drawInfectionCourseBackward) { auto counter = mio::Counter(0); - auto rng = mio::abm::Person::RandomNumberGenerator(mio::Key{0}, 0, counter); + auto rng = mio::abm::PersonalRandomNumberGenerator(mio::Key{0}, mio::abm::PersonId(0), counter); auto t = mio::abm::TimePoint(1); auto dt = mio::abm::days(1); diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 177e8a7e88..3dd2897145 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -121,12 +121,12 @@ TEST(TestLocation, getIndex) // mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups, 1); // mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, num_age_groups, 3); // auto infected1 = mio::abm::Person(rng, home, age); -// auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); +// auto rng_infected1 = mio::abm::PersonalRandomNumberGenerator(rng, infected1); // infected1.add_new_infection( // mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); // mio::abm::migrate(infected1, location, {0}); // auto infected2 = mio::abm::Person(rng, home, age); -// auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); +// auto rng_infected2 = mio::abm::PersonalRandomNumberGenerator(rng, infected2); // infected2.add_new_infection( // mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); // mio::abm::migrate(infected2, location, {0, 1}); @@ -267,7 +267,7 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); - auto person_rng = mio::abm::Person::RandomNumberGenerator(rng, susceptible); + auto person_rng = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); mio::abm::interact(susceptible, location, local_population, t, dt, params, person_rng); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index 2124f5ac6d..7a183736d1 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -53,17 +53,17 @@ TEST(TestLockdownRules, school_closure) p2.set_assigned_location(school); 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; + params.get() = false; params.get()[age_group_5_to_14] = true; // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 or 35-59) - params.get() = false; + params.get() = false; params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; mio::abm::set_school_closure(t, 0.7, params); - auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, p1); + auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, p1); ASSERT_EQ(mio::abm::go_to_school(p1_rng, p1, t_morning, dt, params), mio::abm::LocationType::Home); - auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, p2); + auto p2_rng = mio::abm::PersonalRandomNumberGenerator(rng, p2); ASSERT_EQ(mio::abm::go_to_school(p2_rng, p2, t_morning, dt, params), mio::abm::LocationType::School); } @@ -91,16 +91,16 @@ TEST(TestLockdownRules, school_opening) p.set_assigned_location(school); 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; + params.get() = false; params.get()[age_group_5_to_14] = true; // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 or 35-59) - params.get() = false; + params.get() = false; params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; mio::abm::set_school_closure(t_closing, 1., params); mio::abm::set_school_closure(t_opening, 0., params); - auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, p); ASSERT_EQ(mio::abm::go_to_school(p_rng, p, t_morning, dt, params), mio::abm::LocationType::School); } @@ -116,10 +116,10 @@ TEST(TestLockdownRules, home_office) mio::abm::Parameters params(num_age_groups); // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) - params.get() = false; + params.get() = false; params.get()[age_group_5_to_14] = true; // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 or 35-59) - params.get() = false; + params.get() = false; params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; @@ -142,9 +142,9 @@ TEST(TestLockdownRules, home_office) person2.set_assigned_location(home); person2.set_assigned_location(work); - auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, person1); + auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, person1); ASSERT_EQ(mio::abm::go_to_work(p1_rng, person1, t_morning, dt, params), mio::abm::LocationType::Work); - auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, person2); + auto p2_rng = mio::abm::PersonalRandomNumberGenerator(rng, person2); ASSERT_EQ(mio::abm::go_to_work(p2_rng, person2, t_morning, dt, params), mio::abm::LocationType::Home); } @@ -173,17 +173,17 @@ TEST(TestLockdownRules, no_home_office) p.set_assigned_location(work); 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; + params.get() = false; params.get()[age_group_5_to_14] = true; // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 or 35-59) - params.get() = false; + params.get() = false; params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; mio::abm::set_home_office(t_closing, 0.5, params); mio::abm::set_home_office(t_opening, 0., params); - auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, p); ASSERT_EQ(mio::abm::go_to_work(p_rng, p, t_morning, dt, params), mio::abm::LocationType::Work); } @@ -203,7 +203,7 @@ TEST(TestLockdownRules, social_event_closure) mio::abm::close_social_events(t, 1, params); - auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, p); ASSERT_EQ(mio::abm::go_to_event(p_rng, p, t_evening, dt, params), mio::abm::LocationType::Home); } @@ -228,6 +228,6 @@ TEST(TestLockdownRules, social_events_opening) ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, p); ASSERT_EQ(mio::abm::go_to_event(p_rng, p, t_evening, dt, params), mio::abm::LocationType::SocialEvent); } diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index ed8cd30627..4b23c3b866 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -81,11 +81,11 @@ TEST(TestMasks, maskProtection) ScopedMockDistribution>>> mock_exponential_dist; - auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person1); + auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person1); mio::abm::interact(susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params, p1_rng); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); - auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person2); + auto p2_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person2); mio::abm::interact(susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params, p2_rng); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index c8a62d34b4..fa587bd0fc 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -42,8 +42,8 @@ TEST(TestMigrationRules, student_goes_to_school) auto t_weekend = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(7); auto dt = mio::abm::hours(1); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); - auto child_rng = mio::abm::Person::RandomNumberGenerator(rng, p_child); - auto adult_rng = mio::abm::Person::RandomNumberGenerator(rng, p_child); + auto child_rng = mio::abm::PersonalRandomNumberGenerator(rng, p_child); + auto adult_rng = mio::abm::PersonalRandomNumberGenerator(rng, p_child); // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) params.get() = false; params.get()[age_group_5_to_14] = true; @@ -79,9 +79,9 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home, age_group_5_to_14); - auto rng_child_goes_to_school_at_6 = mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_6); + auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_6); auto p_child_goes_to_school_at_8 = mio::abm::Person(rng, home, age_group_5_to_14); - auto rng_child_goes_to_school_at_8 = mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_8); + auto rng_child_goes_to_school_at_8 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); auto t_morning_8 = mio::abm::TimePoint(0) + mio::abm::hours(8); @@ -139,10 +139,10 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times_with_smaller_t mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home, age_group_5_to_14); - auto rng_child_goes_to_school_at_6 = mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_6); + auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_6); auto p_child_goes_to_school_at_8_30 = mio::abm::Person(rng, home, age_group_5_to_14); auto rng_child_goes_to_school_at_8_30 = - mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_8_30); + mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_8_30); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); auto t_morning_8_30 = mio::abm::TimePoint(0) + mio::abm::hours(8) + mio::abm::seconds(1800); @@ -176,7 +176,7 @@ TEST(TestMigrationRules, school_return) auto rng = mio::RandomNumberGenerator(); mio::abm::Location school(mio::abm::LocationType::School, 0, num_age_groups); auto p_child = mio::abm::Person(rng, school, age_group_5_to_14); - auto rng_child = mio::abm::Person::RandomNumberGenerator(rng, p_child); + auto rng_child = mio::abm::PersonalRandomNumberGenerator(rng, p_child); auto t = mio::abm::TimePoint(0) + mio::abm::hours(15); auto dt = mio::abm::hours(1); @@ -203,9 +203,9 @@ TEST(TestMigrationRules, worker_goes_to_work) .WillRepeatedly(testing::Return(1.0)); auto p_retiree = mio::abm::Person(rng, home, age_group_60_to_79); - auto rng_retiree = mio::abm::Person::RandomNumberGenerator(rng, p_retiree); + auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); auto p_adult = mio::abm::Person(rng, home, age_group_15_to_34); - auto rng_adult = mio::abm::Person::RandomNumberGenerator(rng, p_adult); + auto rng_adult = mio::abm::PersonalRandomNumberGenerator(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); @@ -243,9 +243,9 @@ TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) .WillRepeatedly(testing::Return(1.0)); auto p_retiree = mio::abm::Person(rng, home, age_group_60_to_79); - auto rng_retiree = mio::abm::Person::RandomNumberGenerator(rng, p_retiree); + auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); auto p_adult = mio::abm::Person(rng, home, age_group_15_to_34); - auto rng_adult = mio::abm::Person::RandomNumberGenerator(rng, p_adult); + auto rng_adult = mio::abm::PersonalRandomNumberGenerator(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); @@ -284,9 +284,9 @@ TEST(TestMigrationRules, workers_go_to_work_in_different_times) .WillRepeatedly(testing::Return(1.0)); auto p_adult_goes_to_work_at_6 = mio::abm::Person(rng, home, age_group_15_to_34); - auto rng_adult_goes_to_work_at_6 = mio::abm::Person::RandomNumberGenerator(rng, p_adult_goes_to_work_at_6); + auto rng_adult_goes_to_work_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_adult_goes_to_work_at_6); auto p_adult_goes_to_work_at_8 = mio::abm::Person(rng, home, age_group_15_to_34); - auto rng_adult_goes_to_work_at_8 = mio::abm::Person::RandomNumberGenerator(rng, p_adult_goes_to_work_at_8); + auto rng_adult_goes_to_work_at_8 = mio::abm::PersonalRandomNumberGenerator(rng, p_adult_goes_to_work_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); auto t_morning_8 = mio::abm::TimePoint(0) + mio::abm::hours(8); @@ -320,7 +320,7 @@ TEST(TestMigrationRules, work_return) auto rng = mio::RandomNumberGenerator(); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); auto p_adult = mio::abm::Person(rng, work, age_group_35_to_59); - auto rng_adult = mio::abm::Person::RandomNumberGenerator(rng, p_adult); + auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); auto dt = mio::abm::hours(1); ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t, dt, mio::abm::Parameters(num_age_groups)), @@ -339,18 +339,18 @@ TEST(TestMigrationRules, quarantine) mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0, num_age_groups); auto p_inf1 = make_test_person(work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); - auto rng_inf1 = mio::abm::Person::RandomNumberGenerator(rng, p_inf1); + auto rng_inf1 = mio::abm::PersonalRandomNumberGenerator(rng, p_inf1); p_inf1.get_tested(rng_inf1, t, test_params); ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf1, p_inf1, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); //detected infected person quarantines at home auto p_inf2 = make_test_person(work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); - auto rng_inf2 = mio::abm::Person::RandomNumberGenerator(rng, p_inf2); + auto rng_inf2 = mio::abm::PersonalRandomNumberGenerator(rng, p_inf2); ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf2, p_inf2, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); //undetected infected person does not quaratine auto p_inf3 = make_test_person(hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); - auto rng_inf3 = mio::abm::Person::RandomNumberGenerator(rng, p_inf3); + auto rng_inf3 = mio::abm::PersonalRandomNumberGenerator(rng, p_inf3); p_inf1.get_tested(rng_inf3, t, test_params); ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf3, p_inf3, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Hospital); //detected infected person does not leave hospital to quarantine @@ -363,13 +363,13 @@ TEST(TestMigrationRules, hospital) auto t = mio::abm::TimePoint(12346); auto dt = mio::abm::hours(1); auto p_inf = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); - auto rng_inf = mio::abm::Person::RandomNumberGenerator(rng, p_inf); + auto rng_inf = mio::abm::PersonalRandomNumberGenerator(rng, p_inf); ASSERT_EQ(mio::abm::go_to_hospital(rng_inf, p_inf, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Hospital); auto p_car = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); - auto rng_car = mio::abm::Person::RandomNumberGenerator(rng, p_car); + auto rng_car = mio::abm::PersonalRandomNumberGenerator(rng, p_car); ASSERT_EQ(mio::abm::go_to_hospital(rng_car, p_car, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } @@ -386,9 +386,9 @@ TEST(TestMigrationRules, go_shopping) auto dt = mio::abm::hours(1); auto p_hosp = make_test_person(hospital, age_group_0_to_4, mio::abm::InfectionState::InfectedSymptoms, t_weekday); - auto rng_hosp = mio::abm::Person::RandomNumberGenerator(rng, p_hosp); + auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(rng, p_hosp); auto p_home = mio::abm::Person(rng, home, age_group_60_to_79); - auto rng_home = mio::abm::Person::RandomNumberGenerator(rng, p_home); + auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); ASSERT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Hospital); @@ -414,7 +414,7 @@ TEST(TestMigrationRules, shop_return) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0, num_age_groups); auto p = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); - auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); + auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, shop); mio::abm::interact(p, shop, {p}, t, dt, params, rng_p); //person only returns home after some time passed @@ -428,10 +428,10 @@ TEST(TestMigrationRules, go_event) auto rng = mio::RandomNumberGenerator(); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); auto p_work = mio::abm::Person(rng, work, age_group_35_to_59); - auto rng_work = mio::abm::Person::RandomNumberGenerator(rng, p_work); + auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); auto p_home = mio::abm::Person(rng, home, age_group_60_to_79); - auto rng_home = mio::abm::Person::RandomNumberGenerator(rng, p_home); + auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); auto t_weekday = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(20); auto t_saturday = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(10); @@ -464,7 +464,7 @@ TEST(TestMigrationRules, event_return) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 0, num_age_groups); auto p = mio::abm::Person(rng, home, age_group_15_to_34); - auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); + auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, social_event); mio::abm::interact(p, social_event, {p}, t, dt, params, rng_p); @@ -480,14 +480,14 @@ TEST(TestMigrationRules, icu) auto t = mio::abm::TimePoint(12346); auto dt = mio::abm::hours(1); auto p_hosp = make_test_person(hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedCritical, t); - auto rng_hosp = mio::abm::Person::RandomNumberGenerator(rng, p_hosp); + auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(rng, p_hosp); ASSERT_EQ(mio::abm::go_to_icu(rng_hosp, p_hosp, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::ICU); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); auto p_work = make_test_person(work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); - auto rng_work = mio::abm::Person::RandomNumberGenerator(rng, p_work); + auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); ASSERT_EQ(mio::abm::go_to_icu(rng_work, p_work, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); } @@ -499,9 +499,9 @@ TEST(TestMigrationRules, recover) auto t = mio::abm::TimePoint(12346); auto dt = mio::abm::hours(1); auto p_rec = make_test_person(hospital, age_group_60_to_79, mio::abm::InfectionState::Recovered, t); - auto rng_rec = mio::abm::Person::RandomNumberGenerator(rng, p_rec); + auto rng_rec = mio::abm::PersonalRandomNumberGenerator(rng, p_rec); auto p_inf = make_test_person(hospital, age_group_60_to_79, mio::abm::InfectionState::InfectedSevere, t); - auto rng_inf = mio::abm::Person::RandomNumberGenerator(rng, p_inf); + auto rng_inf = mio::abm::PersonalRandomNumberGenerator(rng, p_inf); ASSERT_EQ(mio::abm::return_home_when_recovered(rng_rec, p_rec, t, dt, {num_age_groups}), mio::abm::LocationType::Home); ASSERT_EQ(mio::abm::return_home_when_recovered(rng_inf, p_inf, t, dt, {num_age_groups}), @@ -516,7 +516,7 @@ TEST(TestMigrationRules, dead) auto t = mio::abm::TimePoint(12346); auto dt = mio::abm::hours(1); auto p_dead = make_test_person(icu, age_group_60_to_79, mio::abm::InfectionState::Dead, t); - auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p_dead); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, p_dead); ASSERT_EQ(mio::abm::get_buried(p_rng, p_dead, t, dt, {num_age_groups}), mio::abm::LocationType::Cemetery); } diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 6ea3a6296f..384ad55826 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -21,6 +21,7 @@ #include "abm/location_type.h" #include "abm/person.h" +#include "abm/person_id.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" #include @@ -35,7 +36,7 @@ TEST(TestPerson, init) EXPECT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(person.get_location(), location.get_id()); - EXPECT_EQ(person.get_person_id(), mio::abm::INVALID_PERSON_ID); + EXPECT_EQ(person.get_person_id(), mio::abm::PersonId::invalid_id()); } TEST(TestPerson, copyPerson) @@ -49,7 +50,7 @@ TEST(TestPerson, copyPerson) EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(copied_person.get_location(), copied_location.get_id()); - EXPECT_EQ(copied_person.get_person_id(), mio::abm::INVALID_PERSON_ID); + EXPECT_EQ(copied_person.get_person_id(), mio::abm::PersonId::invalid_id()); } TEST(TestPerson, migrate) @@ -120,7 +121,7 @@ TEST(TestPerson, quarantine) auto person = make_test_person(home, age_group_35_to_59, mio::abm::InfectionState::InfectedSymptoms, t_morning, infection_parameters); - auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); person.get_tested(rng_person, t_morning, test_params); @@ -142,9 +143,9 @@ TEST(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(loc, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); - auto rng_infected = mio::abm::Person::RandomNumberGenerator(rng, infected); + auto rng_infected = mio::abm::PersonalRandomNumberGenerator(rng, infected); auto susceptible = mio::abm::Person(rng, loc, age_group_15_to_34); - auto rng_suscetible = mio::abm::Person::RandomNumberGenerator(rng, susceptible); + auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); auto pcr_test = mio::abm::PCRTest(); auto antigen_test = mio::abm::AntigenTest(); @@ -205,7 +206,7 @@ TEST(TestPerson, interact) mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::TimePoint t(0); auto person = mio::abm::Person(rng, loc, age_group_15_to_34); - auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days mio::abm::interact(person, loc, {person}, t, dt, infection_parameters, rng_person); EXPECT_EQ(person.get_time_at_location(), dt); @@ -219,7 +220,7 @@ TEST(TestPerson, applyMaskIntervention) mio::abm::Location target(mio::abm::LocationType::Work, 0, num_age_groups); auto person = make_test_person(home); person.get_mask().change_mask(mio::abm::MaskType::Community); - auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); target.set_npi_active(false); person.apply_mask_intervention(rng_person, target); @@ -290,7 +291,7 @@ TEST(TestPerson, getLatestProtection) auto rng = mio::RandomNumberGenerator(); auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); auto person = mio::abm::Person(rng, location, age_group_15_to_34); - auto prng = mio::abm::Person::RandomNumberGenerator(rng, person); + auto prng = mio::abm::PersonalRandomNumberGenerator(rng, person); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); @@ -311,14 +312,14 @@ TEST(Person, rng) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location loc(mio::abm::LocationType::Home, 0); - auto p = mio::abm::Person(rng, loc, age_group_35_to_59, 13); + auto p = mio::abm::Person(rng, loc, age_group_35_to_59, mio::abm::PersonId(13)); - ASSERT_EQ(p.get_rng_counter(), mio::Counter(0)); + EXPECT_EQ(p.get_rng_counter(), mio::Counter(0)); - auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); - ASSERT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{0})); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, p); + EXPECT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{0})); p_rng(); - ASSERT_EQ(p.get_rng_counter(), mio::Counter(1)); - ASSERT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{1})); + EXPECT_EQ(p.get_rng_counter(), mio::Counter(1)); + EXPECT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{1})); } \ No newline at end of file diff --git a/cpp/tests/test_abm_testing_strategy.cpp b/cpp/tests/test_abm_testing_strategy.cpp index 7442701625..b44c350961 100644 --- a/cpp/tests/test_abm_testing_strategy.cpp +++ b/cpp/tests/test_abm_testing_strategy.cpp @@ -91,9 +91,9 @@ TEST(TestTestingScheme, runScheme) mio::abm::Location loc_home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location loc_work(mio::abm::LocationType::Work, 0, num_age_groups); auto person1 = make_test_person(loc_home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - auto rng_person1 = mio::abm::Person::RandomNumberGenerator(rng, person1); + auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(rng, person1); auto person2 = make_test_person(loc_home, age_group_15_to_34, mio::abm::InfectionState::Recovered); - auto rng_person2 = mio::abm::Person::RandomNumberGenerator(rng, person2); + auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(rng, person2); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -130,9 +130,9 @@ TEST(TestTestingScheme, initAndRunTestingStrategy) mio::abm::Location loc_work(mio::abm::LocationType::Work, 0); auto person1 = make_test_person(loc_work, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - auto rng_person1 = mio::abm::Person::RandomNumberGenerator(rng, person1); + auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(rng, person1); auto person2 = make_test_person(loc_work, age_group_15_to_34, mio::abm::InfectionState::Recovered); - auto rng_person2 = mio::abm::Person::RandomNumberGenerator(rng, person2); + auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(rng, person2); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 0a8f69f47d..fc33ec76d6 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -438,7 +438,7 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) auto pid = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time); auto& person = world.get_person(pid); - auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); person.set_assigned_location(home); person.set_assigned_location(work); @@ -589,7 +589,7 @@ TEST(TestWorld, copyWorld) // TODO: this needs either a rewrite or to be removed auto& p1 = world.get_person(pid1); auto& p2 = world.get_person(pid2); - auto rng_p1 = mio::abm::Person::RandomNumberGenerator(rng, p1); + auto rng_p1 = mio::abm::PersonalRandomNumberGenerator(rng, p1); p1.add_new_infection(mio::abm::Infection(rng_p1, mio::abm::VirusVariant::Wildtype, p1.get_age(), world.parameters, mio::abm::TimePoint(0))); p2.set_mask_preferences(std::vector(15, 0.2)); From b0e49ed6dde96907c20ac124b6c91f8ef76bf3ab Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:02:49 +0100 Subject: [PATCH 14/54] fix (in)validating exposure caches for simulations --- cpp/models/abm/world.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index fa22bdd688..e146bab4c1 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -375,9 +375,13 @@ class World { LocationId origin = get_location(person).get_id(); const bool has_moved = mio::abm::migrate(get_person(person), get_location(destination), cells, mode); - if (has_moved && m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(origin).erase(person.get()); - m_local_populations_cache.data.at(destination).emplace(person.get()); + if (has_moved) { + m_air_exposure_rates_cache.invalidate(); + m_contact_exposure_rates_cache.invalidate(); + if (m_local_populations_cache.is_valid()) { + m_local_populations_cache.data.at(origin).erase(person.get()); + m_local_populations_cache.data.at(destination).emplace(person.get()); + } } } @@ -394,6 +398,8 @@ class World { if (!m_air_exposure_rates_cache.is_valid() || !m_contact_exposure_rates_cache.is_valid()) { recompute_exposure_rates(t, dt); + m_air_exposure_rates_cache.validate(); + m_contact_exposure_rates_cache.validate(); } mio::abm::interact(get_person(person), get_location(person), m_air_exposure_rates_cache.data.at(get_location(person).get_id()), From 8293c79478546dba9d5744b903f4da9bebaa8f8f Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:19:57 +0100 Subject: [PATCH 15/54] avoid hashing LocationId --- cpp/models/abm/world.cpp | 2 +- cpp/models/abm/world.h | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index ef55b3d3a9..10d701baf4 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -38,7 +38,7 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) m_has_locations[size_t(type)] = true; if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data[id]; + m_local_populations_cache.data[id.index]; } return id; } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index e146bab4c1..3d8121f349 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -185,7 +185,7 @@ class World new_person.set_assigned_location(m_cemetery_id); if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(new_person.get_location()).emplace(new_id); + m_local_populations_cache.data.at(new_person.get_location().index).emplace(new_id); } return new_id; } @@ -338,8 +338,8 @@ class World size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { if (m_local_populations_cache.is_valid()) { - return std::count_if(m_local_populations_cache.data.at(location).begin(), - m_local_populations_cache.data.at(location).end(), [&](uint32_t p) { + return std::count_if(m_local_populations_cache.data.at(location.index).begin(), + m_local_populations_cache.data.at(location.index).end(), [&](uint32_t p) { return get_person(PersonId(p)).get_infection_state(t) == state; }); } @@ -357,7 +357,7 @@ class World size_t get_number_persons(LocationId location) const { if (m_local_populations_cache.is_valid()) { - return m_local_populations_cache.data.at(location).size(); + return m_local_populations_cache.data.at(location.index).size(); } return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p.get_location() == location; @@ -379,8 +379,8 @@ class World m_air_exposure_rates_cache.invalidate(); m_contact_exposure_rates_cache.invalidate(); if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(origin).erase(person.get()); - m_local_populations_cache.data.at(destination).emplace(person.get()); + m_local_populations_cache.data.at(origin.index).erase(person.get()); + m_local_populations_cache.data.at(destination.index).emplace(person.get()); } } } @@ -402,8 +402,8 @@ class World m_contact_exposure_rates_cache.validate(); } mio::abm::interact(get_person(person), get_location(person), - m_air_exposure_rates_cache.data.at(get_location(person).get_id()), - m_contact_exposure_rates_cache.data.at(get_location(person).get_id()), t, dt, + m_air_exposure_rates_cache.data.at(get_location(person).get_index()), + m_contact_exposure_rates_cache.data.at(get_location(person).get_index()), t, dt, global_parameters, personal_rng); } @@ -487,10 +487,10 @@ class World { m_local_populations_cache.data.clear(); for (size_t i = 0; i < m_locations.size(); i++) { - m_local_populations_cache.data[m_locations[i].get_id()].clear(); + m_local_populations_cache.data[m_locations[i].get_index()].clear(); } for (Person& person : get_persons()) { - m_local_populations_cache.data.at(person.get_location()).emplace(person.get_person_id()); + m_local_populations_cache.data.at(person.get_location().index).emplace(person.get_person_id()); } } @@ -500,25 +500,25 @@ class World m_contact_exposure_rates_cache.data.clear(); for (Location& location : m_locations) { m_air_exposure_rates_cache.data.emplace( - location.get_id(), + location.get_index(), Location::AirExposureRates({CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.)); m_contact_exposure_rates_cache.data.emplace( - location.get_id(), + location.get_index(), Location::ContactExposureRates({CellIndex(location.get_cells().size()), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}, 0.)); } for (Person& person : get_persons()) { - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.data.at(person.get_location()), - m_contact_exposure_rates_cache.data.at(person.get_location()), person, - get_location(person.get_person_id()), t, dt); + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.data.at(person.get_location().index), + m_contact_exposure_rates_cache.data.at(person.get_location().index), + person, get_location(person.get_person_id()), t, dt); } } - Cache>> + Cache>> m_local_populations_cache; // TODO: change this to storing only (sub)population(s) in numbers, using atomic ints - Cache> m_air_exposure_rates_cache; - Cache> m_contact_exposure_rates_cache; + Cache> m_air_exposure_rates_cache; + Cache> m_contact_exposure_rates_cache; std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. From e50bd753d064ff0dd7b31a02bfecd6bb947e5624 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 1 Feb 2024 13:07:37 +0100 Subject: [PATCH 16/54] reorder some arguments of free functions, add a few consts --- cpp/models/abm/functions.cpp | 25 ++++++++++++------------- cpp/models/abm/functions.h | 12 +++++++----- cpp/models/abm/world.h | 8 ++++---- cpp/tests/test_abm_location.cpp | 4 ++-- cpp/tests/test_abm_masks.cpp | 8 ++++---- cpp/tests/test_abm_migration_rules.cpp | 4 ++-- cpp/tests/test_abm_person.cpp | 2 +- 7 files changed, 32 insertions(+), 31 deletions(-) diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index 7c5ad43d06..54c5ea1e30 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -6,7 +6,6 @@ #include "abm/infection.h" #include "abm/virus_variant.h" #include "memilio/epidemiology/age_group.h" -#include "memilio/utils/logging.h" namespace mio { @@ -14,12 +13,10 @@ namespace mio namespace abm { -// TODO: on argument order: maybe personal_rng first, as it always(?) is a non-const reference - // TODO: daily_transmissions functions are only used in interact. expose in header anyways? -ScalarType daily_transmissions_by_contacts(const Location::ContactExposureRates& rates, CellIndex cell_index, - VirusVariant virus, AgeGroup age_receiver, +ScalarType daily_transmissions_by_contacts(const Location::ContactExposureRates& rates, const CellIndex cell_index, + const VirusVariant virus, const AgeGroup age_receiver, const LocalInfectionParameters& params) { assert(age_receiver < rates.size()); @@ -31,15 +28,16 @@ ScalarType daily_transmissions_by_contacts(const Location::ContactExposureRates& return prob; } -ScalarType daily_transmissions_by_air(const Location::AirExposureRates& rates, CellIndex cell_index, VirusVariant virus, - const Parameters& global_params) +ScalarType daily_transmissions_by_air(const Location::AirExposureRates& rates, const CellIndex cell_index, + const VirusVariant virus, const Parameters& global_params) { return rates[{cell_index, virus}] * global_params.get()[{virus}]; } -void interact(Person& person, const Location& location, const Location::AirExposureRates& local_air_exposure, +void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, + const Location::AirExposureRates& local_air_exposure, const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng) + const Parameters& global_parameters) { // make sure all dimensions are set correctly and all indices are valid assert(location.get_cells().size() == local_air_exposure.size().get()); @@ -87,7 +85,7 @@ void interact(Person& person, const Location& location, const Location::AirExpos void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, Location::ContactExposureRates& local_contact_exposure, const Person& person, - const Location& location, TimePoint t, TimeSpan dt) + const Location& location, const TimePoint t, const TimeSpan dt) { assert([&]() { if (person.get_location() != location.get_id()) { @@ -115,8 +113,9 @@ void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, } } -void interact(Person& person, const Location& location, const std::vector& local_population, const TimePoint t, - const TimeSpan dt, const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng) +void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, + const std::vector& local_population, const TimePoint t, const TimeSpan dt, + const Parameters& global_parameters) { Location::AirExposureRates local_air_exposure{{CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.}; Location::ContactExposureRates local_contact_exposure{ @@ -125,7 +124,7 @@ void interact(Person& person, const Location& location, const std::vector& cells, const TransportMode mode) diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h index 4e0a1433c9..9d5f1005e7 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/functions.h @@ -16,16 +16,18 @@ namespace abm // add the contribution of person to the exposure rates at location void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, Location::ContactExposureRates& local_contact_exposure, const Person& person, - const Location& location, TimePoint t, TimeSpan dt); + const Location& location, const TimePoint t, const TimeSpan dt); // let a person interact with a location for and at some time -void interact(Person& person, const Location& location, const Location::AirExposureRates& local_air_exposure, +void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, + const Location::AirExposureRates& local_air_exposure, const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng); + const Parameters& global_parameters); // interact, but it computes the correct exposures for you -void interact(Person& person, const Location& location, const std::vector& local_population, const TimePoint t, - const TimeSpan dt, const Parameters& global_parameters, PersonalRandomNumberGenerator& personal_rng); +void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, + const std::vector& local_population, const TimePoint t, const TimeSpan dt, + const Parameters& global_parameters); // move a person to another location. returns false if the person was at the target location already, true otherwise bool migrate(Person& person, const Location& destination, const std::vector& cells = {0}, diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 3d8121f349..b8140b93f7 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -389,11 +389,11 @@ class World inline void interact(PersonId person, TimePoint t, TimeSpan dt) { auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person)); - interact(person, t, dt, personal_rng, parameters); + interact(personal_rng, person, t, dt, parameters); } // let a person interact with its current location - inline void interact(PersonId person, TimePoint t, TimeSpan dt, PersonalRandomNumberGenerator& personal_rng, + inline void interact(PersonalRandomNumberGenerator& personal_rng, PersonId person, TimePoint t, TimeSpan dt, const Parameters& global_parameters) { if (!m_air_exposure_rates_cache.is_valid() || !m_contact_exposure_rates_cache.is_valid()) { @@ -401,10 +401,10 @@ class World m_air_exposure_rates_cache.validate(); m_contact_exposure_rates_cache.validate(); } - mio::abm::interact(get_person(person), get_location(person), + mio::abm::interact(personal_rng, get_person(person), get_location(person), m_air_exposure_rates_cache.data.at(get_location(person).get_index()), m_contact_exposure_rates_cache.data.at(get_location(person).get_index()), t, dt, - global_parameters, personal_rng); + global_parameters); } // get location by id diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 3dd2897145..c402a8e27c 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -268,12 +268,12 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); auto person_rng = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); - mio::abm::interact(susceptible, location, local_population, t, dt, params, person_rng); + mio::abm::interact(person_rng, susceptible, location, local_population, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - mio::abm::interact(susceptible, location, local_population, t, dt, params, person_rng); + mio::abm::interact(person_rng, susceptible, location, local_population, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); } diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index 4b23c3b866..c61bc309b9 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -82,12 +82,12 @@ TEST(TestMasks, maskProtection) mock_exponential_dist; auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person1); - mio::abm::interact(susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params, - p1_rng); + mio::abm::interact(p1_rng, susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, + params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); auto p2_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person2); - mio::abm::interact(susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params, - p2_rng); + mio::abm::interact(p2_rng, susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, + params); // The person susc_person1 should have full protection against an infection, susc_person2 not ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index fa587bd0fc..19a40aad7c 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -417,7 +417,7 @@ TEST(TestMigrationRules, shop_return) auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, shop); - mio::abm::interact(p, shop, {p}, t, dt, params, rng_p); //person only returns home after some time passed + mio::abm::interact(rng_p, p, shop, {p}, t, dt, params); //person only returns home after some time passed ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); @@ -467,7 +467,7 @@ TEST(TestMigrationRules, event_return) auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, social_event); - mio::abm::interact(p, social_event, {p}, t, dt, params, rng_p); + mio::abm::interact(rng_p, p, social_event, {p}, t, dt, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 384ad55826..ea54e1b29e 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -208,7 +208,7 @@ TEST(TestPerson, interact) auto person = mio::abm::Person(rng, loc, age_group_15_to_34); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days - mio::abm::interact(person, loc, {person}, t, dt, infection_parameters, rng_person); + mio::abm::interact(rng_person, person, loc, {person}, t, dt, infection_parameters); EXPECT_EQ(person.get_time_at_location(), dt); } From 144252ae74e4ad678b4172821428cedeeda53bdc Mon Sep 17 00:00:00 2001 From: reneSchm Date: Fri, 2 Feb 2024 22:12:21 +0100 Subject: [PATCH 17/54] vastly improve performance. avoid creating Location objects, do not rebuild exposure caches --- cpp/models/abm/common_abm_loggers.h | 2 +- cpp/models/abm/location.cpp | 2 +- cpp/models/abm/location.h | 6 ++--- cpp/models/abm/world.cpp | 19 ++++++++----- cpp/models/abm/world.h | 41 +++++++++-------------------- cpp/tests/test_abm_person.cpp | 2 +- 6 files changed, 31 insertions(+), 41 deletions(-) diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 145ce9fdab..561762f2b6 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -177,7 +177,7 @@ struct LogInfectionState : mio::LogAlways { Eigen::VectorXd sum = Eigen::VectorXd::Zero(Eigen::Index(mio::abm::InfectionState::Count)); auto curr_time = sim.get_time(); PRAGMA_OMP(for) - for (auto&& location : sim.get_world().get_locations()) { + for (auto& location : sim.get_world().get_locations()) { for (uint32_t inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { sum[inf_state] += sim.get_world().get_subpopulation(location, curr_time, mio::abm::InfectionState(inf_state)); diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index 66e8ceaabb..3e93a3420d 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -37,7 +37,7 @@ Location::Location(LocationId loc_id, size_t num_agegroups, uint32_t num_cells) assert(num_cells > 0 && "Number of cells has to be larger than 0."); } -Location Location::copy_location_without_persons(size_t) const +Location Location::copy() const { return *this; } diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index c8604b6693..2389834d8b 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -90,7 +90,7 @@ class Location * @param[in] num_agegroups [Default: 1] The number of age groups in the model. * @param[in] num_cells [Default: 1] The number of Cell%s in which the Location is divided. */ - Location(LocationId loc_id, size_t num_agegroups = 1, uint32_t num_cells = 1); + explicit Location(LocationId loc_id, size_t num_agegroups = 1, uint32_t num_cells = 1); /** * @brief Construct a Location with provided parameters. @@ -99,7 +99,7 @@ class Location * @param[in] num_agegroups [Default: 1] The number of age groups in the model. * @param[in] num_cells [Default: 1] The number of Cell%s in which the Location is divided. */ - Location(LocationType loc_type, uint32_t loc_index, size_t num_agegroups = 1, uint32_t num_cells = 1) + explicit Location(LocationType loc_type, uint32_t loc_index, size_t num_agegroups = 1, uint32_t num_cells = 1) : Location(LocationId{loc_index, loc_type}, num_agegroups, num_cells) { } @@ -119,7 +119,7 @@ class Location * @brief Return a copy of this #Location object with an empty m_persons. * @param[in] num_agegroups The number of age groups in the model. */ - Location copy_location_without_persons(size_t num_agegroups) const; + Location copy() const; /** * @brief Compare two Location%s. diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 10d701baf4..07de1bb769 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -40,6 +40,11 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) if (m_local_populations_cache.is_valid()) { m_local_populations_cache.data[id.index]; } + m_air_exposure_rates_cache.data.emplace( + id.index, Location::AirExposureRates({CellIndex(num_cells), VirusVariant::Count}, 0.)); + m_contact_exposure_rates_cache.data.emplace( + id.index, Location::ContactExposureRates( + {CellIndex(num_cells), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}, 0.)); return id; } @@ -75,15 +80,15 @@ void World::migration(TimePoint t, TimeSpan dt) auto try_migration_rule = [&](auto rule) -> bool { //run migration rule and check if migration can actually happen - auto target_type = rule(personal_rng, person, t, dt, parameters); - auto target_location = find_location(target_type, person); - auto current_location = person.get_location(); + auto target_type = rule(personal_rng, person, t, dt, parameters); + const Location& target_location = get_location(find_location(target_type, person)); + const LocationId current_location = person.get_location(); if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { - if (target_location != current_location && - get_number_persons(target_location) < get_location(target_location).get_capacity().persons) { + if (target_location.get_id() != current_location && + get_number_persons(target_location) < target_location.get_capacity().persons) { bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { - migrate(person.get_person_id(), target_location); // TODO: i == PersonId, use? + migrate(person.get_person_id(), target_location.get_id()); // TODO: i == PersonId, use? } return true; } @@ -148,6 +153,8 @@ void World::begin_step(TimePoint t, TimeSpan dt) m_local_populations_cache.validate(); } recompute_exposure_rates(t, dt); + m_air_exposure_rates_cache.validate(); + m_contact_exposure_rates_cache.validate(); } auto World::get_locations() const -> Range> diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index b8140b93f7..94177513f1 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -349,7 +349,7 @@ class World }); } - inline size_t get_subpopulation(Location location, TimePoint t, InfectionState state) const + inline size_t get_subpopulation(const Location& location, TimePoint t, InfectionState state) const { return get_subpopulation(location.get_id(), t, state); } @@ -364,7 +364,7 @@ class World }); } - inline size_t get_number_persons(Location location) const + inline size_t get_number_persons(const Location& location) const { return get_number_persons(location.get_id()); } @@ -452,18 +452,9 @@ class World template struct Cache { T data; - bool m_is_valid = false; - mutable size_t m_hits = 0; - mutable size_t m_misses = 0; bool is_valid() const { - if (m_is_valid) { - m_hits++; - } - else { - m_misses++; - } return m_is_valid; } @@ -477,10 +468,8 @@ class World m_is_valid = true; } - // ~Cache() - // { - // std::cout << "hits: " << m_hits << " misses: " << m_misses << "\n"; - // } + private: + bool m_is_valid = false; }; void rebuild() @@ -496,22 +485,16 @@ class World void recompute_exposure_rates(TimePoint t, TimeSpan dt) { - m_air_exposure_rates_cache.data.clear(); - m_contact_exposure_rates_cache.data.clear(); - for (Location& location : m_locations) { - m_air_exposure_rates_cache.data.emplace( - location.get_index(), - Location::AirExposureRates({CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.)); - m_contact_exposure_rates_cache.data.emplace( - location.get_index(), - Location::ContactExposureRates({CellIndex(location.get_cells().size()), VirusVariant::Count, - AgeGroup(parameters.get_num_groups())}, - 0.)); + for (Location& location : get_locations()) { + auto index = location.get_index(); + m_air_exposure_rates_cache.data.at(index).array().setZero(); + m_contact_exposure_rates_cache.data.at(index).array().setZero(); } for (Person& person : get_persons()) { - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.data.at(person.get_location().index), - m_contact_exposure_rates_cache.data.at(person.get_location().index), - person, get_location(person.get_person_id()), t, dt); + auto location = person.get_location().index; + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.data.at(location), + m_contact_exposure_rates_cache.data.at(location), person, + get_location(person.get_person_id()), t, dt); } } diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index ea54e1b29e..ae33452557 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -45,7 +45,7 @@ TEST(TestPerson, copyPerson) auto location = mio::abm::Location(mio::abm::LocationType::Work, 0, num_age_groups); auto t = mio::abm::TimePoint(0); auto person = mio::abm::Person(rng, location, age_group_60_to_79); - auto copied_location = location.copy_location_without_persons(num_age_groups); + auto copied_location = location.copy(); auto copied_person = person.copy_person(copied_location); EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); From da187500668fe08b0e2c7efa295a171ab6d8c882 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 7 Feb 2024 14:43:19 +0100 Subject: [PATCH 18/54] hide cache data behind read/write functions --- cpp/models/abm/world.cpp | 6 +++--- cpp/models/abm/world.h | 42 +++++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 07de1bb769..19e2c61fca 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -38,11 +38,11 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) m_has_locations[size_t(type)] = true; if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data[id.index]; + m_local_populations_cache.write()[id.index]; } - m_air_exposure_rates_cache.data.emplace( + m_air_exposure_rates_cache.write().emplace( id.index, Location::AirExposureRates({CellIndex(num_cells), VirusVariant::Count}, 0.)); - m_contact_exposure_rates_cache.data.emplace( + m_contact_exposure_rates_cache.write().emplace( id.index, Location::ContactExposureRates( {CellIndex(num_cells), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}, 0.)); return id; diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 94177513f1..d2f045ba68 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -185,7 +185,7 @@ class World new_person.set_assigned_location(m_cemetery_id); if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(new_person.get_location().index).emplace(new_id); + m_local_populations_cache.write().at(new_person.get_location().index).emplace(new_id); } return new_id; } @@ -338,8 +338,8 @@ class World size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { if (m_local_populations_cache.is_valid()) { - return std::count_if(m_local_populations_cache.data.at(location.index).begin(), - m_local_populations_cache.data.at(location.index).end(), [&](uint32_t p) { + return std::count_if(m_local_populations_cache.read().at(location.index).begin(), + m_local_populations_cache.read().at(location.index).end(), [&](uint32_t p) { return get_person(PersonId(p)).get_infection_state(t) == state; }); } @@ -357,7 +357,7 @@ class World size_t get_number_persons(LocationId location) const { if (m_local_populations_cache.is_valid()) { - return m_local_populations_cache.data.at(location.index).size(); + return m_local_populations_cache.read().at(location.index).size(); } return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p.get_location() == location; @@ -379,8 +379,8 @@ class World m_air_exposure_rates_cache.invalidate(); m_contact_exposure_rates_cache.invalidate(); if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.data.at(origin.index).erase(person.get()); - m_local_populations_cache.data.at(destination.index).emplace(person.get()); + m_local_populations_cache.write().at(origin.index).erase(person.get()); + m_local_populations_cache.write().at(destination.index).emplace(person.get()); } } } @@ -402,8 +402,8 @@ class World m_contact_exposure_rates_cache.validate(); } mio::abm::interact(personal_rng, get_person(person), get_location(person), - m_air_exposure_rates_cache.data.at(get_location(person).get_index()), - m_contact_exposure_rates_cache.data.at(get_location(person).get_index()), t, dt, + m_air_exposure_rates_cache.read().at(get_location(person).get_index()), + m_contact_exposure_rates_cache.read().at(get_location(person).get_index()), t, dt, global_parameters); } @@ -451,7 +451,16 @@ class World template struct Cache { - T data; + + const T& read() const + { + return data; + } + + T& write() + { + return data; + } bool is_valid() const { @@ -469,17 +478,18 @@ class World } private: + T data; bool m_is_valid = false; }; void rebuild() { - m_local_populations_cache.data.clear(); + m_local_populations_cache.write().clear(); for (size_t i = 0; i < m_locations.size(); i++) { - m_local_populations_cache.data[m_locations[i].get_index()].clear(); + m_local_populations_cache.write()[m_locations[i].get_index()].clear(); } for (Person& person : get_persons()) { - m_local_populations_cache.data.at(person.get_location().index).emplace(person.get_person_id()); + m_local_populations_cache.write().at(person.get_location().index).emplace(person.get_person_id()); } } @@ -487,13 +497,13 @@ class World { for (Location& location : get_locations()) { auto index = location.get_index(); - m_air_exposure_rates_cache.data.at(index).array().setZero(); - m_contact_exposure_rates_cache.data.at(index).array().setZero(); + m_air_exposure_rates_cache.write().at(index).array().setZero(); + m_contact_exposure_rates_cache.write().at(index).array().setZero(); } for (Person& person : get_persons()) { auto location = person.get_location().index; - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.data.at(location), - m_contact_exposure_rates_cache.data.at(location), person, + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.write().at(location), + m_contact_exposure_rates_cache.write().at(location), person, get_location(person.get_person_id()), t, dt); } } From 54fac3ab3387c59b50f582b8c44dd4504ec90dda Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:04:37 +0100 Subject: [PATCH 19/54] add copyright comment --- cpp/models/abm/person_id.h | 20 ++++++++++++++++++++ cpp/models/abm/personal_rng.cpp | 20 ++++++++++++++++++++ cpp/models/abm/personal_rng.h | 20 ++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index 1f947b8c47..cb4dccc6ae 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -1,3 +1,23 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Rene Schmieding +* +* 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_PERSON_ID_H_ #define MIO_ABM_PERSON_ID_H_ diff --git a/cpp/models/abm/personal_rng.cpp b/cpp/models/abm/personal_rng.cpp index 37e93b5ca2..23c38c6b87 100644 --- a/cpp/models/abm/personal_rng.cpp +++ b/cpp/models/abm/personal_rng.cpp @@ -1,3 +1,23 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Rene Schmieding +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + #include "abm/personal_rng.h" #include "abm/person.h" diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h index 8bde28cdc8..23b7213e53 100644 --- a/cpp/models/abm/personal_rng.h +++ b/cpp/models/abm/personal_rng.h @@ -1,3 +1,23 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Rene Schmieding +* +* 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_PERSONAL_RNG_H_ #define MIO_ABM_PERSONAL_RNG_H_ From 3196a3366ebed41989abeacd06ef46a522ea1969 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:12:03 +0100 Subject: [PATCH 20/54] use atomics for caching. omp parallelisation works --- cpp/models/abm/caching.h | 126 +++++++++++++++++++++++++++++++++++ cpp/models/abm/functions.cpp | 41 ++++++++---- cpp/models/abm/functions.h | 33 +++++++-- cpp/models/abm/location.h | 10 +-- cpp/models/abm/world.cpp | 29 ++++---- cpp/models/abm/world.h | 119 ++++++++++++++------------------- 6 files changed, 250 insertions(+), 108 deletions(-) create mode 100644 cpp/models/abm/caching.h diff --git a/cpp/models/abm/caching.h b/cpp/models/abm/caching.h new file mode 100644 index 0000000000..5329da3ed0 --- /dev/null +++ b/cpp/models/abm/caching.h @@ -0,0 +1,126 @@ +#ifndef CACHING_H_ +#define CACHING_H_ + +#include "memilio/utils/custom_index_array.h" +#include "memilio/utils/index.h" + +// TODO: this implementation is not optimal + +namespace mio +{ + +template +struct Cache { + const T& read() const + { + return data; + } + + T& write() + { + return data; + } + + bool is_valid() const + { + return m_is_valid; + } + + void invalidate() + { + m_is_valid = false; + } + + void validate() + { + m_is_valid = true; + } + +private: + T data; + bool m_is_valid = false; +}; + +template +void setZero(CustomIndexArrayOfAtomics& x) +{ + for (Eigen::Index i = 0; i < x.array().size(); i++) { + x.array()[i].store(0); + } +} + +template +struct CopyableAtomic { + Atomic value; + CopyableAtomic() = default; + CopyableAtomic(const typename Atomic::value_type& v) + : value(v) + { + } + CopyableAtomic(const Atomic& other) + : value(other.load()) + { + } + CopyableAtomic(const CopyableAtomic& other) + : CopyableAtomic(other.value) + { + } + CopyableAtomic& operator=(const CopyableAtomic& other) + { + value.store(other.value.load()); + return *this; + } +}; + +template +class CustomAtomicIndexArray +{ + static_assert(Atomic::is_always_lock_free, "Atomic type must be lock free"); + +public: + using Index = mio::Index; + + CustomAtomicIndexArray(const Index& dimensions, typename Atomic::value_type fill) + : m_dimensions(dimensions) + , m_data(product(m_dimensions), fill) + { + // TODO: why is product not in mio? + } + + Atomic& operator[](const Index& i) + { + return m_data[mio::flatten_index(i, m_dimensions)].value; + } + + const Atomic& operator[](const Index& i) const + { + return m_data[mio::flatten_index(i, m_dimensions)].value; + } + + typename Atomic::value_type operator()(const Index& i) const + { + return m_data[mio::flatten_index(i, m_dimensions)].value.load(); + } + + template + mio::Index size() const + { + return get(m_dimensions); + } + + void setZero() // TODO: this is only well defined for FP atomics + { + for (auto& ca : m_data) { + ca.value.store(0.); + } + } + +private: + Index m_dimensions; + + std::vector> m_data; // TODO: try using eigen::matrix without copyable +}; + +} // namespace mio + +#endif // CACHING_H_ \ No newline at end of file diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index 54c5ea1e30..b3aafbcfa5 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -1,5 +1,24 @@ -#include "functions.h" - +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Rene Schmieding +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "abm/functions.h" #include "abm/location.h" #include "abm/person.h" #include "abm/random_events.h" @@ -15,7 +34,7 @@ namespace abm // TODO: daily_transmissions functions are only used in interact. expose in header anyways? -ScalarType daily_transmissions_by_contacts(const Location::ContactExposureRates& rates, const CellIndex cell_index, +ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const AgeGroup age_receiver, const LocalInfectionParameters& params) { @@ -28,16 +47,15 @@ ScalarType daily_transmissions_by_contacts(const Location::ContactExposureRates& return prob; } -ScalarType daily_transmissions_by_air(const Location::AirExposureRates& rates, const CellIndex cell_index, +ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const Parameters& global_params) { return rates[{cell_index, virus}] * global_params.get()[{virus}]; } void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, - const Location::AirExposureRates& local_air_exposure, - const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters) + const AirExposureRates& local_air_exposure, const ContactExposureRates& local_contact_exposure, + const TimePoint t, const TimeSpan dt, const Parameters& global_parameters) { // make sure all dimensions are set correctly and all indices are valid assert(location.get_cells().size() == local_air_exposure.size().get()); @@ -83,9 +101,8 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const person.add_time_at_location(dt); } -void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, - Location::ContactExposureRates& local_contact_exposure, const Person& person, - const Location& location, const TimePoint t, const TimeSpan dt) +void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExposureRates& local_contact_exposure, + const Person& person, const Location& location, const TimePoint t, const TimeSpan dt) { assert([&]() { if (person.get_location() != location.get_id()) { @@ -117,8 +134,8 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const const std::vector& local_population, const TimePoint t, const TimeSpan dt, const Parameters& global_parameters) { - Location::AirExposureRates local_air_exposure{{CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.}; - Location::ContactExposureRates local_contact_exposure{ + AirExposureRates local_air_exposure{{CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.}; + ContactExposureRates local_contact_exposure{ {CellIndex(location.get_cells().size()), VirusVariant::Count, AgeGroup(global_parameters.get_num_groups())}, 0.}; for (const Person& p : local_population) { diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h index 9d5f1005e7..97cfd1bbe7 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/functions.h @@ -1,10 +1,31 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Rene Schmieding +* +* 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 FUNCTIONS_H_ #define FUNCTIONS_H_ // TODO: find a meaningfull header name -#include "abm/person.h" #include "abm/location.h" +#include "abm/person.h" + #include namespace mio @@ -14,15 +35,13 @@ namespace abm { // add the contribution of person to the exposure rates at location -void add_exposure_contribution(Location::AirExposureRates& local_air_exposure, - Location::ContactExposureRates& local_contact_exposure, const Person& person, - const Location& location, const TimePoint t, const TimeSpan dt); +void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExposureRates& local_contact_exposure, + const Person& person, const Location& location, const TimePoint t, const TimeSpan dt); // let a person interact with a location for and at some time void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, - const Location::AirExposureRates& local_air_exposure, - const Location::ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters); + const AirExposureRates& local_air_exposure, const ContactExposureRates& local_contact_exposure, + const TimePoint t, const TimeSpan dt, const Parameters& global_parameters); // interact, but it computes the correct exposures for you void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 2389834d8b..a6a1acde98 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -20,11 +20,12 @@ #ifndef MIO_ABM_LOCATION_H #define MIO_ABM_LOCATION_H +#include "caching.h" #include "abm/mask_type.h" #include "abm/parameters.h" #include "abm/location_type.h" -#include "memilio/epidemiology/age_group.h" -#include "memilio/utils/custom_index_array.h" + +#include "boost/atomic/atomic.hpp" namespace mio { @@ -38,6 +39,9 @@ struct CellIndex : public mio::Index { } }; +using ContactExposureRates = CustomAtomicIndexArray, CellIndex, VirusVariant, AgeGroup>; +using AirExposureRates = CustomAtomicIndexArray, CellIndex, VirusVariant>; + /** * @brief CellCapacity describes the size of a Cell. * It consists of a volume and a capacity in Person%s which is an upper bound for the number @@ -82,8 +86,6 @@ struct Cell { class Location { public: - using ContactExposureRates = CustomIndexArray; - using AirExposureRates = CustomIndexArray; /** * @brief Construct a Location of a certain LocationId. * @param[in] loc_id The #LocationId. diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 19e2c61fca..1980df8a3e 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -37,14 +37,12 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) m_locations.emplace_back(id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; - if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.write()[id.index]; + if (m_local_population_size_cache.is_valid()) { + m_local_population_size_cache.write()[id.index].value = 0.; } - m_air_exposure_rates_cache.write().emplace( - id.index, Location::AirExposureRates({CellIndex(num_cells), VirusVariant::Count}, 0.)); - m_contact_exposure_rates_cache.write().emplace( - id.index, Location::ContactExposureRates( - {CellIndex(num_cells), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}, 0.)); + m_air_exposure_rates_cache.write().push_back({{CellIndex(num_cells), VirusVariant::Count}, 0.}); + m_contact_exposure_rates_cache.write().push_back( + {{CellIndex(num_cells), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}, 0.}); return id; } @@ -64,17 +62,18 @@ void World::evolve(TimePoint t, TimeSpan dt) void World::interaction(TimePoint t, TimeSpan dt) { + const auto num_persons = m_persons.size(); PRAGMA_OMP(parallel for) - for (auto i = size_t(0); i < m_persons.size(); ++i) { - // TODO: i == get_person(i).get_person_id(), but this does not have to stay true - interact(m_persons[i].get_person_id(), t, dt); + for (auto i = size_t(0); i < num_persons; ++i) { + interact(i, t, dt); } } void World::migration(TimePoint t, TimeSpan dt) { + const auto num_persons = m_persons.size(); PRAGMA_OMP(parallel for) - for (auto i = size_t(0); i < m_persons.size(); ++i) { + for (auto i = size_t(0); i < num_persons; ++i) { auto& person = m_persons[i]; auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); @@ -88,7 +87,7 @@ void World::migration(TimePoint t, TimeSpan dt) get_number_persons(target_location) < target_location.get_capacity().persons) { bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { - migrate(person.get_person_id(), target_location.get_id()); // TODO: i == PersonId, use? + migrate(i, target_location.get_id()); } return true; } @@ -148,9 +147,9 @@ void World::begin_step(TimePoint t, TimeSpan dt) { m_testing_strategy.update_activity_status(t); - if (!m_local_populations_cache.is_valid()) { - rebuild(); - m_local_populations_cache.validate(); + if (!m_local_population_size_cache.is_valid()) { + build_local_population_cache(); + m_local_population_size_cache.validate(); } recompute_exposure_rates(t, dt); m_air_exposure_rates_cache.validate(); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index d2f045ba68..8f4fab5f9e 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -20,7 +20,8 @@ #ifndef MIO_ABM_WORLD_H #define MIO_ABM_WORLD_H -#include "abm/config.h" +#include "abm/caching.h" +#include "abm/functions.h" #include "abm/location_type.h" #include "abm/movement_data.h" #include "abm/parameters.h" @@ -30,19 +31,14 @@ #include "abm/trip_list.h" #include "abm/random_events.h" #include "abm/testing_strategy.h" -#include "abm/virus_variant.h" #include "memilio/epidemiology/age_group.h" -#include "memilio/utils/custom_index_array.h" -#include "memilio/utils/index.h" +#include "memilio/utils/mioomp.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" -#include "functions.h" - +#include #include #include -#include -#include namespace mio { @@ -179,13 +175,13 @@ class World }()); // assert that location is in world assert(person.get_age().get() < parameters.get_num_groups()); - PersonId new_id = static_cast(m_persons.size()); // TODO: this breaks when removing a person + PersonId new_id = static_cast(m_persons.size()); m_persons.emplace_back(person, new_id); auto& new_person = m_persons.back(); new_person.set_assigned_location(m_cemetery_id); - if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.write().at(new_person.get_location().index).emplace(new_id); + if (m_local_population_size_cache.is_valid()) { + ++m_local_population_size_cache.write().at(new_person.get_location().index).value; } return new_id; } @@ -337,13 +333,13 @@ class World size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { - if (m_local_populations_cache.is_valid()) { - return std::count_if(m_local_populations_cache.read().at(location.index).begin(), - m_local_populations_cache.read().at(location.index).end(), [&](uint32_t p) { - return get_person(PersonId(p)).get_infection_state(t) == state; - }); - } - + // if (m_local_populations_cache.is_valid()) { + // return std::count_if(m_local_populations_cache.read().at(location.index).begin(), + // m_local_populations_cache.read().at(location.index).end(), [&](uint32_t p) { + // return get_person(PersonId(p)).get_infection_state(t) == state; + // }); + // } + // TODO: check performance on this. return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p.get_location() == location && p.get_infection_state(t) == state; }); @@ -356,8 +352,8 @@ class World size_t get_number_persons(LocationId location) const { - if (m_local_populations_cache.is_valid()) { - return m_local_populations_cache.read().at(location.index).size(); + if (m_local_population_size_cache.is_valid()) { + return m_local_population_size_cache.read().at(location.index).value; } return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p.get_location() == location; @@ -378,9 +374,9 @@ class World if (has_moved) { m_air_exposure_rates_cache.invalidate(); m_contact_exposure_rates_cache.invalidate(); - if (m_local_populations_cache.is_valid()) { - m_local_populations_cache.write().at(origin.index).erase(person.get()); - m_local_populations_cache.write().at(destination.index).emplace(person.get()); + if (m_local_population_size_cache.is_valid()) { + --m_local_population_size_cache.write().at(origin.index).value; + ++m_local_population_size_cache.write().at(destination.index).value; } } } @@ -449,69 +445,52 @@ class World */ void migration(TimePoint t, TimeSpan dt); - template - struct Cache { - - const T& read() const - { - return data; - } - - T& write() - { - return data; - } - - bool is_valid() const - { - return m_is_valid; - } - - void invalidate() - { - m_is_valid = false; - } - - void validate() - { - m_is_valid = true; - } - - private: - T data; - bool m_is_valid = false; - }; - - void rebuild() + void build_local_population_cache() { - m_local_populations_cache.write().clear(); + m_local_population_size_cache.write().resize(m_locations.size()); + // this loop (in partikular map[]) cannot run in parallel for (size_t i = 0; i < m_locations.size(); i++) { - m_local_populations_cache.write()[m_locations[i].get_index()].clear(); + m_local_population_size_cache.write()[i].value = 0.; } + PRAGMA_OMP(parallel for) for (Person& person : get_persons()) { - m_local_populations_cache.write().at(person.get_location().index).emplace(person.get_person_id()); + ++m_local_population_size_cache.write().at(person.get_location().index).value; } } void recompute_exposure_rates(TimePoint t, TimeSpan dt) { - for (Location& location : get_locations()) { - auto index = location.get_index(); - m_air_exposure_rates_cache.write().at(index).array().setZero(); - m_contact_exposure_rates_cache.write().at(index).array().setZero(); + // use these const values to help omp recognize that the for loops are bounded + const auto num_locations = m_locations.size(); + const auto num_persons = m_persons.size(); + + // 1) reset all cached values + // Note: we cannot easily reuse values, as they are time dependant (get_infection_state) + PRAGMA_OMP(parallel for) + for (size_t i = 0; i < num_locations; ++i) { + const auto index = i; + m_air_exposure_rates_cache.write().at(index).setZero(); + m_contact_exposure_rates_cache.write().at(index).setZero(); } - for (Person& person : get_persons()) { - auto location = person.get_location().index; + // here is an implicit (and needed) barrier from parallel for + + // 2) add all contributions from each person + PRAGMA_OMP(parallel for) + for (size_t i = 0; i < num_persons; ++i) { + const Person& person = m_persons[i]; + const auto location = person.get_location().index; mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.write().at(location), m_contact_exposure_rates_cache.write().at(location), person, get_location(person.get_person_id()), t, dt); } } - Cache>> - m_local_populations_cache; // TODO: change this to storing only (sub)population(s) in numbers, using atomic ints - Cache> m_air_exposure_rates_cache; - Cache> m_contact_exposure_rates_cache; + Cache>> + m_local_population_size_cache; ///< Current number of Persons in a given location. + Cache> + m_air_exposure_rates_cache; ///< Cache for local exposure through droplets in #transmissions/day. + Cache> + m_contact_exposure_rates_cache; ///< Cache for local exposure through contacts in #transmissions/day. std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. From 43294fcb855794dfa4441d60dc1e8391e75ea51c Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:20:39 +0100 Subject: [PATCH 21/54] caches now use CustemIndexArray and Eigen::Vector. This allows deleting some duplicate code or workarounds, but makes handling caches more difficult. World now needs an explicit copy constructor, and resizing or setting caches to zero is cumbersome --- cpp/memilio/utils/custom_index_array.h | 14 +++-- cpp/models/abm/caching.h | 87 +------------------------- cpp/models/abm/functions.cpp | 24 +++++-- cpp/models/abm/location.h | 5 +- cpp/models/abm/migration_rules.cpp | 5 -- cpp/models/abm/world.cpp | 30 +++++++-- cpp/models/abm/world.h | 74 ++++++++++++++++------ 7 files changed, 113 insertions(+), 126 deletions(-) diff --git a/cpp/memilio/utils/custom_index_array.h b/cpp/memilio/utils/custom_index_array.h index 57f15b5c9d..64bc7f93a4 100644 --- a/cpp/memilio/utils/custom_index_array.h +++ b/cpp/memilio/utils/custom_index_array.h @@ -20,16 +20,10 @@ #ifndef CUSTOMINDEXARRAY_H #define CUSTOMINDEXARRAY_H -#include "memilio/config.h" -#include "memilio/math/eigen.h" #include "memilio/math/eigen_util.h" #include "memilio/utils/index.h" #include "memilio/utils/stl_util.h" -#include -#include -#include - namespace { @@ -165,6 +159,14 @@ class CustomIndexArray using Index = ::mio::Index; using InternalArrayType = Eigen::Array; + /// @brief Create an empty CustomIndexArray with size 0. Use the resize member function to add entries. + explicit CustomIndexArray() + : m_dimensions(Index::Zero()) + , m_numel(0) + , m_y() + { + } + /** * @brief CustomIndexArray constructor, that initializes the array * to constant instances of `CustsomIndexArray::Type`. diff --git a/cpp/models/abm/caching.h b/cpp/models/abm/caching.h index 5329da3ed0..08e6df3020 100644 --- a/cpp/models/abm/caching.h +++ b/cpp/models/abm/caching.h @@ -1,21 +1,18 @@ #ifndef CACHING_H_ #define CACHING_H_ -#include "memilio/utils/custom_index_array.h" -#include "memilio/utils/index.h" - -// TODO: this implementation is not optimal - namespace mio { template struct Cache { + // const access to the cached data const T& read() const { return data; } + // access to cached data - only intended for writing, use read() to access data T& write() { return data; @@ -41,86 +38,6 @@ struct Cache { bool m_is_valid = false; }; -template -void setZero(CustomIndexArrayOfAtomics& x) -{ - for (Eigen::Index i = 0; i < x.array().size(); i++) { - x.array()[i].store(0); - } -} - -template -struct CopyableAtomic { - Atomic value; - CopyableAtomic() = default; - CopyableAtomic(const typename Atomic::value_type& v) - : value(v) - { - } - CopyableAtomic(const Atomic& other) - : value(other.load()) - { - } - CopyableAtomic(const CopyableAtomic& other) - : CopyableAtomic(other.value) - { - } - CopyableAtomic& operator=(const CopyableAtomic& other) - { - value.store(other.value.load()); - return *this; - } -}; - -template -class CustomAtomicIndexArray -{ - static_assert(Atomic::is_always_lock_free, "Atomic type must be lock free"); - -public: - using Index = mio::Index; - - CustomAtomicIndexArray(const Index& dimensions, typename Atomic::value_type fill) - : m_dimensions(dimensions) - , m_data(product(m_dimensions), fill) - { - // TODO: why is product not in mio? - } - - Atomic& operator[](const Index& i) - { - return m_data[mio::flatten_index(i, m_dimensions)].value; - } - - const Atomic& operator[](const Index& i) const - { - return m_data[mio::flatten_index(i, m_dimensions)].value; - } - - typename Atomic::value_type operator()(const Index& i) const - { - return m_data[mio::flatten_index(i, m_dimensions)].value.load(); - } - - template - mio::Index size() const - { - return get(m_dimensions); - } - - void setZero() // TODO: this is only well defined for FP atomics - { - for (auto& ca : m_data) { - ca.value.store(0.); - } - } - -private: - Index m_dimensions; - - std::vector> m_data; // TODO: try using eigen::matrix without copyable -}; - } // namespace mio #endif // CACHING_H_ \ No newline at end of file diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index b3aafbcfa5..581b5998b0 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -34,6 +34,7 @@ namespace abm // TODO: daily_transmissions functions are only used in interact. expose in header anyways? +// compute the local contact exposure rate ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const AgeGroup age_receiver, const LocalInfectionParameters& params) @@ -47,12 +48,14 @@ ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, co return prob; } +// compute the local air exposure rate ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const Parameters& global_params) { return rates[{cell_index, virus}] * global_params.get()[{virus}]; } +// let a person interact with a local population void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, const AirExposureRates& local_air_exposure, const ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, const Parameters& global_parameters) @@ -101,6 +104,7 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const person.add_time_at_location(dt); } +// for the given person and time span, add its exposure contributions at the given location and time void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExposureRates& local_contact_exposure, const Person& person, const Location& location, const TimePoint t, const TimeSpan dt) { @@ -130,17 +134,29 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo } } +// compatability layer for using interact without existing caches void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, const std::vector& local_population, const TimePoint t, const TimeSpan dt, const Parameters& global_parameters) { - AirExposureRates local_air_exposure{{CellIndex(location.get_cells().size()), VirusVariant::Count}, 0.}; - ContactExposureRates local_contact_exposure{ - {CellIndex(location.get_cells().size()), VirusVariant::Count, AgeGroup(global_parameters.get_num_groups())}, - 0.}; + // allocate and initialize air exposures with 0 + AirExposureRates local_air_exposure; + local_air_exposure.resize({CellIndex(location.get_cells().size()), VirusVariant::Count}); + std::for_each(local_air_exposure.begin(), local_air_exposure.end(), [](auto& r) { + r = 0.0; + }); + // allocate and initialize contact exposures with 0 + ContactExposureRates local_contact_exposure; + local_contact_exposure.resize( + {CellIndex(location.get_cells().size()), VirusVariant::Count, AgeGroup(global_parameters.get_num_groups())}); + std::for_each(local_contact_exposure.begin(), local_contact_exposure.end(), [](auto& r) { + r = 0.0; + }); + // caclculate current exposures for (const Person& p : local_population) { add_exposure_contribution(local_air_exposure, local_contact_exposure, p, location, t, dt); } + // run interaction interact(personal_rng, person, location, local_air_exposure, local_contact_exposure, t, dt, global_parameters); } diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index a6a1acde98..cca3e44864 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -20,7 +20,6 @@ #ifndef MIO_ABM_LOCATION_H #define MIO_ABM_LOCATION_H -#include "caching.h" #include "abm/mask_type.h" #include "abm/parameters.h" #include "abm/location_type.h" @@ -39,8 +38,8 @@ struct CellIndex : public mio::Index { } }; -using ContactExposureRates = CustomAtomicIndexArray, CellIndex, VirusVariant, AgeGroup>; -using AirExposureRates = CustomAtomicIndexArray, CellIndex, VirusVariant>; +using ContactExposureRates = CustomIndexArray, CellIndex, VirusVariant, AgeGroup>; +using AirExposureRates = CustomIndexArray, CellIndex, VirusVariant>; /** * @brief CellCapacity describes the size of a Cell. diff --git a/cpp/models/abm/migration_rules.cpp b/cpp/models/abm/migration_rules.cpp index 9212dfa7f8..042af98449 100644 --- a/cpp/models/abm/migration_rules.cpp +++ b/cpp/models/abm/migration_rules.cpp @@ -19,14 +19,9 @@ */ #include "abm/migration_rules.h" #include "abm/person.h" -#include "abm/location.h" #include "abm/random_events.h" -#include "abm/location.h" -#include "memilio/utils/random_number_generator.h" #include "abm/location_type.h" -#include - namespace mio { namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 1980df8a3e..6ee5e78aac 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -37,12 +37,32 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) m_locations.emplace_back(id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; - if (m_local_population_size_cache.is_valid()) { - m_local_population_size_cache.write()[id.index].value = 0.; + // rebuild this on demand // TODO: do this with the other caches too? maybe split the concept valid into valid values / valid shape + m_local_population_size_cache.invalidate(); + // resize air exposures cache + std::vector air_dims(id.index, AirExposureRates::Index::Zero()); + for (size_t i = 0; i < air_dims.size(); i++) { + air_dims[i] = m_air_exposure_rates_cache.read()[i].size(); } - m_air_exposure_rates_cache.write().push_back({{CellIndex(num_cells), VirusVariant::Count}, 0.}); - m_contact_exposure_rates_cache.write().push_back( - {{CellIndex(num_cells), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}, 0.}); + m_air_exposure_rates_cache.write().resize(id.index + 1); + for (size_t i = 0; i < air_dims.size(); i++) { + m_air_exposure_rates_cache.write()[i].resize(air_dims[i]); + } + m_air_exposure_rates_cache.write()[id.index].resize({CellIndex(num_cells), VirusVariant::Count}); + m_air_exposure_rates_cache.invalidate(); + // resize contact exposures cache + std::vector contact_dims(id.index, ContactExposureRates::Index::Zero()); + for (size_t i = 0; i < contact_dims.size(); i++) { + contact_dims[i] = m_contact_exposure_rates_cache.read()[i].size(); + } + m_contact_exposure_rates_cache.write().resize(id.index + 1); + for (size_t i = 0; i < contact_dims.size(); i++) { + m_contact_exposure_rates_cache.write()[i].resize(contact_dims[i]); + } + m_contact_exposure_rates_cache.write()[id.index].resize( + {CellIndex(num_cells), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}); + m_contact_exposure_rates_cache.invalidate(); + return id; } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 8f4fab5f9e..a463923209 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -84,8 +84,40 @@ class World } //type is move-only for stable references of persons/locations - World(const World&) = default; - World(World&& other) = default; + World(const World& other) + : parameters(other.parameters) + , m_local_population_size_cache() + , m_air_exposure_rates_cache() + , m_contact_exposure_rates_cache() + , m_persons(other.m_persons) + , m_locations(other.m_locations) + , m_has_locations(other.m_has_locations) + , m_testing_strategy(other.m_testing_strategy) + , m_trip_list(other.m_trip_list) + , m_use_migration_rules(other.m_use_migration_rules) + , m_migration_rules(other.m_migration_rules) + , m_cemetery_id(other.m_cemetery_id) + , m_rng(other.m_rng) + { + m_air_exposure_rates_cache.write().resize(other.m_air_exposure_rates_cache.read().size()); + for (Eigen::Index i = 0; i < other.m_air_exposure_rates_cache.read().size(); ++i) { + m_air_exposure_rates_cache.write()[i].resize(other.m_air_exposure_rates_cache.read()[i].size()); + std::for_each(m_air_exposure_rates_cache.write()[i].begin(), m_air_exposure_rates_cache.write()[i].end(), + [](auto& r) { + r = 0.0; + }); + } + + m_contact_exposure_rates_cache.write().resize(other.m_contact_exposure_rates_cache.read().size()); + for (Eigen::Index i = 0; i < other.m_contact_exposure_rates_cache.read().size(); ++i) { + m_contact_exposure_rates_cache.write()[i].resize(other.m_contact_exposure_rates_cache.read()[i].size()); + std::for_each(m_contact_exposure_rates_cache.write()[i].begin(), + m_contact_exposure_rates_cache.write()[i].end(), [](auto& r) { + r = 0.0; + }); + } + } + World(World&& other) = default; // TODO? World& operator=(World&& other) = default; World& operator=(const World&) = delete; @@ -181,7 +213,7 @@ class World new_person.set_assigned_location(m_cemetery_id); if (m_local_population_size_cache.is_valid()) { - ++m_local_population_size_cache.write().at(new_person.get_location().index).value; + ++m_local_population_size_cache.write()[new_person.get_location().index]; } return new_id; } @@ -353,7 +385,7 @@ class World size_t get_number_persons(LocationId location) const { if (m_local_population_size_cache.is_valid()) { - return m_local_population_size_cache.read().at(location.index).value; + return m_local_population_size_cache.read()[location.index]; } return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p.get_location() == location; @@ -375,8 +407,8 @@ class World m_air_exposure_rates_cache.invalidate(); m_contact_exposure_rates_cache.invalidate(); if (m_local_population_size_cache.is_valid()) { - --m_local_population_size_cache.write().at(origin.index).value; - ++m_local_population_size_cache.write().at(destination.index).value; + --m_local_population_size_cache.write()[origin.index]; + ++m_local_population_size_cache.write()[destination.index]; } } } @@ -398,8 +430,8 @@ class World m_contact_exposure_rates_cache.validate(); } mio::abm::interact(personal_rng, get_person(person), get_location(person), - m_air_exposure_rates_cache.read().at(get_location(person).get_index()), - m_contact_exposure_rates_cache.read().at(get_location(person).get_index()), t, dt, + m_air_exposure_rates_cache.read()[get_location(person).get_index()], + m_contact_exposure_rates_cache.read()[get_location(person).get_index()], t, dt, global_parameters); } @@ -450,11 +482,11 @@ class World m_local_population_size_cache.write().resize(m_locations.size()); // this loop (in partikular map[]) cannot run in parallel for (size_t i = 0; i < m_locations.size(); i++) { - m_local_population_size_cache.write()[i].value = 0.; + m_local_population_size_cache.write()[i] = 0.; } PRAGMA_OMP(parallel for) for (Person& person : get_persons()) { - ++m_local_population_size_cache.write().at(person.get_location().index).value; + ++m_local_population_size_cache.write()[person.get_location().index]; } } @@ -468,9 +500,15 @@ class World // Note: we cannot easily reuse values, as they are time dependant (get_infection_state) PRAGMA_OMP(parallel for) for (size_t i = 0; i < num_locations; ++i) { - const auto index = i; - m_air_exposure_rates_cache.write().at(index).setZero(); - m_contact_exposure_rates_cache.write().at(index).setZero(); + const auto index = i; + auto& local_air_exposure = m_air_exposure_rates_cache.write()[index]; + std::for_each(local_air_exposure.begin(), local_air_exposure.end(), [](auto& r) { + r = 0.0; + }); + auto& local_contact_exposure = m_contact_exposure_rates_cache.write()[index]; + std::for_each(local_contact_exposure.begin(), local_contact_exposure.end(), [](auto& r) { + r = 0.0; + }); } // here is an implicit (and needed) barrier from parallel for @@ -479,17 +517,17 @@ class World for (size_t i = 0; i < num_persons; ++i) { const Person& person = m_persons[i]; const auto location = person.get_location().index; - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.write().at(location), - m_contact_exposure_rates_cache.write().at(location), person, + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.write()[location], + m_contact_exposure_rates_cache.write()[location], person, get_location(person.get_person_id()), t, dt); } } - Cache>> + Cache> m_local_population_size_cache; ///< Current number of Persons in a given location. - Cache> + Cache> m_air_exposure_rates_cache; ///< Cache for local exposure through droplets in #transmissions/day. - Cache> + Cache> m_contact_exposure_rates_cache; ///< Cache for local exposure through contacts in #transmissions/day. std::vector m_persons; ///< Vector of every Person. From 30d1ddf434eac965327b54f07cb51b43ab20f499 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:35:54 +0100 Subject: [PATCH 22/54] only rebuild exposure caches on demand. this saves a lot of time wasted by add_location --- cpp/models/abm/world.cpp | 24 ++---------------------- cpp/models/abm/world.h | 39 ++++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 39 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 6ee5e78aac..bb15a59f67 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -37,31 +37,11 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) m_locations.emplace_back(id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; - // rebuild this on demand // TODO: do this with the other caches too? maybe split the concept valid into valid values / valid shape + // mark caches for rebuild m_local_population_size_cache.invalidate(); - // resize air exposures cache - std::vector air_dims(id.index, AirExposureRates::Index::Zero()); - for (size_t i = 0; i < air_dims.size(); i++) { - air_dims[i] = m_air_exposure_rates_cache.read()[i].size(); - } - m_air_exposure_rates_cache.write().resize(id.index + 1); - for (size_t i = 0; i < air_dims.size(); i++) { - m_air_exposure_rates_cache.write()[i].resize(air_dims[i]); - } - m_air_exposure_rates_cache.write()[id.index].resize({CellIndex(num_cells), VirusVariant::Count}); m_air_exposure_rates_cache.invalidate(); - // resize contact exposures cache - std::vector contact_dims(id.index, ContactExposureRates::Index::Zero()); - for (size_t i = 0; i < contact_dims.size(); i++) { - contact_dims[i] = m_contact_exposure_rates_cache.read()[i].size(); - } - m_contact_exposure_rates_cache.write().resize(id.index + 1); - for (size_t i = 0; i < contact_dims.size(); i++) { - m_contact_exposure_rates_cache.write()[i].resize(contact_dims[i]); - } - m_contact_exposure_rates_cache.write()[id.index].resize( - {CellIndex(num_cells), VirusVariant::Count, AgeGroup(parameters.get_num_groups())}); m_contact_exposure_rates_cache.invalidate(); + m_exposure_rates_need_rebuild = true; return id; } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index a463923209..5070c6d146 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -89,6 +89,7 @@ class World , m_local_population_size_cache() , m_air_exposure_rates_cache() , m_contact_exposure_rates_cache() + , m_exposure_rates_need_rebuild(true) , m_persons(other.m_persons) , m_locations(other.m_locations) , m_has_locations(other.m_has_locations) @@ -99,23 +100,6 @@ class World , m_cemetery_id(other.m_cemetery_id) , m_rng(other.m_rng) { - m_air_exposure_rates_cache.write().resize(other.m_air_exposure_rates_cache.read().size()); - for (Eigen::Index i = 0; i < other.m_air_exposure_rates_cache.read().size(); ++i) { - m_air_exposure_rates_cache.write()[i].resize(other.m_air_exposure_rates_cache.read()[i].size()); - std::for_each(m_air_exposure_rates_cache.write()[i].begin(), m_air_exposure_rates_cache.write()[i].end(), - [](auto& r) { - r = 0.0; - }); - } - - m_contact_exposure_rates_cache.write().resize(other.m_contact_exposure_rates_cache.read().size()); - for (Eigen::Index i = 0; i < other.m_contact_exposure_rates_cache.read().size(); ++i) { - m_contact_exposure_rates_cache.write()[i].resize(other.m_contact_exposure_rates_cache.read()[i].size()); - std::for_each(m_contact_exposure_rates_cache.write()[i].begin(), - m_contact_exposure_rates_cache.write()[i].end(), [](auto& r) { - r = 0.0; - }); - } } World(World&& other) = default; // TODO? World& operator=(World&& other) = default; @@ -490,8 +474,28 @@ class World } } + void build_exposure_caches() + { + const size_t num_locations = m_locations.size(); + m_air_exposure_rates_cache.write().resize(num_locations); + m_contact_exposure_rates_cache.write().resize(num_locations); + for (size_t i = 0; i < num_locations; i++) { + m_air_exposure_rates_cache.write()[i].resize( + {CellIndex(m_locations[i].get_cells().size()), VirusVariant::Count}); + m_contact_exposure_rates_cache.write()[i].resize({CellIndex(m_locations[i].get_cells().size()), + VirusVariant::Count, + AgeGroup(parameters.get_num_groups())}); + } + m_air_exposure_rates_cache.invalidate(); + m_contact_exposure_rates_cache.invalidate(); + m_exposure_rates_need_rebuild = false; + } + void recompute_exposure_rates(TimePoint t, TimeSpan dt) { + if (m_exposure_rates_need_rebuild) { + build_exposure_caches(); + } // use these const values to help omp recognize that the for loops are bounded const auto num_locations = m_locations.size(); const auto num_persons = m_persons.size(); @@ -529,6 +533,7 @@ class World m_air_exposure_rates_cache; ///< Cache for local exposure through droplets in #transmissions/day. Cache> m_contact_exposure_rates_cache; ///< Cache for local exposure through contacts in #transmissions/day. + bool m_exposure_rates_need_rebuild = true; std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. From 505002236b596cc4adc6b56c56df79531f02b032 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:13:27 +0100 Subject: [PATCH 23/54] clean up. comments and interface changes. Improved thread safety of caching in World. Added several comments, moved some functions to .cpp files, reduced number of Location/Person references used in favor of IDs. Removed/renamed get_individualized_location in favor of get_location. --- cpp/examples/abm_history_object.cpp | 12 +- cpp/examples/abm_minimal.cpp | 14 +- cpp/models/abm/caching.h | 2 +- cpp/models/abm/common_abm_loggers.h | 12 +- cpp/models/abm/functions.cpp | 33 ++-- cpp/models/abm/functions.h | 43 ++++-- cpp/models/abm/household.cpp | 5 +- cpp/models/abm/location.h | 36 +---- cpp/models/abm/person.cpp | 18 +-- cpp/models/abm/person.h | 45 +++--- cpp/models/abm/world.cpp | 135 ++++++++++++++--- cpp/models/abm/world.h | 201 ++++++++----------------- cpp/simulations/abm.cpp | 40 ++--- cpp/simulations/abm_braunschweig.cpp | 15 +- cpp/tests/abm_helpers.cpp | 2 +- cpp/tests/test_abm_infection.cpp | 2 +- cpp/tests/test_abm_location.cpp | 4 +- cpp/tests/test_abm_lockdown_rules.cpp | 48 +++--- cpp/tests/test_abm_masks.cpp | 12 +- cpp/tests/test_abm_migration_rules.cpp | 40 ++--- cpp/tests/test_abm_person.cpp | 20 +-- cpp/tests/test_abm_world.cpp | 162 ++++++++++---------- cpp/tests/test_json_serializer.cpp | 2 +- 23 files changed, 463 insertions(+), 440 deletions(-) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index d310e1fd55..ce4de646a7 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -104,21 +104,21 @@ 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 = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(5); + world.get_location(event).get_infection_parameters().set(5); // Add hospital and ICU with 5 maximum contacs. auto hospital = world.add_location(mio::abm::LocationType::Hospital); - world.get_individualized_location(hospital).get_infection_parameters().set(5); + world.get_location(hospital).get_infection_parameters().set(5); auto icu = world.add_location(mio::abm::LocationType::ICU); - world.get_individualized_location(icu).get_infection_parameters().set(5); + world.get_location(icu).get_infection_parameters().set(5); // Add one supermarket, maximum constacts are assumed to be 20. auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); + world.get_location(shop).get_infection_parameters().set(20); // At every school, the maximum contacts are 20. auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(20); + world.get_location(school).get_infection_parameters().set(20); // At every workplace, maximum contacts are 10. auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(10); + world.get_location(work).get_infection_parameters().set(10); // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. auto testing_min_time = mio::abm::days(1); diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index e53fc1dd08..e9915a6811 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -82,26 +82,26 @@ 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 = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(5); + world.get_location(event).get_infection_parameters().set(5); // Add hospital and ICU with 5 maximum contacs. auto hospital = world.add_location(mio::abm::LocationType::Hospital); - world.get_individualized_location(hospital).get_infection_parameters().set(5); + world.get_location(hospital).get_infection_parameters().set(5); auto icu = world.add_location(mio::abm::LocationType::ICU); - world.get_individualized_location(icu).get_infection_parameters().set(5); + world.get_location(icu).get_infection_parameters().set(5); // Add one supermarket, maximum constacts are assumed to be 20. auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); + world.get_location(shop).get_infection_parameters().set(20); // At every school, the maximum contacts are 20. auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(20); + world.get_location(school).get_infection_parameters().set(20); // At every workplace, maximum contacts are 20. auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(20); + world.get_location(work).get_infection_parameters().set(20); // Increase aerosol transmission for all locations world.parameters.get() = 10.0; // Increase contact rate for all people between 15 and 34 (i.e. people meet more often in the same location) - world.get_individualized_location(work) + world.get_location(work) .get_infection_parameters() .get()[{age_group_15_to_34, age_group_15_to_34}] = 10.0; diff --git a/cpp/models/abm/caching.h b/cpp/models/abm/caching.h index 08e6df3020..97d1ba9abe 100644 --- a/cpp/models/abm/caching.h +++ b/cpp/models/abm/caching.h @@ -40,4 +40,4 @@ struct Cache { } // namespace mio -#endif // CACHING_H_ \ No newline at end of file +#endif // CACHING_H_ diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 561762f2b6..d956f7dc82 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -51,7 +51,7 @@ struct movement_data { mio::abm::InfectionState infection_state; }; -mio::abm::ActivityType guess_activity_type(mio::abm::LocationType current_location) +constexpr mio::abm::ActivityType guess_activity_type(mio::abm::LocationType current_location) { switch (current_location) { case mio::abm::LocationType::Home: @@ -123,9 +123,11 @@ struct LogPersonInformation : mio::LogOnce { static Type log(const mio::abm::Simulation& sim) { Type person_information{}; - for (auto&& person : sim.get_world().get_persons()) { + person_information.reserve(sim.get_world().get_persons().size()); + for (auto& person : sim.get_world().get_persons()) { person_information.push_back(std::make_tuple( - person.get_person_id(), sim.get_world().find_location(mio::abm::LocationType::Home, person).get_index(), + person.get_person_id(), + sim.get_world().find_location(mio::abm::LocationType::Home, person.get_person_id()).get_index(), person.get_age())); } return person_information; @@ -179,8 +181,8 @@ struct LogInfectionState : mio::LogAlways { PRAGMA_OMP(for) for (auto& location : sim.get_world().get_locations()) { for (uint32_t inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { - sum[inf_state] += - sim.get_world().get_subpopulation(location, curr_time, mio::abm::InfectionState(inf_state)); + sum[inf_state] += sim.get_world().get_subpopulation(location.get_id(), curr_time, + mio::abm::InfectionState(inf_state)); } } return std::make_pair(curr_time, sum); diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index 581b5998b0..c8f1300994 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -28,13 +28,20 @@ namespace mio { - namespace abm { // TODO: daily_transmissions functions are only used in interact. expose in header anyways? -// compute the local contact exposure rate +/** + * @brief Compute the number of daily transmissions for contact transmission of a virus in a cell. + * @param[in] rates The local exposure rates. + * @param[in] cell_index Cell index of the Cell. + * @param[in] virus VirusVariant of interest. + * @param[in] age_receiver AgeGroup of the receiving Person. + * @param[in] params The local infection parameters. + * @return Average amount of Infection%s with the virus from the AgeGroup of the transmitter per day. + */ ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const AgeGroup age_receiver, const LocalInfectionParameters& params) @@ -48,14 +55,20 @@ ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, co return prob; } -// compute the local air exposure rate +/** + * @brief Compute the number of daily transmissions for aerosol transmission of a virus in a cell. + * @param[in] rates The local exposure rates. + * @param[in] cell_index Cell index of the Cell. + * @param[in] virus VirusVariant of interest. + * @param[in] global_params The parameter set of the World. + * @return Average amount of Infection%s with the virus per day. + */ ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const Parameters& global_params) { return rates[{cell_index, virus}] * global_params.get()[{virus}]; } -// let a person interact with a local population void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, const AirExposureRates& local_air_exposure, const ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, const Parameters& global_parameters) @@ -104,7 +117,6 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const person.add_time_at_location(dt); } -// for the given person and time span, add its exposure contributions at the given location and time void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExposureRates& local_contact_exposure, const Person& person, const Location& location, const TimePoint t, const TimeSpan dt) { @@ -135,9 +147,9 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo } // compatability layer for using interact without existing caches -void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, - const std::vector& local_population, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters) +void interact_testing(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, + const std::vector& local_population, const TimePoint t, const TimeSpan dt, + const Parameters& global_parameters) { // allocate and initialize air exposures with 0 AirExposureRates local_air_exposure; @@ -167,7 +179,7 @@ bool migrate(Person& person, const Location& destination, const std::vector& local_population, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters); +void interact_testing(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, + const std::vector& local_population, const TimePoint t, const TimeSpan dt, + const Parameters& global_parameters); -// move a person to another location. returns false if the person was at the target location already, true otherwise +/** + * @brief Move a person to another location. + * If the person already is at the destination, neither mode nor cells are set. + * @param[in, out] person The person to be moved. + * @param[in] destination The destination to move to. + * @param[in] cells The cells within the destination the person should be in. + * @param[in] mode The transport mode the person uses to move. + * @return Returns false if the person already is at the given destination, true otherwise. + */ bool migrate(Person& person, const Location& destination, const std::vector& cells = {0}, const TransportMode mode = TransportMode::Unknown); -} // namespace abm +} // namespace abm } // namespace mio -#endif \ No newline at end of file +#endif diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index 83bca30052..92941563b1 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -60,9 +60,8 @@ void add_household_to_world(World& world, const Household& household) { auto home = world.add_location(LocationType::Home); auto members = household.get_members(); - world.get_individualized_location(home).set_capacity(household.get_total_number_of_members(), - household.get_total_number_of_members() * - household.get_space_per_member()); + world.get_location(home).set_capacity(household.get_total_number_of_members(), + household.get_total_number_of_members() * household.get_space_per_member()); for (auto& memberTouple : members) { int count; diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index cca3e44864..705267ad46 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -153,38 +153,10 @@ class Location return m_id.index; } - /** - * @brief Compute the transmission factor for contact transmission of the virus in a Cell. - * @param[in] cell_index Cell index of the Cell. - * @param[in] virus VirusVariant of interest. - * @param[in] age_receiver AgeGroup of the receiving Person. - * @param[in] age_transmitter AgeGroup of the transmitting Person. - * @param[in] num_agegroups The number of age groups in the model. - * @return Amount of average Infection%s with the virus from the AgeGroup of the transmitter per day. - */ - // ScalarType transmission_contacts_per_day(uint32_t cell_index, VirusVariant virus, AgeGroup age_receiver, - // size_t num_agegroups) const; - - /** - * @brief Compute the transmission factor for a aerosol transmission of the virus in a Cell. - * @param[in] cell_index Cell index of the Cell. - * @param[in] virus VirusVariant of interest. - * @param[in] global_params The Parameters set of the World. - * @return Amount of average Infection%s with the virus per day. - */ - // ScalarType transmission_air_per_day(uint32_t cell_index, VirusVariant virus, const Parameters& global_params) const; - - /** - * @brief Prepare the Location for the next Simulation step. - * @param[in] t Current TimePoint of the Simulation. - * @param[in] dt The duration of the Simulation step. - * @param[in] num_agegroups The number of age groups in the model. - */ - // void cache_exposure_rates(TimePoint t, TimeSpan dt, size_t num_agegroups); - /** * @brief Get the Location specific Infection parameters. * @return Parameters of the Infection that are specific to this Location. + * @{ */ LocalInfectionParameters& get_infection_parameters() { @@ -195,6 +167,7 @@ class Location { return m_parameters; } + /** @} */ /** * @brief Get the Cell%s of this Location. @@ -314,7 +287,10 @@ class Location m_geographical_location = location; } - // return id by value. used to identify a location in a World + /** + * @brief Get the location's identifier in a World. + * @return The location's LocationId by value. + */ LocationId get_id() const { return m_id; diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 1d4cfa8ff5..af86249042 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -31,8 +31,8 @@ namespace mio namespace abm { -Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, PersonId person_id) - : m_location(location.get_id()) +Person::Person(mio::RandomNumberGenerator& rng, LocationId location, AgeGroup age, PersonId person_id) + : m_location(location) , m_assigned_locations((uint32_t)LocationType::Count, INVALID_LOCATION_INDEX) , m_quarantine_start(TimePoint(-(std::numeric_limits::max() / 2))) , m_age(age) @@ -57,7 +57,7 @@ Person::Person(const Person& other, PersonId id) m_person_id = id; } -Person Person::copy_person(Location& location) +Person Person::copy_person(LocationId location) { Person copy = *this; copy.set_location(location); @@ -97,11 +97,6 @@ LocationId Person::get_location() const return m_location; } -void Person::set_location(const Location& location) -{ - set_location(location.get_id()); -} - void Person::set_location(LocationId id) { m_location = id; @@ -118,7 +113,7 @@ Infection& Person::get_infection() return m_infections.back(); } -void Person::set_assigned_location(Location& location) +void Person::set_assigned_location(LocationId location) { /* TODO: This is not safe if the location is not the same as added in the world, e.g. the index is wrong. We need to check this. * For now only use it like this: auto home_id = world.add_location(mio::abm::LocationType::Home); @@ -127,11 +122,6 @@ void Person::set_assigned_location(Location& location) m_assigned_locations[(uint32_t)location.get_type()] = location.get_index(); } -void Person::set_assigned_location(LocationId id) -{ - m_assigned_locations[(uint32_t)id.type] = id.index; -} - uint32_t Person::get_assigned_location_index(LocationType type) const { return m_assigned_locations[(uint32_t)type]; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 9c674f4fae..d06690e662 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -23,6 +23,7 @@ #include "abm/infection.h" #include "abm/infection_state.h" #include "abm/location_type.h" +#include "abm/location.h" #include "abm/parameters.h" #include "abm/person_id.h" #include "abm/personal_rng.h" @@ -38,9 +39,6 @@ namespace mio namespace abm { -struct LocationId; -class Location; // TODO: try to remove. include directly, or use id only - /** * @brief Agents in the simulated World that can carry and spread the Infection. */ @@ -54,7 +52,7 @@ class Person * @param[in] age The AgeGroup of the Person. * @param[in] person_id Index of the Person. */ - explicit Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, + explicit Person(mio::RandomNumberGenerator& rng, LocationId location, AgeGroup age, PersonId person_id = PersonId::invalid_id()); explicit Person(const Person& other, PersonId id); @@ -65,7 +63,7 @@ class Person * @brief Create a copy of this #Person object with a new Location. * @param[in, out] location The new #Location of the Person. */ - Person copy_person(Location& location); + Person copy_person(LocationId location); /** * @brief Compare two Person%s. @@ -85,6 +83,7 @@ class Person /** * @brief Get all Vaccination%s of the Person. * @return A vector with all Vaccination%s. + * @{ */ std::vector& get_vaccinations() { @@ -95,6 +94,7 @@ class Person { return m_vaccinations; } + /** @} */ /** * @brief Returns if the Person is infected at the TimePoint. @@ -131,10 +131,10 @@ class Person */ LocationId get_location() const; - // set new location, e.g. when migrating - void set_location(const Location& location); - - // set new location, e.g. when migrating + /** + * @brief Change the location of the person. + * @param[in] id The new location. + */ void set_location(LocationId id); /** @@ -146,6 +146,10 @@ class Person return m_time_at_location; } + /** + * @brief Add to the time the Person has been at its current Location. + * @param[in] dt TimeSpan the Person has spent at the Location. + */ void add_time_at_location(const TimeSpan dt) { m_time_at_location += dt; @@ -159,13 +163,6 @@ class Person { return m_time_of_last_test; } - /** - * @brief Set an assigned Location of the Person. - * The assigned Location is saved by its index of its LocationId. Assume that a Person has at most one assigned - * Location per #LocationType. - * @param[in] location The new assigned Location. - */ - void set_assigned_location(Location& location); /** * @brief Set an assigned Location of the Person. @@ -355,7 +352,7 @@ class Person * @brief Add a new #Vaccination * @param[in] v ExposureType (i. e. vaccine) the person takes. * @param[in] t TimePoint of the Vaccination. - */ + */ void add_new_vaccination(ExposureType v, TimePoint t) { m_vaccinations.push_back(Vaccination(v, t)); @@ -364,21 +361,25 @@ class Person /** * @brief Get the transport mode the Person used to get to its current Location. * @return TransportMode the Person used to get to its current Location. - */ + */ mio::abm::TransportMode get_last_transport_mode() const { return m_last_transport_mode; } + /** + * @brief Set the transport mode the Person used to get to its current Location. + * @param[in] mode TransportMode the Person used to get to its current Location. + */ void set_last_transport_mode(const mio::abm::TransportMode mode) { m_last_transport_mode = mode; } /** - * Get this persons RandomNumberGenerator counter. - * @see mio::abm::PersonalRandomNumberGenerator. - */ + * @brief Get this persons RandomNumberGenerator counter. + * @see mio::abm::PersonalRandomNumberGenerator. + */ Counter& get_rng_counter() { return m_rng_counter; @@ -386,7 +387,7 @@ class Person /** * @brief Get the latest #Infection or #Vaccination and its initial TimePoint of the Person. - */ + */ std::pair get_latest_protection() const; /** diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index bb15a59f67..4a57a9486c 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -38,7 +38,7 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) m_has_locations[size_t(type)] = true; // mark caches for rebuild - m_local_population_size_cache.invalidate(); + m_local_population_cache.invalidate(); m_air_exposure_rates_cache.invalidate(); m_contact_exposure_rates_cache.invalidate(); m_exposure_rates_need_rebuild = true; @@ -48,7 +48,24 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) PersonId World::add_person(const LocationId id, AgeGroup age) { - return add_person(Person(m_rng, get_location(id), age)); + return add_person(Person(m_rng, id, age)); +} + +PersonId World::add_person(Person&& person) +{ + assert(person.get_location().index != INVALID_LOCATION_INDEX); + assert(person.get_location().index < m_locations.size()); + assert(person.get_age().get() < parameters.get_num_groups()); + + PersonId new_id = static_cast(m_persons.size()); + m_persons.emplace_back(person, new_id); + auto& new_person = m_persons.back(); + new_person.set_assigned_location(m_cemetery_id); + + if (m_local_population_cache.is_valid()) { + ++m_local_population_cache.write()[new_person.get_location().index]; + } + return new_id; } void World::evolve(TimePoint t, TimeSpan dt) @@ -74,17 +91,17 @@ void World::migration(TimePoint t, TimeSpan dt) const auto num_persons = m_persons.size(); PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < num_persons; ++i) { - auto& person = m_persons[i]; + Person& person = m_persons[i]; auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); auto try_migration_rule = [&](auto rule) -> bool { //run migration rule and check if migration can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); - const Location& target_location = get_location(find_location(target_type, person)); + const Location& target_location = get_location(find_location(target_type, i)); const LocationId current_location = person.get_location(); if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { if (target_location.get_id() != current_location && - get_number_persons(target_location) < target_location.get_capacity().persons) { + get_number_persons(target_location.get_id()) < target_location.get_capacity().persons) { bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { migrate(i, target_location.get_id()); @@ -129,7 +146,7 @@ void World::migration(TimePoint t, TimeSpan dt) auto& person = get_person(trip.person_id); auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { - auto& target_location = get_individualized_location(trip.migration_destination); + auto& target_location = get_location(trip.migration_destination); if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); migrate(person.get_person_id(), target_location.get_id(), trip.trip_mode); @@ -143,42 +160,120 @@ void World::migration(TimePoint t, TimeSpan dt) } } +void World::build_compute_local_population_cache() const +{ + PRAGMA_OMP(single) + { + const auto num_locations = m_locations.size(); + const auto num_persons = m_persons.size(); + m_local_population_cache.write().resize(num_locations); + PRAGMA_OMP(taskloop) + for (size_t i = 0; i < num_locations; i++) { + m_local_population_cache.write()[i] = 0.; + } // implicit taskloop barrier + PRAGMA_OMP(taskloop) + for (size_t i = 0; i < num_persons; i++) { + ++m_local_population_cache.write()[m_persons[i].get_location().index]; + } // implicit taskloop barrier + } // implicit single barrier +} + +void World::build_exposure_caches() +{ + PRAGMA_OMP(single) + { + const size_t num_locations = m_locations.size(); + m_air_exposure_rates_cache.write().resize(num_locations); + m_contact_exposure_rates_cache.write().resize(num_locations); + PRAGMA_OMP(taskloop) + for (size_t i = 0; i < num_locations; i++) { + m_air_exposure_rates_cache.write()[i].resize( + {CellIndex(m_locations[i].get_cells().size()), VirusVariant::Count}); + m_contact_exposure_rates_cache.write()[i].resize({CellIndex(m_locations[i].get_cells().size()), + VirusVariant::Count, + AgeGroup(parameters.get_num_groups())}); + } // implicit taskloop barrier + m_air_exposure_rates_cache.invalidate(); + m_contact_exposure_rates_cache.invalidate(); + m_exposure_rates_need_rebuild = false; + } // implicit single barrier +} + +void World::compute_exposure_caches(TimePoint t, TimeSpan dt) +{ + PRAGMA_OMP(single) + { + // if cache shape was changed (e.g. by add_location), rebuild it + if (m_exposure_rates_need_rebuild) { + build_exposure_caches(); + } + // use these const values to help omp recognize that the for loops are bounded + const auto num_locations = m_locations.size(); + const auto num_persons = m_persons.size(); + + // 1) reset all cached values + // Note: we cannot easily reuse values, as they are time dependant (get_infection_state) + PRAGMA_OMP(taskloop) + for (size_t i = 0; i < num_locations; ++i) { + const auto index = i; + auto& local_air_exposure = m_air_exposure_rates_cache.write()[index]; + std::for_each(local_air_exposure.begin(), local_air_exposure.end(), [](auto& r) { + r = 0.0; + }); + auto& local_contact_exposure = m_contact_exposure_rates_cache.write()[index]; + std::for_each(local_contact_exposure.begin(), local_contact_exposure.end(), [](auto& r) { + r = 0.0; + }); + } // implicit taskloop barrier + // here is an implicit (and needed) barrier from parallel for + + // 2) add all contributions from each person + PRAGMA_OMP(taskloop) + for (size_t i = 0; i < num_persons; ++i) { + const Person& person = m_persons[i]; + const auto location = person.get_location().index; + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.write()[location], + m_contact_exposure_rates_cache.write()[location], person, + get_location(person.get_person_id()), t, dt); + } // implicit taskloop barrier + } // implicit single barrier +} + void World::begin_step(TimePoint t, TimeSpan dt) { m_testing_strategy.update_activity_status(t); - if (!m_local_population_size_cache.is_valid()) { - build_local_population_cache(); - m_local_population_size_cache.validate(); + if (!m_local_population_cache.is_valid()) { + build_compute_local_population_cache(); + m_local_population_cache.validate(); } - recompute_exposure_rates(t, dt); + compute_exposure_caches(t, dt); m_air_exposure_rates_cache.validate(); m_contact_exposure_rates_cache.validate(); } auto World::get_locations() const -> Range> { - return std::make_pair(ConstLocationIterator(m_locations.begin()), ConstLocationIterator(m_locations.end())); + return std::make_pair(m_locations.cbegin(), m_locations.cend()); } - -auto World::get_persons() const -> Range> +auto World::get_locations() -> Range> { - return std::make_pair(ConstPersonIterator(m_persons.begin()), ConstPersonIterator(m_persons.end())); + return std::make_pair(m_locations.begin(), m_locations.end()); } -const Location& World::get_individualized_location(LocationId id) const +auto World::get_persons() const -> Range> { - return m_locations[id.index]; + return std::make_pair(m_persons.cbegin(), m_persons.cend()); } -Location& World::get_individualized_location(LocationId id) +auto World::get_persons() -> Range> { - return m_locations[id.index]; + return std::make_pair(m_persons.begin(), m_persons.end()); } -LocationId World::find_location(LocationType type, const Person& person) const +LocationId World::find_location(LocationType type, const PersonId person) const { - auto index = person.get_assigned_location_index(type); + auto index = get_person(person).get_assigned_location_index(type); assert(index != INVALID_LOCATION_INDEX && "unexpected error."); return {index, type}; } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 5070c6d146..17cd5e5136 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -32,11 +32,9 @@ #include "abm/random_events.h" #include "abm/testing_strategy.h" #include "memilio/epidemiology/age_group.h" -#include "memilio/utils/mioomp.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" -#include #include #include @@ -86,7 +84,7 @@ class World //type is move-only for stable references of persons/locations World(const World& other) : parameters(other.parameters) - , m_local_population_size_cache() + , m_local_population_cache() , m_air_exposure_rates_cache() , m_contact_exposure_rates_cache() , m_exposure_rates_need_rebuild(true) @@ -183,53 +181,25 @@ class World PersonId add_person(const LocationId id, AgeGroup age); // adds a copy of person to the world - PersonId add_person(const Person& person) - { - assert([&]() { - get_location(person.get_location()); - return true; - }()); // assert that location is in world - assert(person.get_age().get() < parameters.get_num_groups()); - - PersonId new_id = static_cast(m_persons.size()); - m_persons.emplace_back(person, new_id); - auto& new_person = m_persons.back(); - new_person.set_assigned_location(m_cemetery_id); - - if (m_local_population_size_cache.is_valid()) { - ++m_local_population_size_cache.write()[new_person.get_location().index]; - } - return new_id; - } + PersonId add_person(Person&& person); /** * @brief Get a range of all Location%s in the World. * @return A range of all Location%s. + * @{ */ Range> get_locations() const; - Range> get_locations() - { - return make_range(m_locations.begin(), m_locations.end()); - } + Range> get_locations(); + /** @} */ /** * @brief Get a range of all Person%s in the World. * @return A range of all Person%s. + * @{ */ Range> get_persons() const; - Range> get_persons() - { - return make_range(m_persons.begin(), m_persons.end()); - } - - /** - * @brief Get an individualized Location. - * @param[in] id LocationId of the Location. - * @return Reference to the Location. - */ - const Location& get_individualized_location(LocationId id) const; // TODO: replace by get_location? - - Location& get_individualized_location(LocationId id); + Range> get_persons(); + /** @} */ /** * @brief Find an assigned Location of a Person. @@ -237,7 +207,7 @@ class World * @param[in] person The Person. * @return Reference to the assigned Location. */ - LocationId find_location(LocationType type, const Person& person) const; + LocationId find_location(LocationType type, const PersonId person) const; /** * @brief Get the number of Persons in one #InfectionState at all Location%s. @@ -333,52 +303,51 @@ class World */ void remove_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme); + /** + * @brief Get a reference to a Person from this World. + * @param[in] id A person's PersonId. + * @return A reference to the Person. + * @{ + */ Person& get_person(PersonId id) { - assert(id == m_persons[id.get()].get_person_id()); assert(id.get() < m_persons.size()); return m_persons[id.get()]; } const Person& get_person(PersonId id) const { - assert(id == m_persons[id.get()].get_person_id()); assert(id.get() < m_persons.size()); return m_persons[id.get()]; } + /** @} */ + /** + * @brief Get the number of Person%s of a particular #InfectionState for all Cell%s. + * @param[in] location A LocationId from the world. + * @param[in] t TimePoint of querry. + * @param[in] state #InfectionState of interest. + * @return Amount of Person%s of the #InfectionState in all Cell%s of the Location. + */ size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { - // if (m_local_populations_cache.is_valid()) { - // return std::count_if(m_local_populations_cache.read().at(location.index).begin(), - // m_local_populations_cache.read().at(location.index).end(), [&](uint32_t p) { - // return get_person(PersonId(p)).get_infection_state(t) == state; - // }); - // } - // TODO: check performance on this. + // TODO: if used during simulation, this function may be very slow! return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p.get_location() == location && p.get_infection_state(t) == state; }); } - inline size_t get_subpopulation(const Location& location, TimePoint t, InfectionState state) const - { - return get_subpopulation(location.get_id(), t, state); - } - + /** + * @brief Get the total number of Person%s at the Location. + * @param[in] location A LocationId from the world. + * @return Number of Person%s in the location. + */ size_t get_number_persons(LocationId location) const { - if (m_local_population_size_cache.is_valid()) { - return m_local_population_size_cache.read()[location.index]; + if (!m_local_population_cache.is_valid()) { + build_compute_local_population_cache(); } - return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { - return p.get_location() == location; - }); - } - - inline size_t get_number_persons(const Location& location) const - { - return get_number_persons(location.get_id()); + return m_local_population_cache.read()[location.index]; } // move a person to another location. this requires that location is part of this world. @@ -387,12 +356,13 @@ class World { LocationId origin = get_location(person).get_id(); const bool has_moved = mio::abm::migrate(get_person(person), get_location(destination), cells, mode); + // if the person has moved, invalidate exposure caches but keep population caches valid if (has_moved) { m_air_exposure_rates_cache.invalidate(); m_contact_exposure_rates_cache.invalidate(); - if (m_local_population_size_cache.is_valid()) { - --m_local_population_size_cache.write()[origin.index]; - ++m_local_population_size_cache.write()[destination.index]; + if (m_local_population_cache.is_valid()) { + --m_local_population_cache.write()[origin.index]; + ++m_local_population_cache.write()[destination.index]; } } } @@ -409,7 +379,9 @@ class World const Parameters& global_parameters) { if (!m_air_exposure_rates_cache.is_valid() || !m_contact_exposure_rates_cache.is_valid()) { - recompute_exposure_rates(t, dt); + // checking caches is only needed for external calls + // during simulation (i.e. in evolve()), the caches are computed in begin_step + compute_exposure_caches(t, dt); m_air_exposure_rates_cache.validate(); m_contact_exposure_rates_cache.validate(); } @@ -419,7 +391,12 @@ class World global_parameters); } - // get location by id + /** + * @brief Get a reference to a location in this World. + * @param[in] id LocationId of the Location. + * @return Reference to the Location. + * @{ + */ const Location& get_location(LocationId id) const { assert(id.index != INVALID_LOCATION_INDEX); @@ -427,25 +404,30 @@ class World return m_locations[id.index]; } - // get location by id Location& get_location(LocationId id) { assert(id.index != INVALID_LOCATION_INDEX); assert(id.index < m_locations.size()); return m_locations[id.index]; } + /** @} */ - // get current location of the Person + /** + * @brief Get a reference to the location of a person. + * @param[in] id PersonId of a person. + * @return Reference to the Location. + * @{ + */ inline Location& get_location(PersonId id) { return get_location(get_person(id).get_location()); } - // get current location of the Person inline const Location& get_location(PersonId id) const { return get_location(get_person(id).get_location()); } + /** @} */ private: /** @@ -461,74 +443,21 @@ class World */ void migration(TimePoint t, TimeSpan dt); - void build_local_population_cache() - { - m_local_population_size_cache.write().resize(m_locations.size()); - // this loop (in partikular map[]) cannot run in parallel - for (size_t i = 0; i < m_locations.size(); i++) { - m_local_population_size_cache.write()[i] = 0.; - } - PRAGMA_OMP(parallel for) - for (Person& person : get_persons()) { - ++m_local_population_size_cache.write()[person.get_location().index]; - } - } + /// @brief Shape the cache and store how many Person%s are at any Location. Use from single thread! + void build_compute_local_population_cache() const; - void build_exposure_caches() - { - const size_t num_locations = m_locations.size(); - m_air_exposure_rates_cache.write().resize(num_locations); - m_contact_exposure_rates_cache.write().resize(num_locations); - for (size_t i = 0; i < num_locations; i++) { - m_air_exposure_rates_cache.write()[i].resize( - {CellIndex(m_locations[i].get_cells().size()), VirusVariant::Count}); - m_contact_exposure_rates_cache.write()[i].resize({CellIndex(m_locations[i].get_cells().size()), - VirusVariant::Count, - AgeGroup(parameters.get_num_groups())}); - } - m_air_exposure_rates_cache.invalidate(); - m_contact_exposure_rates_cache.invalidate(); - m_exposure_rates_need_rebuild = false; - } + /// @brief Shape the air and contact exposure cache according to the current Location%s. + void build_exposure_caches(); - void recompute_exposure_rates(TimePoint t, TimeSpan dt) - { - if (m_exposure_rates_need_rebuild) { - build_exposure_caches(); - } - // use these const values to help omp recognize that the for loops are bounded - const auto num_locations = m_locations.size(); - const auto num_persons = m_persons.size(); - - // 1) reset all cached values - // Note: we cannot easily reuse values, as they are time dependant (get_infection_state) - PRAGMA_OMP(parallel for) - for (size_t i = 0; i < num_locations; ++i) { - const auto index = i; - auto& local_air_exposure = m_air_exposure_rates_cache.write()[index]; - std::for_each(local_air_exposure.begin(), local_air_exposure.end(), [](auto& r) { - r = 0.0; - }); - auto& local_contact_exposure = m_contact_exposure_rates_cache.write()[index]; - std::for_each(local_contact_exposure.begin(), local_contact_exposure.end(), [](auto& r) { - r = 0.0; - }); - } - // here is an implicit (and needed) barrier from parallel for - - // 2) add all contributions from each person - PRAGMA_OMP(parallel for) - for (size_t i = 0; i < num_persons; ++i) { - const Person& person = m_persons[i]; - const auto location = person.get_location().index; - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.write()[location], - m_contact_exposure_rates_cache.write()[location], person, - get_location(person.get_person_id()), t, dt); - } - } + /** + * @brief Store all air/contact exposures for the current simulation step. + * @param[in] t Current TimePoint of the simulation. + * @param[in] dt The duration of the simulation step. + */ + void compute_exposure_caches(TimePoint t, TimeSpan dt); - Cache> - m_local_population_size_cache; ///< Current number of Persons in a given location. + mutable Cache> + m_local_population_cache; ///< Current number of Persons in a given location. Cache> m_air_exposure_rates_cache; ///< Cache for local exposure through droplets in #transmissions/day. Cache> diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 6f608ccdce..05b73d25fa 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -319,8 +319,8 @@ void create_assign_locations(mio::abm::World& world) // For the capacity we assume an area of 1.25 m^2 per person (https://doi.org/10.1371/journal.pone.0259037) and a // room height of 3 m auto event = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(100); - world.get_individualized_location(event).set_capacity(100, 375); + world.get_location(event).get_infection_parameters().set(100); + world.get_location(event).set_capacity(100, 375); auto testing_criteria = mio::abm::TestingCriteria(); auto testing_min_time = mio::abm::days(2); @@ -342,11 +342,11 @@ void create_assign_locations(mio::abm::World& world) // (https://doi.org/10.1016/j.buildenv.2021.107926)) // For the ICUs we assume a capacity of 30 agents and the same volume. auto hospital = world.add_location(mio::abm::LocationType::Hospital); - world.get_individualized_location(hospital).get_infection_parameters().set(5); - world.get_individualized_location(hospital).set_capacity(584, 26242); + world.get_location(hospital).get_infection_parameters().set(5); + world.get_location(hospital).set_capacity(584, 26242); auto icu = world.add_location(mio::abm::LocationType::ICU); - world.get_individualized_location(icu).get_infection_parameters().set(5); - world.get_individualized_location(icu).set_capacity(30, 1350); + world.get_location(icu).get_infection_parameters().set(5); + world.get_location(icu).set_capacity(30, 1350); // Add schools, workplaces and shops. // At every school there are 600 students. The maximum contacs are 40. @@ -360,16 +360,16 @@ void create_assign_locations(mio::abm::World& world) // and a volume of 7200 cubic meters (10 m^2 per person (https://doi.org/10.1371/journal.pone.0259037) and 3 m // room height). auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); - world.get_individualized_location(shop).set_capacity(240, 7200); + world.get_location(shop).get_infection_parameters().set(20); + world.get_location(shop).set_capacity(240, 7200); auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(40); - world.get_individualized_location(school).set_capacity(600, 3600); + world.get_location(school).get_infection_parameters().set(40); + world.get_location(school).set_capacity(600, 3600); auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(40); - world.get_individualized_location(work).set_capacity(100, 3000); + world.get_location(work).get_infection_parameters().set(40); + world.get_location(work).set_capacity(100, 3000); int counter_event = 0; int counter_school = 0; @@ -399,26 +399,26 @@ void create_assign_locations(mio::abm::World& world) if (counter_event == 1000) { counter_event = 0; event = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).set_capacity(100, 375); - world.get_individualized_location(event).get_infection_parameters().set(100); + world.get_location(event).set_capacity(100, 375); + world.get_location(event).get_infection_parameters().set(100); } if (counter_school == 600) { counter_school = 0; school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(40); - world.get_individualized_location(school).set_capacity(600, 3600); + world.get_location(school).get_infection_parameters().set(40); + world.get_location(school).set_capacity(600, 3600); } if (counter_work == 100) { counter_work = 0; work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(40); - world.get_individualized_location(work).set_capacity(100, 3000); + world.get_location(work).get_infection_parameters().set(40); + world.get_location(work).set_capacity(100, 3000); } if (counter_shop == 15000) { counter_shop = 0; shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); - world.get_individualized_location(shop).set_capacity(240, 7200); + world.get_location(shop).get_infection_parameters().set(20); + world.get_location(shop).set_capacity(240, 7200); } } diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index 4c93d852d5..ade955d314 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -225,13 +225,12 @@ void create_world_from_data(mio::abm::World& world, const std::string& filename, // We assume that no person goes to an hospital, altough e.g. "Sonstiges" could be a hospital auto hospital = world.add_location(mio::abm::LocationType::Hospital); - world.get_individualized_location(hospital).get_infection_parameters().set(5); - world.get_individualized_location(hospital).set_capacity(std::numeric_limits::max(), - std::numeric_limits::max()); + world.get_location(hospital).get_infection_parameters().set(5); + world.get_location(hospital).set_capacity(std::numeric_limits::max(), + std::numeric_limits::max()); auto icu = world.add_location(mio::abm::LocationType::ICU); - world.get_individualized_location(icu).get_infection_parameters().set(5); - world.get_individualized_location(icu).set_capacity(std::numeric_limits::max(), - std::numeric_limits::max()); + world.get_location(icu).get_infection_parameters().set(5); + world.get_location(icu).set_capacity(std::numeric_limits::max(), std::numeric_limits::max()); // First we determine the persons number and their starting locations int number_of_persons = 0; @@ -310,7 +309,7 @@ void create_world_from_data(mio::abm::World& world, const std::string& filename, locations.insert({home_id, home}); mio::abm::GeographicalLocation location_long_lat_home = {(double)row[index["lon_start"]] / 1e+5, (double)row[index["lat_start"]] / 1e+5}; - world.get_individualized_location(home).set_geographical_location(location_long_lat_home); + world.get_location(home).set_geographical_location(location_long_lat_home); } else { home = it_home->second; @@ -324,7 +323,7 @@ void create_world_from_data(mio::abm::World& world, const std::string& filename, get_location_type(activity_end), 1); // Assume one place has one activity, this may be untrue but not important for now(?) locations.insert({target_location_id, location}); - world.get_individualized_location(location).set_geographical_location(location_long_lat); + world.get_location(location).set_geographical_location(location_long_lat); } } fin.clear(); diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 4ca62a2025..bf92297772 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -27,7 +27,7 @@ mio::abm::Person make_test_person(mio::abm::Location& location, mio::AgeGroup ag { assert(age.get() < params.get_num_groups()); auto rng = mio::RandomNumberGenerator(); - mio::abm::Person p(rng, location, age); + mio::abm::Person p(rng, location.get_id(), age); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index 759d9cbfb7..fbbc022789 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -166,7 +166,7 @@ TEST(TestInfection, getPersonalProtectiveFactor) auto rng = mio::RandomNumberGenerator(); auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = mio::abm::Person(rng, location, age_group_15_to_34); + auto person = mio::abm::Person(rng, location.get_id(), age_group_15_to_34); person.add_new_vaccination(mio::abm::ExposureType::GenericVaccine, mio::abm::TimePoint(0)); auto latest_protection = person.get_latest_protection(); diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index c402a8e27c..df4850d8ca 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -268,12 +268,12 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); auto person_rng = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); - mio::abm::interact(person_rng, susceptible, location, local_population, t, dt, params); + mio::abm::interact_testing(person_rng, susceptible, location, local_population, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - mio::abm::interact(person_rng, susceptible, location, local_population, t, dt, params); + mio::abm::interact_testing(person_rng, susceptible, location, local_population, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); } diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index 7a183736d1..54ba344b13 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -45,12 +45,12 @@ TEST(TestLockdownRules, school_closure) .WillOnce(testing::Return(0.2)) .WillRepeatedly(testing::Return(1.0)); - auto p1 = mio::abm::Person(rng, home, age_group_5_to_14); - p1.set_assigned_location(home); - p1.set_assigned_location(school); - auto p2 = mio::abm::Person(rng, home, age_group_5_to_14); - p2.set_assigned_location(home); - p2.set_assigned_location(school); + auto p1 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + p1.set_assigned_location(home.get_id()); + p1.set_assigned_location(school.get_id()); + auto p2 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + p2.set_assigned_location(home.get_id()); + p2.set_assigned_location(school.get_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; @@ -86,9 +86,9 @@ TEST(TestLockdownRules, school_opening) .WillOnce(testing::Return(0.6)) .WillOnce(testing::Return(0.6)) .WillRepeatedly(testing::Return(1.0)); - auto p = mio::abm::Person(rng, home, age_group_5_to_14); - p.set_assigned_location(home); - p.set_assigned_location(school); + auto p = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + p.set_assigned_location(home.get_id()); + p.set_assigned_location(school.get_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; @@ -135,12 +135,12 @@ TEST(TestLockdownRules, home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto person1 = mio::abm::Person(rng, home, age_group_15_to_34); - auto person2 = mio::abm::Person(rng, home, age_group_15_to_34); - person1.set_assigned_location(home); - person1.set_assigned_location(work); - person2.set_assigned_location(home); - person2.set_assigned_location(work); + auto person1 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + auto person2 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + person1.set_assigned_location(home.get_id()); + person1.set_assigned_location(work.get_id()); + person2.set_assigned_location(home.get_id()); + person2.set_assigned_location(work.get_id()); auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, person1); ASSERT_EQ(mio::abm::go_to_work(p1_rng, person1, t_morning, dt, params), mio::abm::LocationType::Work); @@ -168,9 +168,9 @@ TEST(TestLockdownRules, no_home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto p = mio::abm::Person(rng, home, age_group_15_to_34); - p.set_assigned_location(home); - p.set_assigned_location(work); + auto p = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + p.set_assigned_location(home.get_id()); + p.set_assigned_location(work.get_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; @@ -196,9 +196,9 @@ TEST(TestLockdownRules, social_event_closure) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location event(mio::abm::LocationType::SocialEvent, 0, num_age_groups); - auto p = mio::abm::Person(rng, home, age_group_5_to_14); - p.set_assigned_location(home); - p.set_assigned_location(event); + auto p = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + p.set_assigned_location(home.get_id()); + p.set_assigned_location(event.get_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); mio::abm::close_social_events(t, 1, params); @@ -217,9 +217,9 @@ TEST(TestLockdownRules, social_events_opening) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location event(mio::abm::LocationType::SocialEvent, 0, num_age_groups); - auto p = mio::abm::Person(rng, home, age_group_5_to_14); - p.set_assigned_location(event); - p.set_assigned_location(home); + auto p = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + p.set_assigned_location(event.get_id()); + p.set_assigned_location(home.get_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); mio::abm::close_social_events(t_closing, 1, params); diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index c61bc309b9..4ddae32e1d 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -65,8 +65,8 @@ TEST(TestMasks, maskProtection) //setup location with some chance of exposure auto t = mio::abm::TimePoint(0); mio::abm::Location infection_location(mio::abm::LocationType::School, 0, num_age_groups); - auto susc_person1 = mio::abm::Person(rng, infection_location, age_group_15_to_34); - auto susc_person2 = mio::abm::Person(rng, infection_location, age_group_15_to_34); + auto susc_person1 = mio::abm::Person(rng, infection_location.get_id(), age_group_15_to_34); + auto susc_person2 = mio::abm::Person(rng, infection_location.get_id(), age_group_15_to_34); auto infected1 = make_test_person(infection_location, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior @@ -82,12 +82,12 @@ TEST(TestMasks, maskProtection) mock_exponential_dist; auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person1); - mio::abm::interact(p1_rng, susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, - params); + mio::abm::interact_testing(p1_rng, susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, + params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); auto p2_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person2); - mio::abm::interact(p2_rng, susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, - params); + mio::abm::interact_testing(p2_rng, susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, + params); // The person susc_person1 should have full protection against an infection, susc_person2 not ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 19a40aad7c..c3de3ac456 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -35,8 +35,8 @@ TEST(TestMigrationRules, 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(rng, home, age_group_5_to_14); - auto p_adult = mio::abm::Person(rng, home, age_group_15_to_34); + auto p_child = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + auto p_adult = mio::abm::Person(rng, home.get_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); @@ -78,9 +78,9 @@ TEST(TestMigrationRules, 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(rng, home, age_group_5_to_14); + auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_6); - auto p_child_goes_to_school_at_8 = mio::abm::Person(rng, home, age_group_5_to_14); + auto p_child_goes_to_school_at_8 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_8 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); @@ -138,9 +138,9 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times_with_smaller_t .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(rng, home, age_group_5_to_14); + auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_6); - auto p_child_goes_to_school_at_8_30 = mio::abm::Person(rng, home, age_group_5_to_14); + auto p_child_goes_to_school_at_8_30 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_8_30 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_8_30); @@ -175,7 +175,7 @@ TEST(TestMigrationRules, school_return) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location school(mio::abm::LocationType::School, 0, num_age_groups); - auto p_child = mio::abm::Person(rng, school, age_group_5_to_14); + auto p_child = mio::abm::Person(rng, school.get_id(), age_group_5_to_14); auto rng_child = mio::abm::PersonalRandomNumberGenerator(rng, p_child); auto t = mio::abm::TimePoint(0) + mio::abm::hours(15); @@ -202,9 +202,9 @@ TEST(TestMigrationRules, worker_goes_to_work) .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = mio::abm::Person(rng, home, age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home, age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); @@ -242,9 +242,9 @@ TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = mio::abm::Person(rng, home, age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home, age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); @@ -283,9 +283,9 @@ TEST(TestMigrationRules, workers_go_to_work_in_different_times) .WillRepeatedly(testing::Return(1.0)); - auto p_adult_goes_to_work_at_6 = mio::abm::Person(rng, home, age_group_15_to_34); + auto p_adult_goes_to_work_at_6 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); auto rng_adult_goes_to_work_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_adult_goes_to_work_at_6); - auto p_adult_goes_to_work_at_8 = mio::abm::Person(rng, home, age_group_15_to_34); + auto p_adult_goes_to_work_at_8 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); auto rng_adult_goes_to_work_at_8 = mio::abm::PersonalRandomNumberGenerator(rng, p_adult_goes_to_work_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); @@ -319,7 +319,7 @@ TEST(TestMigrationRules, work_return) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); - auto p_adult = mio::abm::Person(rng, work, age_group_35_to_59); + auto p_adult = mio::abm::Person(rng, work.get_id(), age_group_35_to_59); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); auto dt = mio::abm::hours(1); @@ -387,7 +387,7 @@ TEST(TestMigrationRules, go_shopping) auto p_hosp = make_test_person(hospital, age_group_0_to_4, mio::abm::InfectionState::InfectedSymptoms, t_weekday); auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(rng, p_hosp); - auto p_home = mio::abm::Person(rng, home, age_group_60_to_79); + auto p_home = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); ASSERT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), @@ -417,7 +417,7 @@ TEST(TestMigrationRules, shop_return) auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, shop); - mio::abm::interact(rng_p, p, shop, {p}, t, dt, params); //person only returns home after some time passed + mio::abm::interact_testing(rng_p, p, shop, {p}, t, dt, params); //person only returns home after some time passed ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); @@ -427,10 +427,10 @@ TEST(TestMigrationRules, go_event) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); - auto p_work = mio::abm::Person(rng, work, age_group_35_to_59); + auto p_work = mio::abm::Person(rng, work.get_id(), age_group_35_to_59); auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - auto p_home = mio::abm::Person(rng, home, age_group_60_to_79); + auto p_home = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); auto t_weekday = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(20); @@ -463,11 +463,11 @@ TEST(TestMigrationRules, event_return) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 0, num_age_groups); - auto p = mio::abm::Person(rng, home, age_group_15_to_34); + auto p = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, social_event); - mio::abm::interact(rng_p, p, social_event, {p}, t, dt, params); + mio::abm::interact_testing(rng_p, p, social_event, {p}, t, dt, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index ae33452557..3bb90f84d4 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -32,7 +32,7 @@ TEST(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(rng, location, age_group_60_to_79); + auto person = mio::abm::Person(rng, location.get_id(), age_group_60_to_79); EXPECT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(person.get_location(), location.get_id()); @@ -44,9 +44,9 @@ TEST(TestPerson, copyPerson) auto rng = mio::RandomNumberGenerator(); auto location = mio::abm::Location(mio::abm::LocationType::Work, 0, num_age_groups); auto t = mio::abm::TimePoint(0); - auto person = mio::abm::Person(rng, location, age_group_60_to_79); + auto person = mio::abm::Person(rng, location.get_id(), age_group_60_to_79); auto copied_location = location.copy(); - auto copied_person = person.copy_person(copied_location); + auto copied_person = person.copy_person(copied_location.get_id()); EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(copied_person.get_location(), copied_location.get_id()); @@ -85,8 +85,8 @@ TEST(TestPerson, setGetAssignedLocation) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location location(mio::abm::LocationType::Work, 2, num_age_groups); - auto person = mio::abm::Person(rng, location, age_group_35_to_59); - person.set_assigned_location(location); + auto person = mio::abm::Person(rng, location.get_id(), age_group_35_to_59); + person.set_assigned_location(location.get_id()); EXPECT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 2); person.set_assigned_location({4, mio::abm::LocationType::Work}); @@ -144,7 +144,7 @@ TEST(TestPerson, get_tested) mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); auto infected = make_test_person(loc, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); auto rng_infected = mio::abm::PersonalRandomNumberGenerator(rng, infected); - auto susceptible = mio::abm::Person(rng, loc, age_group_15_to_34); + auto susceptible = mio::abm::Person(rng, loc.get_id(), age_group_15_to_34); auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); auto pcr_test = mio::abm::PCRTest(); @@ -205,10 +205,10 @@ TEST(TestPerson, interact) auto infection_parameters = mio::abm::Parameters(num_age_groups); mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::TimePoint t(0); - auto person = mio::abm::Person(rng, loc, age_group_15_to_34); + auto person = mio::abm::Person(rng, loc.get_id(), age_group_15_to_34); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days - mio::abm::interact(rng_person, person, loc, {person}, t, dt, infection_parameters); + mio::abm::interact_testing(rng_person, person, loc, {person}, t, dt, infection_parameters); EXPECT_EQ(person.get_time_at_location(), dt); } @@ -290,7 +290,7 @@ TEST(TestPerson, getLatestProtection) { auto rng = mio::RandomNumberGenerator(); auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = mio::abm::Person(rng, location, age_group_15_to_34); + auto person = mio::abm::Person(rng, location.get_id(), age_group_15_to_34); auto prng = mio::abm::PersonalRandomNumberGenerator(rng, person); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); @@ -312,7 +312,7 @@ TEST(Person, rng) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location loc(mio::abm::LocationType::Home, 0); - auto p = mio::abm::Person(rng, loc, age_group_35_to_59, mio::abm::PersonId(13)); + auto p = mio::abm::Person(rng, loc.get_id(), age_group_35_to_59, mio::abm::PersonId(13)); EXPECT_EQ(p.get_rng_counter(), mio::Counter(0)); diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index fc33ec76d6..e6b016b1d4 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -42,10 +42,10 @@ TEST(TestWorld, addLocation) ASSERT_EQ((int)school_id1.index, 1); ASSERT_EQ((int)school_id2.index, 2); - auto& school1 = world.get_individualized_location(school_id1); - auto& school2 = world.get_individualized_location(school_id2); - auto& work = world.get_individualized_location(work_id); - auto& home = world.get_individualized_location(home_id); + auto& school1 = world.get_location(school_id1); + auto& school2 = world.get_location(school_id2); + auto& work = world.get_location(work_id); + auto& home = world.get_location(home_id); size_t count_schools = 0; for (auto& loc : world.get_locations()) { @@ -104,20 +104,21 @@ TEST(TestWorld, findLocation) auto home_id = world.add_location(mio::abm::LocationType::Home); auto school_id = world.add_location(mio::abm::LocationType::School); auto work_id = world.add_location(mio::abm::LocationType::Work); - auto& person = world.get_person(add_test_person(world, home_id)); + auto person_id = add_test_person(world, home_id); + auto& person = world.get_person(person_id); person.set_assigned_location(home_id); person.set_assigned_location(work_id); person.set_assigned_location(school_id); - ASSERT_EQ(world.find_location(mio::abm::LocationType::Work, person), work_id); - ASSERT_EQ(world.find_location(mio::abm::LocationType::School, person), school_id); - ASSERT_EQ(world.find_location(mio::abm::LocationType::Home, person), home_id); + ASSERT_EQ(world.find_location(mio::abm::LocationType::Work, person_id), work_id); + ASSERT_EQ(world.find_location(mio::abm::LocationType::School, person_id), school_id); + ASSERT_EQ(world.find_location(mio::abm::LocationType::Home, person_id), home_id); auto&& world_test = std::as_const(world); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Work, person), work_id); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::School, person), school_id); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Home, person), home_id); + ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Work, person_id), work_id); + ASSERT_EQ(world_test.find_location(mio::abm::LocationType::School, person_id), school_id); + ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Home, person_id), home_id); } TEST(TestWorld, evolveStateTransition) @@ -308,38 +309,33 @@ TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a uni world.evolve(t, dt); - auto& event = world.get_individualized_location(event_id); - auto& work = world.get_individualized_location(work_id); - auto& home = world.get_individualized_location(home_id); - auto& hospital = world.get_individualized_location(hospital_id); - - EXPECT_EQ(p1.get_location(), work.get_id()); - EXPECT_EQ(p2.get_location(), event.get_id()); - EXPECT_EQ(p3.get_location(), hospital.get_id()); - EXPECT_EQ(p4.get_location(), home.get_id()); - EXPECT_EQ(p5.get_location(), event.get_id()); - EXPECT_EQ(world.get_number_persons(event), 2); - EXPECT_EQ(world.get_number_persons(work), 1); - EXPECT_EQ(world.get_number_persons(home), 1); - EXPECT_EQ(world.get_number_persons(hospital), 1); - - world.migrate(p1.get_person_id(), home.get_id()); - world.migrate(p2.get_person_id(), home.get_id()); - world.migrate(p5.get_person_id(), home.get_id()); + EXPECT_EQ(p1.get_location(), work_id); + EXPECT_EQ(p2.get_location(), event_id); + EXPECT_EQ(p3.get_location(), hospital_id); + EXPECT_EQ(p4.get_location(), home_id); + EXPECT_EQ(p5.get_location(), event_id); + EXPECT_EQ(world.get_number_persons(event_id), 2); + EXPECT_EQ(world.get_number_persons(work_id), 1); + EXPECT_EQ(world.get_number_persons(home_id), 1); + EXPECT_EQ(world.get_number_persons(hospital_id), 1); + + world.migrate(p1.get_person_id(), home_id); + world.migrate(p2.get_person_id(), home_id); + world.migrate(p5.get_person_id(), home_id); t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); world.get_trip_list().reset_index(); world.evolve(t, dt); - EXPECT_EQ(p1.get_location(), work.get_id()); - EXPECT_EQ(p2.get_location(), event.get_id()); - EXPECT_EQ(p3.get_location(), home.get_id()); - EXPECT_EQ(p4.get_location(), home.get_id()); - EXPECT_EQ(p5.get_location(), event.get_id()); - EXPECT_EQ(world.get_number_persons(event), 2); - EXPECT_EQ(world.get_number_persons(work), 1); - EXPECT_EQ(world.get_number_persons(home), 2); + EXPECT_EQ(p1.get_location(), work_id); + EXPECT_EQ(p2.get_location(), event_id); + EXPECT_EQ(p3.get_location(), home_id); + EXPECT_EQ(p4.get_location(), home_id); + EXPECT_EQ(p5.get_location(), event_id); + EXPECT_EQ(world.get_number_persons(event_id), 2); + EXPECT_EQ(world.get_number_persons(work_id), 1); + EXPECT_EQ(world.get_number_persons(home_id), 2); bool weekend = true; mio::abm::Trip tripweekend1(p1.get_person_id(), @@ -356,14 +352,14 @@ TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a uni world.evolve(t, dt); - EXPECT_EQ(p1.get_location(), event.get_id()); - EXPECT_EQ(p2.get_location(), home.get_id()); - EXPECT_EQ(p3.get_location(), home.get_id()); - EXPECT_EQ(p4.get_location(), home.get_id()); - EXPECT_EQ(p5.get_location(), work.get_id()); - EXPECT_EQ(world.get_number_persons(event), 1); - EXPECT_EQ(world.get_number_persons(work), 1); - EXPECT_EQ(world.get_number_persons(home), 3); + EXPECT_EQ(p1.get_location(), event_id); + EXPECT_EQ(p2.get_location(), home_id); + EXPECT_EQ(p3.get_location(), home_id); + EXPECT_EQ(p4.get_location(), home_id); + EXPECT_EQ(p5.get_location(), work_id); + EXPECT_EQ(world.get_number_persons(event_id), 1); + EXPECT_EQ(world.get_number_persons(work_id), 1); + EXPECT_EQ(world.get_number_persons(home_id), 3); } // Test that a dead person cannot make a movement @@ -430,17 +426,17 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) 100; world.parameters.get()[{mio::abm::VirusVariant(0), age_group_15_to_34}] = 100; - auto home_id = world.add_location(mio::abm::LocationType::Home); - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto& home = world.get_individualized_location(home_id); - auto& work = world.get_individualized_location(work_id); + auto home_id = world.add_location(mio::abm::LocationType::Home); + auto work_id = world.add_location(mio::abm::LocationType::Work); + auto& work = world.get_location(work_id); + auto current_time = mio::abm::TimePoint(0); auto pid = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time); auto& person = world.get_person(pid); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); - person.set_assigned_location(home); - person.set_assigned_location(work); + person.set_assigned_location(home_id); + person.set_assigned_location(work_id); auto testing_criteria = mio::abm::TestingCriteria(); testing_criteria.add_infection_state(mio::abm::InfectionState::InfectedSymptoms); @@ -575,13 +571,13 @@ TEST(TestWorld, copyWorld) // TODO: this needs either a rewrite or to be removed auto work_id = world.add_location(mio::abm::LocationType::Work); auto home_id = world.add_location(mio::abm::LocationType::Home); - auto& school1 = world.get_individualized_location(school_id1); + auto& school1 = world.get_location(school_id1); school1.set_required_mask(mio::abm::MaskType::Surgical); school1.set_npi_active(true); - auto& school2 = world.get_individualized_location(school_id2); + auto& school2 = world.get_location(school_id2); school2.set_required_mask(mio::abm::MaskType::FFP2); - auto& work = world.get_individualized_location(work_id); - auto& home = world.get_individualized_location(home_id); + auto& work = world.get_location(work_id); + auto& home = world.get_location(home_id); auto pid1 = world.add_person(school_id1, age_group_0_to_4); auto pid2 = world.add_person(school_id2, age_group_15_to_34); @@ -631,44 +627,44 @@ TEST(TestWorld, copyWorld) // TODO: this needs either a rewrite or to be removed ASSERT_EQ(copied_world.get_locations()[2].get_index(), world.get_locations()[2].get_index()); ASSERT_EQ(copied_world.get_locations()[3].get_index(), world.get_locations()[3].get_index()); ASSERT_EQ(copied_world.get_locations()[4].get_index(), world.get_locations()[4].get_index()); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[1]), - world.get_number_persons(world.get_locations()[1])); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[2]), - world.get_number_persons(world.get_locations()[2])); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[3]), - world.get_number_persons(world.get_locations()[3])); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[4]), - world.get_number_persons(world.get_locations()[4])); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[1].get_id()), + world.get_number_persons(world.get_locations()[1].get_id())); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[2].get_id()), + world.get_number_persons(world.get_locations()[2].get_id())); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[3].get_id()), + world.get_number_persons(world.get_locations()[3].get_id())); + ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[4].get_id()), + world.get_number_persons(world.get_locations()[4].get_id())); ASSERT_EQ(copied_world.get_locations()[1].get_npi_active(), world.get_locations()[1].get_npi_active()); ASSERT_EQ(copied_world.get_locations()[2].get_npi_active(), world.get_locations()[2].get_npi_active()); ASSERT_EQ(copied_world.get_locations()[3].get_npi_active(), world.get_locations()[3].get_npi_active()); ASSERT_EQ(copied_world.get_locations()[4].get_npi_active(), world.get_locations()[4].get_npi_active()); ASSERT_EQ(copied_world.get_locations()[1].get_required_mask(), world.get_locations()[1].get_required_mask()); ASSERT_EQ(copied_world.get_locations()[2].get_required_mask(), world.get_locations()[2].get_required_mask()); - ASSERT_EQ( - copied_world.get_subpopulation(copied_world.get_locations()[1], mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[1], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[1], mio::abm::TimePoint(0), + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[1].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[1].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed)); + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[1].get_id(), mio::abm::TimePoint(0), mio::abm::InfectionState::Susceptible), - world.get_subpopulation(world.get_locations()[1], mio::abm::TimePoint(0), + world.get_subpopulation(world.get_locations()[1].get_id(), mio::abm::TimePoint(0), mio::abm::InfectionState::Susceptible)); - ASSERT_EQ( - copied_world.get_subpopulation(copied_world.get_locations()[2], mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[2], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[2], mio::abm::TimePoint(0), + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[2].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[2].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed)); + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[2].get_id(), mio::abm::TimePoint(0), mio::abm::InfectionState::Susceptible), - world.get_subpopulation(world.get_locations()[2], mio::abm::TimePoint(0), + world.get_subpopulation(world.get_locations()[2].get_id(), mio::abm::TimePoint(0), mio::abm::InfectionState::Susceptible)); - ASSERT_EQ( - copied_world.get_subpopulation(copied_world.get_locations()[3], mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[3], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); - ASSERT_EQ( - copied_world.get_subpopulation(copied_world.get_locations()[4], mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[4], mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed)); + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[3].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[3].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed)); + ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[4].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed), + world.get_subpopulation(world.get_locations()[4].get_id(), mio::abm::TimePoint(0), + mio::abm::InfectionState::Exposed)); ASSERT_EQ(copied_world.get_locations()[1].get_cells().size(), world.get_locations()[1].get_cells().size()); ASSERT_EQ(copied_world.get_locations()[2].get_cells().size(), world.get_locations()[2].get_cells().size()); ASSERT_EQ(copied_world.get_locations()[3].get_cells().size(), world.get_locations()[2].get_cells().size()); diff --git a/cpp/tests/test_json_serializer.cpp b/cpp/tests/test_json_serializer.cpp index e81242b22f..53e763b16e 100644 --- a/cpp/tests/test_json_serializer.cpp +++ b/cpp/tests/test_json_serializer.cpp @@ -509,7 +509,7 @@ TEST(TestJsonSerializer, abmTrip) auto world = mio::abm::World(num_age_groups); auto home_id = world.add_location(mio::abm::LocationType::Home); auto work_id = world.add_location(mio::abm::LocationType::Work); - auto& home = world.get_individualized_location(home_id); + auto& home = world.get_location(home_id); auto person = make_test_person(home); mio::abm::Trip trip(person.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), work_id, home_id); auto js = mio::serialize_json(trip, true); From bcbc17e69fb7fb3e875d7204716c4da6c4352870 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:02:46 +0100 Subject: [PATCH 24/54] clean up part 2. moved interact_testing to abm_helpers.h/.cpp. removed caching.h and Cache class, as it ended up being less usefull than storing a bool and the data directly. --- cpp/models/abm/caching.h | 43 ------ cpp/models/abm/functions.cpp | 45 ------ cpp/models/abm/functions.h | 32 +++-- cpp/models/abm/person.h | 2 - cpp/models/abm/world.cpp | 51 +++---- cpp/models/abm/world.h | 45 +++--- cpp/tests/abm_helpers.cpp | 27 ++++ cpp/tests/abm_helpers.h | 9 +- cpp/tests/test_abm_infection.cpp | 1 + cpp/tests/test_abm_location.cpp | 111 +-------------- cpp/tests/test_abm_lockdown_rules.cpp | 2 + cpp/tests/test_abm_masks.cpp | 6 +- cpp/tests/test_abm_migration_rules.cpp | 5 +- cpp/tests/test_abm_person.cpp | 3 +- cpp/tests/test_abm_world.cpp | 185 +------------------------ 15 files changed, 118 insertions(+), 449 deletions(-) delete mode 100644 cpp/models/abm/caching.h diff --git a/cpp/models/abm/caching.h b/cpp/models/abm/caching.h deleted file mode 100644 index 97d1ba9abe..0000000000 --- a/cpp/models/abm/caching.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef CACHING_H_ -#define CACHING_H_ - -namespace mio -{ - -template -struct Cache { - // const access to the cached data - const T& read() const - { - return data; - } - - // access to cached data - only intended for writing, use read() to access data - T& write() - { - return data; - } - - bool is_valid() const - { - return m_is_valid; - } - - void invalidate() - { - m_is_valid = false; - } - - void validate() - { - m_is_valid = true; - } - -private: - T data; - bool m_is_valid = false; -}; - -} // namespace mio - -#endif // CACHING_H_ diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index c8f1300994..76dc7646d6 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -31,17 +31,6 @@ namespace mio namespace abm { -// TODO: daily_transmissions functions are only used in interact. expose in header anyways? - -/** - * @brief Compute the number of daily transmissions for contact transmission of a virus in a cell. - * @param[in] rates The local exposure rates. - * @param[in] cell_index Cell index of the Cell. - * @param[in] virus VirusVariant of interest. - * @param[in] age_receiver AgeGroup of the receiving Person. - * @param[in] params The local infection parameters. - * @return Average amount of Infection%s with the virus from the AgeGroup of the transmitter per day. - */ ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const AgeGroup age_receiver, const LocalInfectionParameters& params) @@ -55,14 +44,6 @@ ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, co return prob; } -/** - * @brief Compute the number of daily transmissions for aerosol transmission of a virus in a cell. - * @param[in] rates The local exposure rates. - * @param[in] cell_index Cell index of the Cell. - * @param[in] virus VirusVariant of interest. - * @param[in] global_params The parameter set of the World. - * @return Average amount of Infection%s with the virus per day. - */ ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const Parameters& global_params) { @@ -146,32 +127,6 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo } } -// compatability layer for using interact without existing caches -void interact_testing(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, - const std::vector& local_population, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters) -{ - // allocate and initialize air exposures with 0 - AirExposureRates local_air_exposure; - local_air_exposure.resize({CellIndex(location.get_cells().size()), VirusVariant::Count}); - std::for_each(local_air_exposure.begin(), local_air_exposure.end(), [](auto& r) { - r = 0.0; - }); - // allocate and initialize contact exposures with 0 - ContactExposureRates local_contact_exposure; - local_contact_exposure.resize( - {CellIndex(location.get_cells().size()), VirusVariant::Count, AgeGroup(global_parameters.get_num_groups())}); - std::for_each(local_contact_exposure.begin(), local_contact_exposure.end(), [](auto& r) { - r = 0.0; - }); - // caclculate current exposures - for (const Person& p : local_population) { - add_exposure_contribution(local_air_exposure, local_contact_exposure, p, location, t, dt); - } - // run interaction - interact(personal_rng, person, location, local_air_exposure, local_contact_exposure, t, dt, global_parameters); -} - bool migrate(Person& person, const Location& destination, const std::vector& cells, const TransportMode mode) { assert(std::all_of(cells.begin(), cells.end(), [&](const auto& cell) { diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h index 42f4cd24f7..e02efc8ec5 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/functions.h @@ -21,8 +21,6 @@ #ifndef FUNCTIONS_H_ #define FUNCTIONS_H_ -// TODO: find a meaningfull header name - #include "abm/location.h" #include "abm/person.h" @@ -33,6 +31,30 @@ namespace mio namespace abm { +/** + * @brief Compute the number of daily transmissions for contact transmission of a virus in a cell. + * @param[in] rates The local exposure rates. + * @param[in] cell_index Cell index of the Cell. + * @param[in] virus VirusVariant of interest. + * @param[in] age_receiver AgeGroup of the receiving Person. + * @param[in] params The local infection parameters. + * @return Average amount of Infection%s with the virus from the AgeGroup of the transmitter per day. + */ +ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, + const VirusVariant virus, const AgeGroup age_receiver, + const LocalInfectionParameters& params); + +/** + * @brief Compute the number of daily transmissions for aerosol transmission of a virus in a cell. + * @param[in] rates The local exposure rates. + * @param[in] cell_index Cell index of the Cell. + * @param[in] virus VirusVariant of interest. + * @param[in] global_params The parameter set of the World. + * @return Average amount of Infection%s with the virus per day. + */ +ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, + const VirusVariant virus, const Parameters& global_params); + /** * @brief Add the contribution of a person to the local exposure rates. * @param[in, out] local_air_exposure Exposure rates by aerosols for the local population. @@ -59,12 +81,6 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, const AirExposureRates& local_air_exposure, const ContactExposureRates& local_contact_exposure, const TimePoint t, const TimeSpan dt, const Parameters& global_parameters); - -// interact, but it computes the correct exposures for you -void interact_testing(PersonalRandomNumberGenerator& personal_rng, Person& person, const Location& location, - const std::vector& local_population, const TimePoint t, const TimeSpan dt, - const Parameters& global_parameters); - /** * @brief Move a person to another location. * If the person already is at the destination, neither mode nor cells are set. diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index d06690e662..8c4604e04f 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -57,8 +57,6 @@ class Person explicit Person(const Person& other, PersonId id); - // TODO: re-add migrate_to, using mio::abm::migrate? - /** * @brief Create a copy of this #Person object with a new Location. * @param[in, out] location The new #Location of the Person. diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 4a57a9486c..dad7a54399 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -38,10 +38,9 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) m_has_locations[size_t(type)] = true; // mark caches for rebuild - m_local_population_cache.invalidate(); - m_air_exposure_rates_cache.invalidate(); - m_contact_exposure_rates_cache.invalidate(); - m_exposure_rates_need_rebuild = true; + m_is_local_population_cache_valid = false; + m_are_exposure_caches_valid = false; + m_exposure_caches_need_rebuild = true; return id; } @@ -62,8 +61,8 @@ PersonId World::add_person(Person&& person) auto& new_person = m_persons.back(); new_person.set_assigned_location(m_cemetery_id); - if (m_local_population_cache.is_valid()) { - ++m_local_population_cache.write()[new_person.get_location().index]; + if (m_is_local_population_cache_valid) { + ++m_local_population_cache[new_person.get_location().index]; } return new_id; } @@ -166,14 +165,14 @@ void World::build_compute_local_population_cache() const { const auto num_locations = m_locations.size(); const auto num_persons = m_persons.size(); - m_local_population_cache.write().resize(num_locations); + m_local_population_cache.resize(num_locations); PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_locations; i++) { - m_local_population_cache.write()[i] = 0.; + m_local_population_cache[i] = 0.; } // implicit taskloop barrier PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_persons; i++) { - ++m_local_population_cache.write()[m_persons[i].get_location().index]; + ++m_local_population_cache[m_persons[i].get_location().index]; } // implicit taskloop barrier } // implicit single barrier } @@ -183,19 +182,16 @@ void World::build_exposure_caches() PRAGMA_OMP(single) { const size_t num_locations = m_locations.size(); - m_air_exposure_rates_cache.write().resize(num_locations); - m_contact_exposure_rates_cache.write().resize(num_locations); + m_air_exposure_rates_cache.resize(num_locations); + m_contact_exposure_rates_cache.resize(num_locations); PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_locations; i++) { - m_air_exposure_rates_cache.write()[i].resize( - {CellIndex(m_locations[i].get_cells().size()), VirusVariant::Count}); - m_contact_exposure_rates_cache.write()[i].resize({CellIndex(m_locations[i].get_cells().size()), - VirusVariant::Count, - AgeGroup(parameters.get_num_groups())}); + m_air_exposure_rates_cache[i].resize({CellIndex(m_locations[i].get_cells().size()), VirusVariant::Count}); + m_contact_exposure_rates_cache[i].resize({CellIndex(m_locations[i].get_cells().size()), VirusVariant::Count, + AgeGroup(parameters.get_num_groups())}); } // implicit taskloop barrier - m_air_exposure_rates_cache.invalidate(); - m_contact_exposure_rates_cache.invalidate(); - m_exposure_rates_need_rebuild = false; + m_are_exposure_caches_valid = false; + m_exposure_caches_need_rebuild = false; } // implicit single barrier } @@ -204,7 +200,7 @@ void World::compute_exposure_caches(TimePoint t, TimeSpan dt) PRAGMA_OMP(single) { // if cache shape was changed (e.g. by add_location), rebuild it - if (m_exposure_rates_need_rebuild) { + if (m_exposure_caches_need_rebuild) { build_exposure_caches(); } // use these const values to help omp recognize that the for loops are bounded @@ -216,11 +212,11 @@ void World::compute_exposure_caches(TimePoint t, TimeSpan dt) PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_locations; ++i) { const auto index = i; - auto& local_air_exposure = m_air_exposure_rates_cache.write()[index]; + auto& local_air_exposure = m_air_exposure_rates_cache[index]; std::for_each(local_air_exposure.begin(), local_air_exposure.end(), [](auto& r) { r = 0.0; }); - auto& local_contact_exposure = m_contact_exposure_rates_cache.write()[index]; + auto& local_contact_exposure = m_contact_exposure_rates_cache[index]; std::for_each(local_contact_exposure.begin(), local_contact_exposure.end(), [](auto& r) { r = 0.0; }); @@ -232,8 +228,8 @@ void World::compute_exposure_caches(TimePoint t, TimeSpan dt) for (size_t i = 0; i < num_persons; ++i) { const Person& person = m_persons[i]; const auto location = person.get_location().index; - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache.write()[location], - m_contact_exposure_rates_cache.write()[location], person, + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], + m_contact_exposure_rates_cache[location], person, get_location(person.get_person_id()), t, dt); } // implicit taskloop barrier } // implicit single barrier @@ -243,13 +239,12 @@ void World::begin_step(TimePoint t, TimeSpan dt) { m_testing_strategy.update_activity_status(t); - if (!m_local_population_cache.is_valid()) { + if (!m_is_local_population_cache_valid) { build_compute_local_population_cache(); - m_local_population_cache.validate(); + m_is_local_population_cache_valid = true; } compute_exposure_caches(t, dt); - m_air_exposure_rates_cache.validate(); - m_contact_exposure_rates_cache.validate(); + m_are_exposure_caches_valid = true; } auto World::get_locations() const -> Range> diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 17cd5e5136..87ef424a60 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -20,7 +20,6 @@ #ifndef MIO_ABM_WORLD_H #define MIO_ABM_WORLD_H -#include "abm/caching.h" #include "abm/functions.h" #include "abm/location_type.h" #include "abm/movement_data.h" @@ -87,7 +86,9 @@ class World , m_local_population_cache() , m_air_exposure_rates_cache() , m_contact_exposure_rates_cache() - , m_exposure_rates_need_rebuild(true) + , m_is_local_population_cache_valid(false) + , m_are_exposure_caches_valid(false) + , m_exposure_caches_need_rebuild(true) , m_persons(other.m_persons) , m_locations(other.m_locations) , m_has_locations(other.m_has_locations) @@ -99,9 +100,9 @@ class World , m_rng(other.m_rng) { } - World(World&& other) = default; // TODO? - World& operator=(World&& other) = default; - World& operator=(const World&) = delete; + World& operator=(const World&) = default; + World(World&&) = default; + World& operator=(World&&) = default; /** * serialize this. @@ -331,7 +332,6 @@ class World */ size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { - // TODO: if used during simulation, this function may be very slow! return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { return p.get_location() == location && p.get_infection_state(t) == state; }); @@ -344,10 +344,10 @@ class World */ size_t get_number_persons(LocationId location) const { - if (!m_local_population_cache.is_valid()) { + if (!m_is_local_population_cache_valid) { build_compute_local_population_cache(); } - return m_local_population_cache.read()[location.index]; + return m_local_population_cache[location.index]; } // move a person to another location. this requires that location is part of this world. @@ -358,11 +358,10 @@ class World const bool has_moved = mio::abm::migrate(get_person(person), get_location(destination), cells, mode); // if the person has moved, invalidate exposure caches but keep population caches valid if (has_moved) { - m_air_exposure_rates_cache.invalidate(); - m_contact_exposure_rates_cache.invalidate(); - if (m_local_population_cache.is_valid()) { - --m_local_population_cache.write()[origin.index]; - ++m_local_population_cache.write()[destination.index]; + m_are_exposure_caches_valid = false; + if (m_is_local_population_cache_valid) { + --m_local_population_cache[origin.index]; + ++m_local_population_cache[destination.index]; } } } @@ -378,17 +377,15 @@ class World inline void interact(PersonalRandomNumberGenerator& personal_rng, PersonId person, TimePoint t, TimeSpan dt, const Parameters& global_parameters) { - if (!m_air_exposure_rates_cache.is_valid() || !m_contact_exposure_rates_cache.is_valid()) { + if (!m_are_exposure_caches_valid) { // checking caches is only needed for external calls // during simulation (i.e. in evolve()), the caches are computed in begin_step compute_exposure_caches(t, dt); - m_air_exposure_rates_cache.validate(); - m_contact_exposure_rates_cache.validate(); + m_are_exposure_caches_valid = true; } mio::abm::interact(personal_rng, get_person(person), get_location(person), - m_air_exposure_rates_cache.read()[get_location(person).get_index()], - m_contact_exposure_rates_cache.read()[get_location(person).get_index()], t, dt, - global_parameters); + m_air_exposure_rates_cache[get_location(person).get_index()], + m_contact_exposure_rates_cache[get_location(person).get_index()], t, dt, global_parameters); } /** @@ -456,13 +453,15 @@ class World */ void compute_exposure_caches(TimePoint t, TimeSpan dt); - mutable Cache> + mutable Eigen::Matrix m_local_population_cache; ///< Current number of Persons in a given location. - Cache> + Eigen::Matrix m_air_exposure_rates_cache; ///< Cache for local exposure through droplets in #transmissions/day. - Cache> + Eigen::Matrix m_contact_exposure_rates_cache; ///< Cache for local exposure through contacts in #transmissions/day. - bool m_exposure_rates_need_rebuild = true; + bool m_is_local_population_cache_valid = false; + bool m_are_exposure_caches_valid = false; + bool m_exposure_caches_need_rebuild = true; std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index bf92297772..21ec73c750 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -41,3 +41,30 @@ mio::abm::PersonId add_test_person(mio::abm::World& world, mio::abm::LocationId { return world.add_person(make_test_person(world.get_location(loc_id), age, infection_state, t, world.parameters)); } + +void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, + const mio::abm::Location& location, const std::vector& local_population, + const mio::abm::TimePoint t, const mio::abm::TimeSpan dt, + const mio::abm::Parameters& global_parameters) +{ + // allocate and initialize air exposures with 0 + mio::abm::AirExposureRates local_air_exposure; + local_air_exposure.resize({mio::abm::CellIndex(location.get_cells().size()), mio::abm::VirusVariant::Count}); + std::for_each(local_air_exposure.begin(), local_air_exposure.end(), [](auto& r) { + r = 0.0; + }); + // allocate and initialize contact exposures with 0 + mio::abm::ContactExposureRates local_contact_exposure; + local_contact_exposure.resize({mio::abm::CellIndex(location.get_cells().size()), mio::abm::VirusVariant::Count, + mio::AgeGroup(global_parameters.get_num_groups())}); + std::for_each(local_contact_exposure.begin(), local_contact_exposure.end(), [](auto& r) { + r = 0.0; + }); + // caclculate current exposures + for (const mio::abm::Person& p : local_population) { + add_exposure_contribution(local_air_exposure, local_contact_exposure, p, location, t, dt); + } + // run interaction + mio::abm::interact(personal_rng, person, location, local_air_exposure, local_contact_exposure, t, dt, + global_parameters); +} diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 015eff3011..d98490e226 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -20,7 +20,8 @@ #ifndef ABM_HELPERS_H #define ABM_HELPERS_H -#include "abm/abm.h" +#include "abm/world.h" + #include "gmock/gmock.h" // Assign the name to general age group. @@ -104,4 +105,10 @@ mio::abm::PersonId add_test_person(mio::abm::World& world, mio::abm::LocationId mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0)); +/// @brief mio::abm::interact, but it computes the correct exposures for you +void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, + const mio::abm::Location& location, const std::vector& local_population, + const mio::abm::TimePoint t, const mio::abm::TimeSpan dt, + const mio::abm::Parameters& global_parameters); + #endif //ABM_HELPERS_H diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index fbbc022789..6180b2fb3d 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -21,6 +21,7 @@ #include "abm/location_type.h" #include "abm/person.h" #include "abm_helpers.h" +#include "memilio/math/interpolation.h" #include "memilio/utils/random_number_generator.h" #include "abm_helpers.h" diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index df4850d8ca..654e5e9fc8 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -18,24 +18,12 @@ * limitations under the License. */ -#include "abm/functions.h" #include "abm/parameters.h" #include "abm/person.h" #include "abm/world.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" -// TODO; this test no longer makes sense here, consider changing it and/or moving its contents to world -// TEST(TestLocation, init) -// { -// mio::abm::Location location(mio::abm::LocationType::School, 0, num_age_groups); -// for (mio::abm::InfectionState i = mio::abm::InfectionState(0); i < mio::abm::InfectionState::Count; -// i = mio::abm::InfectionState(size_t(i) + 1)) { -// ASSERT_EQ(location.get_subpopulation(mio::abm::TimePoint(0), i), 0); -// } -// EXPECT_EQ(location.get_number_persons(), 0); -// } - TEST(TestLocation, copyLocation) { auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); @@ -63,101 +51,6 @@ TEST(TestLocation, getIndex) ASSERT_EQ((int)location.get_index(), 0); } -// TODO: Make sure removing this is correct, move if appropriate. reason: location no longer stores person -// TEST(TestLocation, addRemovePerson) -// { -// mio::abm::World world(0); // num_agegroups is not needed in this test -// auto& home = world.get_location(world.add_location(mio::abm::LocationType::Home, 1)); -// auto& location = world.get_location(world.add_location(mio::abm::LocationType::PublicTransport, 3)); - -// auto person1 = make_test_person(home, age_group_5_to_14, mio::abm::InfectionState::InfectedSymptoms); -// auto person2 = make_test_person(home, age_group_5_to_14, mio::abm::InfectionState::InfectedSymptoms); -// auto person3 = make_test_person(home, age_group_35_to_59, mio::abm::InfectionState::Exposed); - -// home.add_person(person1, {0}); -// home.add_person(person2, {0}); -// home.add_person(person3, {0}); - -// world.migrate(person1, location, mio::abm::TransportMode::Unknown, {0, 1}); -// world.migrate(person2, location, mio::abm::TransportMode::Unknown, {0}); -// world.migrate(person3, location, mio::abm::TransportMode::Unknown, {0, 1}); - -// auto t = mio::abm::TimePoint(0); -// ASSERT_EQ(home.get_number_persons(), 0u); -// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 2); -// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); -// ASSERT_EQ(location.get_cells()[0].m_persons.size(), 3u); -// ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); -// ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); - -// location.remove_person(person2); - -// EXPECT_EQ(location.get_number_persons(), 2u); -// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 1); -// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); -// ASSERT_EQ(location.get_cells()[0].m_persons.size(), 2u); -// ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); -// ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); -// } - -// TODO: outdated. maybe repurpose for new global functions -// TEST(TestLocation, CacheExposureRate) -// { -// using testing::Return; - -// auto rng = mio::RandomNumberGenerator(); - -// mio::AgeGroup age = -// mio::AgeGroup(mio::UniformIntDistribution::get_instance()(rng, 0, int(num_age_groups - 1))); -// mio::abm::VirusVariant variant = mio::abm::VirusVariant( -// mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::VirusVariant::Count) - 1)); - -// auto t = mio::abm::TimePoint(0); -// auto dt = mio::abm::seconds(10000); - -// mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); - -// // setup a location with some chance of exposure -// mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups, 1); -// mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, num_age_groups, 3); -// auto infected1 = mio::abm::Person(rng, home, age); -// auto rng_infected1 = mio::abm::PersonalRandomNumberGenerator(rng, infected1); -// infected1.add_new_infection( -// mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); -// mio::abm::migrate(infected1, location, {0}); -// auto infected2 = mio::abm::Person(rng, home, age); -// auto rng_infected2 = mio::abm::PersonalRandomNumberGenerator(rng, infected2); -// infected2.add_new_infection( -// mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); -// mio::abm::migrate(infected2, location, {0, 1}); - -// location.get_cells()[0].m_persons.emplace_back(&infected1); -// location.get_cells()[0].m_persons.emplace_back(&infected2); -// location.get_cells()[1].m_persons.emplace_back(&infected2); - -// //cache precomputed results -// location.cache_exposure_rates(t, dt, num_age_groups); - -// EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, 1e-14); -// EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.015015859523894731, 1e-14); -// EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), 0.0075079297619473654, -// 1e-14); -// EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.0075079297619473654, 1e-14); -// EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); -// EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); - -// // should also work with capacities -// location.get_infection_parameters().set(true); -// location.set_capacity(2, 22, 0); // Capacity for Cell 1 -// location.set_capacity(2, 22, 1); // Capacity for Cell 2 -// location.set_capacity(2, 22, 2); // Capacity for Cell 3 -// location.cache_exposure_rates(t, dt, num_age_groups); - -// EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); -// EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); -// EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); -// } - TEST(TestLocation, reachCapacity) { using testing::Return; @@ -268,12 +161,12 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); auto person_rng = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); - mio::abm::interact_testing(person_rng, susceptible, location, local_population, t, dt, params); + interact_testing(person_rng, susceptible, location, local_population, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - mio::abm::interact_testing(person_rng, susceptible, location, local_population, t, dt, params); + interact_testing(person_rng, susceptible, location, local_population, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); } diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index 54ba344b13..90038f53b6 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -17,6 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/lockdown_rules.h" +#include "abm/migration_rules.h" #include "abm/person.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index 4ddae32e1d..5b457823dd 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -82,12 +82,10 @@ TEST(TestMasks, maskProtection) mock_exponential_dist; auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person1); - mio::abm::interact_testing(p1_rng, susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, - params); + interact_testing(p1_rng, susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); auto p2_rng = mio::abm::PersonalRandomNumberGenerator(rng, susc_person2); - mio::abm::interact_testing(p2_rng, susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, - params); + interact_testing(p2_rng, susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params); // The person susc_person1 should have full protection against an infection, susc_person2 not ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index c3de3ac456..75bce23a76 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/functions.h" +#include "abm/migration_rules.h" #include "abm/person.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" @@ -417,7 +418,7 @@ TEST(TestMigrationRules, shop_return) auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, shop); - mio::abm::interact_testing(rng_p, p, shop, {p}, t, dt, params); //person only returns home after some time passed + interact_testing(rng_p, p, shop, {p}, t, dt, params); //person only returns home after some time passed ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); @@ -467,7 +468,7 @@ TEST(TestMigrationRules, event_return) auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); mio::abm::migrate(p, social_event); - mio::abm::interact_testing(rng_p, p, social_event, {p}, t, dt, params); + interact_testing(rng_p, p, social_event, {p}, t, dt, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 3bb90f84d4..5dae6a3b34 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -19,6 +19,7 @@ */ #include "abm/functions.h" #include "abm/location_type.h" +#include "abm/migration_rules.h" #include "abm/person.h" #include "abm/person_id.h" @@ -208,7 +209,7 @@ TEST(TestPerson, interact) auto person = mio::abm::Person(rng, loc.get_id(), age_group_15_to_34); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days - mio::abm::interact_testing(rng_person, person, loc, {person}, t, dt, infection_parameters); + interact_testing(rng_person, person, loc, {person}, t, dt, infection_parameters); EXPECT_EQ(person.get_time_at_location(), dt); } diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index e6b016b1d4..4edaa34f61 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -17,7 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "abm/movement_data.h" #include "abm/person.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" @@ -70,8 +69,8 @@ TEST(TestWorld, addPerson) world.add_person(location, age_group_35_to_59); ASSERT_EQ(world.get_persons().size(), 2); - // ASSERT_EQ(&world.get_persons()[0], &p1); // TODO: rethink these, add new add_person fcts - // ASSERT_EQ(&world.get_persons()[1], &p2); + ASSERT_EQ(world.get_person(0).get_age(), age_group_15_to_34); + ASSERT_EQ(world.get_person(1).get_age(), age_group_35_to_59); } TEST(TestWorld, getSubpopulationCombined) @@ -557,183 +556,3 @@ TEST(TestWorld, checkParameterConstraints) params.get() = mio::abm::TimePoint(-2); ASSERT_EQ(params.check_constraints(), true); } - -TEST(TestWorld, copyWorld) // TODO: this needs either a rewrite or to be removed -{ - auto world = mio::abm::World(num_age_groups); - auto rng = mio::RandomNumberGenerator(); - - world.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 4.; - world.use_migration_rules(false); - - auto school_id1 = world.add_location(mio::abm::LocationType::School); - auto school_id2 = world.add_location(mio::abm::LocationType::School); - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto home_id = world.add_location(mio::abm::LocationType::Home); - - auto& school1 = world.get_location(school_id1); - school1.set_required_mask(mio::abm::MaskType::Surgical); - school1.set_npi_active(true); - auto& school2 = world.get_location(school_id2); - school2.set_required_mask(mio::abm::MaskType::FFP2); - auto& work = world.get_location(work_id); - auto& home = world.get_location(home_id); - - auto pid1 = world.add_person(school_id1, age_group_0_to_4); - auto pid2 = world.add_person(school_id2, age_group_15_to_34); - - auto& p1 = world.get_person(pid1); - auto& p2 = world.get_person(pid2); - - auto rng_p1 = mio::abm::PersonalRandomNumberGenerator(rng, p1); - p1.add_new_infection(mio::abm::Infection(rng_p1, mio::abm::VirusVariant::Wildtype, p1.get_age(), world.parameters, - mio::abm::TimePoint(0))); - p2.set_mask_preferences(std::vector(15, 0.2)); - - mio::abm::TripList& trip_data = world.get_trip_list(); - mio::abm::Trip trip1(p1.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), school_id1, home_id); - mio::abm::Trip trip2(p2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id); - trip_data.add_trip(trip1); - trip_data.add_trip(trip2); - - auto infection_params = - world.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] - .value(); - - auto copied_world = mio::abm::World(world); - auto copied_infection_params = - copied_world.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] - .value(); - - // Assert the parameters, trips, locations and persons of copied world are logically equal to that of original world - ASSERT_EQ(copied_infection_params, infection_params); - ASSERT_EQ(copied_world.use_migration_rules(), world.use_migration_rules()); - - mio::abm::TripList& copied_trip_data = copied_world.get_trip_list(); - ASSERT_EQ(copied_trip_data.num_trips(), trip_data.num_trips()); - ASSERT_EQ(copied_trip_data.get_next_trip(false).person_id, trip_data.get_next_trip(false).person_id); - ASSERT_EQ(copied_trip_data.get_next_trip(false).migration_destination, - trip_data.get_next_trip(false).migration_destination); - ASSERT_EQ(copied_trip_data.get_next_trip(false).migration_origin, trip_data.get_next_trip(false).migration_origin); - copied_trip_data.increase_index(); - trip_data.increase_index(); - ASSERT_EQ(copied_trip_data.get_next_trip(false).person_id, trip_data.get_next_trip(false).person_id); - ASSERT_EQ(copied_trip_data.get_next_trip(false).migration_destination, - trip_data.get_next_trip(false).migration_destination); - ASSERT_EQ(copied_trip_data.get_next_trip(false).migration_origin, trip_data.get_next_trip(false).migration_origin); - - ASSERT_EQ(copied_world.get_locations().size(), world.get_locations().size()); - ASSERT_EQ(copied_world.get_locations()[1].get_index(), world.get_locations()[1].get_index()); - ASSERT_EQ(copied_world.get_locations()[2].get_index(), world.get_locations()[2].get_index()); - ASSERT_EQ(copied_world.get_locations()[3].get_index(), world.get_locations()[3].get_index()); - ASSERT_EQ(copied_world.get_locations()[4].get_index(), world.get_locations()[4].get_index()); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[1].get_id()), - world.get_number_persons(world.get_locations()[1].get_id())); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[2].get_id()), - world.get_number_persons(world.get_locations()[2].get_id())); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[3].get_id()), - world.get_number_persons(world.get_locations()[3].get_id())); - ASSERT_EQ(copied_world.get_number_persons(copied_world.get_locations()[4].get_id()), - world.get_number_persons(world.get_locations()[4].get_id())); - ASSERT_EQ(copied_world.get_locations()[1].get_npi_active(), world.get_locations()[1].get_npi_active()); - ASSERT_EQ(copied_world.get_locations()[2].get_npi_active(), world.get_locations()[2].get_npi_active()); - ASSERT_EQ(copied_world.get_locations()[3].get_npi_active(), world.get_locations()[3].get_npi_active()); - ASSERT_EQ(copied_world.get_locations()[4].get_npi_active(), world.get_locations()[4].get_npi_active()); - ASSERT_EQ(copied_world.get_locations()[1].get_required_mask(), world.get_locations()[1].get_required_mask()); - ASSERT_EQ(copied_world.get_locations()[2].get_required_mask(), world.get_locations()[2].get_required_mask()); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[1].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[1].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed)); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[1].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Susceptible), - world.get_subpopulation(world.get_locations()[1].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Susceptible)); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[2].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[2].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed)); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[2].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Susceptible), - world.get_subpopulation(world.get_locations()[2].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Susceptible)); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[3].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[3].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed)); - ASSERT_EQ(copied_world.get_subpopulation(copied_world.get_locations()[4].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed), - world.get_subpopulation(world.get_locations()[4].get_id(), mio::abm::TimePoint(0), - mio::abm::InfectionState::Exposed)); - ASSERT_EQ(copied_world.get_locations()[1].get_cells().size(), world.get_locations()[1].get_cells().size()); - ASSERT_EQ(copied_world.get_locations()[2].get_cells().size(), world.get_locations()[2].get_cells().size()); - ASSERT_EQ(copied_world.get_locations()[3].get_cells().size(), world.get_locations()[2].get_cells().size()); - ASSERT_EQ(copied_world.get_locations()[4].get_cells().size(), world.get_locations()[2].get_cells().size()); - - ASSERT_EQ(copied_world.get_persons().size(), world.get_persons().size()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[0].get_person_id()).get_index(), - world.get_location(world.get_persons()[0].get_person_id()).get_index()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[1].get_person_id()).get_index(), - world.get_location(world.get_persons()[1].get_person_id()).get_index()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[0].get_person_id()).get_type(), - world.get_location(world.get_persons()[0].get_person_id()).get_type()); - ASSERT_EQ(copied_world.get_location(world.get_persons()[1].get_person_id()).get_type(), - world.get_location(world.get_persons()[1].get_person_id()).get_type()); - ASSERT_EQ(copied_world.get_persons()[0].get_infection().get_infection_state(mio::abm::TimePoint(0)), - world.get_persons()[0].get_infection().get_infection_state(mio::abm::TimePoint(0))); - ASSERT_EQ(copied_world.get_persons()[0].get_mask_compliance(mio::abm::LocationType::Home), - world.get_persons()[0].get_mask_compliance(mio::abm::LocationType::Home)); - ASSERT_EQ(copied_world.get_persons()[0].get_mask_compliance(mio::abm::LocationType::Work), - world.get_persons()[0].get_mask_compliance(mio::abm::LocationType::Work)); - ASSERT_EQ(copied_world.get_persons()[1].get_mask_compliance(mio::abm::LocationType::Home), - world.get_persons()[1].get_mask_compliance(mio::abm::LocationType::Home)); - ASSERT_EQ(copied_world.get_persons()[1].get_mask_compliance(mio::abm::LocationType::Work), - world.get_persons()[1].get_mask_compliance(mio::abm::LocationType::Work)); - - // Assert the parameters, trips, locations, persons and their member variables of copied world are stored in different address of original world - ASSERT_NE(&(copied_world.parameters), &world.parameters); - ASSERT_NE(&(copied_world.get_trip_list()), &trip_data); - - ASSERT_NE(&copied_world.get_locations()[1], &world.get_locations()[1]); - ASSERT_NE(&copied_world.get_locations()[2], &world.get_locations()[2]); - ASSERT_NE(&copied_world.get_locations()[3], &world.get_locations()[3]); - ASSERT_NE(&copied_world.get_locations()[4], &world.get_locations()[4]); - ASSERT_NE(&copied_world.get_locations()[1].get_cells(), &world.get_locations()[1].get_cells()); - ASSERT_NE(&copied_world.get_locations()[2].get_cells(), &world.get_locations()[2].get_cells()); - ASSERT_NE(&copied_world.get_locations()[3].get_cells(), &world.get_locations()[3].get_cells()); - ASSERT_NE(&copied_world.get_locations()[4].get_cells(), &world.get_locations()[4].get_cells()); - ASSERT_NE(&(copied_world.get_locations()[1].get_cells()[0]), &(world.get_locations()[1].get_cells()[0])); - ASSERT_NE(&(copied_world.get_locations()[2].get_cells()[0]), &(world.get_locations()[2].get_cells()[0])); - // ASSERT_NE(&(copied_world.get_locations()[1].get_cells()[0].m_persons[0]), - // &(world.get_locations()[1].get_cells()[0].m_persons[0])); - // ASSERT_NE(&(copied_world.get_locations()[2].get_cells()[0].m_persons[0]), - // &(world.get_locations()[2].get_cells()[0].m_persons[0])); - - ASSERT_NE(&copied_world.get_persons()[0], &world.get_persons()[0]); - ASSERT_NE(&copied_world.get_persons()[1], &world.get_persons()[1]); - ASSERT_NE(&copied_world.get_location(copied_world.get_persons()[0].get_person_id()), - &world.get_location(world.get_persons()[0].get_person_id())); - ASSERT_NE(&copied_world.get_location(copied_world.get_persons()[1].get_person_id()), - &world.get_location(world.get_persons()[1].get_person_id())); - ASSERT_NE(&(copied_world.get_locations()[1]), &(world.get_locations()[1])); - ASSERT_NE(&(copied_world.get_locations()[2]), &(world.get_locations()[2])); - ASSERT_NE(&(copied_world.get_persons()[0].get_assigned_locations()), - &world.get_persons()[0].get_assigned_locations()); - ASSERT_NE(&(copied_world.get_persons()[1].get_assigned_locations()), - &world.get_persons()[1].get_assigned_locations()); - ASSERT_NE(&(copied_world.get_persons()[0].get_infection()), &world.get_persons()[0].get_infection()); - ASSERT_NE(&(copied_world.get_persons()[0].get_mask()), &world.get_persons()[0].get_mask()); - ASSERT_NE(&(copied_world.get_persons()[1].get_mask()), &world.get_persons()[1].get_mask()); - ASSERT_NE(&(copied_world.get_persons()[0].get_cells()), &world.get_persons()[0].get_cells()); - ASSERT_NE(&(copied_world.get_persons()[1].get_cells()), &world.get_persons()[1].get_cells()); - - // Evolve the world and check that the copied world has not evolved - copied_world.migrate(copied_world.get_persons()[0].get_person_id(), work.get_id(), mio::abm::TransportMode::Unknown, - {0}); - copied_world.migrate(copied_world.get_persons()[1].get_person_id(), home.get_id(), mio::abm::TransportMode::Unknown, - {0}); - ASSERT_NE(copied_world.get_location(copied_world.get_persons()[0].get_person_id()).get_type(), - world.get_location(world.get_persons()[0].get_person_id()).get_type()); - ASSERT_NE(copied_world.get_location(copied_world.get_persons()[1].get_person_id()).get_type(), - world.get_location(world.get_persons()[1].get_person_id()).get_type()); -} From bdbd2071e500f72ad4473e96dd8d193c5e8a700c Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:19:54 +0100 Subject: [PATCH 25/54] add credit for authors of the original code --- cpp/models/abm/functions.cpp | 2 +- cpp/models/abm/functions.h | 2 +- cpp/models/abm/personal_rng.cpp | 2 +- cpp/models/abm/personal_rng.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index 76dc7646d6..b0d12f0035 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2020-2024 MEmilio * -* Authors: Rene Schmieding +* Authors: Daniel Abele, Elisabeth Kluth, Khoa Nguyen, David Kerkmann, Rene Schmieding * * Contact: Martin J. Kuehn * diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h index e02efc8ec5..b7db153561 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/functions.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2020-2024 MEmilio * -* Authors: Rene Schmieding +* Authors: Daniel Abele, Elisabeth Kluth, Khoa Nguyen, David Kerkmann, Rene Schmieding * * Contact: Martin J. Kuehn * diff --git a/cpp/models/abm/personal_rng.cpp b/cpp/models/abm/personal_rng.cpp index 23c38c6b87..36a552d18f 100644 --- a/cpp/models/abm/personal_rng.cpp +++ b/cpp/models/abm/personal_rng.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2020-2024 MEmilio * -* Authors: Rene Schmieding +* Authors: Daniel Abele, Elisabeth Kluth, David Kerkmann, Khoa Nguyen, Rene Schmieding * * Contact: Martin J. Kuehn * diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h index 23b7213e53..4bf2b3245e 100644 --- a/cpp/models/abm/personal_rng.h +++ b/cpp/models/abm/personal_rng.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2020-2024 MEmilio * -* Authors: Rene Schmieding +* Authors: Daniel Abele, Elisabeth Kluth, David Kerkmann, Khoa Nguyen, Rene Schmieding * * Contact: Martin J. Kuehn * From b4d38515cdb6b15747b777748ad4f58d84817785 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:34:21 +0100 Subject: [PATCH 26/54] make some implicit conversions explicit --- cpp/models/abm/world.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index dad7a54399..edf1142767 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -56,7 +56,7 @@ PersonId World::add_person(Person&& person) assert(person.get_location().index < m_locations.size()); assert(person.get_age().get() < parameters.get_num_groups()); - PersonId new_id = static_cast(m_persons.size()); + PersonId new_id{static_cast(m_persons.size())}; m_persons.emplace_back(person, new_id); auto& new_person = m_persons.back(); new_person.set_assigned_location(m_cemetery_id); @@ -78,18 +78,18 @@ void World::evolve(TimePoint t, TimeSpan dt) void World::interaction(TimePoint t, TimeSpan dt) { - const auto num_persons = m_persons.size(); + const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (auto i = size_t(0); i < num_persons; ++i) { + for (uint32_t i = 0; i < num_persons; ++i) { interact(i, t, dt); } } void World::migration(TimePoint t, TimeSpan dt) { - const auto num_persons = m_persons.size(); + const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (auto i = size_t(0); i < num_persons; ++i) { + for (uint32_t i = 0; i < num_persons; ++i) { Person& person = m_persons[i]; auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); @@ -163,12 +163,12 @@ void World::build_compute_local_population_cache() const { PRAGMA_OMP(single) { - const auto num_locations = m_locations.size(); - const auto num_persons = m_persons.size(); + const size_t num_locations = m_locations.size(); + const size_t num_persons = m_persons.size(); m_local_population_cache.resize(num_locations); PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_locations; i++) { - m_local_population_cache[i] = 0.; + m_local_population_cache[i] = 0; } // implicit taskloop barrier PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_persons; i++) { From cfe81cbae723a7bbde6aa0dbe3c00bc6073a62d4 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:31:57 +0100 Subject: [PATCH 27/54] fix failing overload resolution by explicit casts --- .../memilio/simulation/abm.cpp | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index b8c9e86ab2..bad727691c 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -192,11 +192,22 @@ PYBIND11_MODULE(_simulation_abm, m) py::class_(m, "World") .def(py::init()) .def("add_location", &mio::abm::World::add_location, py::arg("location_type"), py::arg("num_cells") = 1) - .def("add_person", &mio::abm::World::add_person, py::arg("location_id"), py::arg("age_group"), - py::return_value_policy::reference_internal) - .def_property_readonly("locations", &mio::abm::World::get_locations, - py::keep_alive<1, 0>{}) //keep this world alive while contents are referenced in ranges - .def_property_readonly("persons", &mio::abm::World::get_persons, py::keep_alive<1, 0>{}) + .def("add_person", + static_cast( + &mio::abm::World::add_person), + py::arg("location_id"), py::arg("age_group")) + .def_property_readonly( + "locations", + static_cast< + mio::Range> ( + mio::abm::World::*)() const>(&mio::abm::World::get_locations), + py::keep_alive<1, 0>{}) //keep this world alive while contents are referenced in ranges + .def_property_readonly( + "persons", + static_cast< + mio::Range> ( + mio::abm::World::*)() const>(&mio::abm::World::get_persons), + py::keep_alive<1, 0>{}) .def_property( "trip_list", py::overload_cast<>(&mio::abm::World::get_trip_list), [](mio::abm::World& self, const mio::abm::TripList& list) { From 24ff9a8705f35043f0d687ab6fd0d0f275a2727c Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:29:38 +0100 Subject: [PATCH 28/54] change argument order in mio::abm::migrate, add comments to World::migrate and World::interact --- cpp/models/abm/functions.cpp | 2 +- cpp/models/abm/functions.h | 6 +++--- cpp/models/abm/location.h | 9 --------- cpp/models/abm/world.h | 27 ++++++++++++++++----------- cpp/tests/test_abm_person.cpp | 11 +++++------ 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/functions.cpp index b0d12f0035..61cc449873 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/functions.cpp @@ -127,7 +127,7 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo } } -bool migrate(Person& person, const Location& destination, const std::vector& cells, const TransportMode mode) +bool migrate(Person& person, const Location& destination, const TransportMode mode, const std::vector& cells) { assert(std::all_of(cells.begin(), cells.end(), [&](const auto& cell) { return cell < destination.get_cells().size(); diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/functions.h index b7db153561..03982f4ad5 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/functions.h @@ -86,12 +86,12 @@ 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 be moved. * @param[in] destination The destination to move to. - * @param[in] cells The cells within the destination the person should be in. * @param[in] mode The transport mode the person uses to move. + * @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 migrate(Person& person, const Location& destination, const std::vector& cells = {0}, - const TransportMode mode = TransportMode::Unknown); +bool migrate(Person& person, const Location& destination, const TransportMode mode = TransportMode::Unknown, + const std::vector& cells = {0}); } // namespace abm } // namespace mio diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 705267ad46..1e18903ee8 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -68,15 +68,6 @@ struct Cell { * @return The relative cell size for the Cell. */ ScalarType compute_space_per_person_relative() const; - - /** - * @brief Get subpopulation of a particular #InfectionState in the Cell. - * @param[in] t TimePoint of querry. - * @param[in] state #InfectionState of interest. - * @return Amount of Person%s of the #InfectionState in the Cell. - */ - // size_t get_subpopulation(TimePoint t, InfectionState state) const; - }; // namespace mio /** diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 87ef424a60..8ceea7cb55 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -351,11 +351,18 @@ class World } // move a person to another location. this requires that location is part of this world. + /** + * @brief Let a person move to another location. + * @param[in] person PersonId of a person from this world. + * @param[in] destination LocationId of the location in this world, which the person should move to. + * @param[in] mode The transport mode the person uses to move. + * @param[in] cells The cells within the destination the person should be in. + */ inline void migrate(PersonId person, LocationId destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { LocationId origin = get_location(person).get_id(); - const bool has_moved = mio::abm::migrate(get_person(person), get_location(destination), cells, mode); + const bool has_moved = mio::abm::migrate(get_person(person), get_location(destination), mode, cells); // if the person has moved, invalidate exposure caches but keep population caches valid if (has_moved) { m_are_exposure_caches_valid = false; @@ -366,16 +373,13 @@ class World } } - // let a person interact with its current location + /** + * @brief Let a person interact with the population at its current location. + * @param[in] person PersonId of a person from this world. + * @param[in] t Time step of the simulation. + * @param[in] dt Step size of the simulation. + */ inline void interact(PersonId person, TimePoint t, TimeSpan dt) - { - auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person)); - interact(personal_rng, person, t, dt, parameters); - } - - // let a person interact with its current location - inline void interact(PersonalRandomNumberGenerator& personal_rng, PersonId person, TimePoint t, TimeSpan dt, - const Parameters& global_parameters) { if (!m_are_exposure_caches_valid) { // checking caches is only needed for external calls @@ -383,9 +387,10 @@ class World compute_exposure_caches(t, dt); m_are_exposure_caches_valid = true; } + auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person)); mio::abm::interact(personal_rng, get_person(person), get_location(person), m_air_exposure_rates_cache[get_location(person).get_index()], - m_contact_exposure_rates_cache[get_location(person).get_index()], t, dt, global_parameters); + m_contact_exposure_rates_cache[get_location(person).get_index()], t, dt, parameters); } /** diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 5dae6a3b34..ee7a687522 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -21,10 +21,9 @@ #include "abm/location_type.h" #include "abm/migration_rules.h" #include "abm/person.h" - -#include "abm/person_id.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" + #include TEST(TestPerson, init) @@ -64,17 +63,17 @@ TEST(TestPerson, migrate) mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 0, 6, 2); auto person = make_test_person(home, age_group_0_to_4, mio::abm::InfectionState::Recovered); - mio::abm::migrate(person, loc1, {0}); + mio::abm::migrate(person, loc1, mio::abm::TransportMode::Unknown, {0}); EXPECT_EQ(person.get_location(), loc1.get_id()); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); - mio::abm::migrate(person, loc2, {0}, mio::abm::TransportMode::Walking); + mio::abm::migrate(person, loc2, mio::abm::TransportMode::Walking, {0}); EXPECT_EQ(person.get_location(), loc2.get_id()); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); - mio::abm::migrate(person, loc3, {0, 1}, mio::abm::TransportMode::Bike); + mio::abm::migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1}); EXPECT_EQ(person.get_cells().size(), 2); EXPECT_EQ(person.get_cells()[0], 0u); @@ -193,7 +192,7 @@ TEST(TestPerson, getCells) mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 2); auto person = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - mio::abm::migrate(person, location, {0, 1}); + mio::abm::migrate(person, location, mio::abm::TransportMode::Unknown, {0, 1}); EXPECT_EQ(person.get_cells().size(), 2); // TODO: is this a meaningfull test? } From d8adc017c8aaf74492f05815effdb228a6a25036 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:30:39 +0100 Subject: [PATCH 29/54] update tests in line with changes to cpp code --- .../memilio/simulation/abm.cpp | 11 +++++++++-- .../memilio/simulation_test/test_abm.py | 17 +++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index bad727691c..5519a381c2 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -130,6 +130,10 @@ PYBIND11_MODULE(_simulation_abm, m) .def(py::self == py::self) .def(py::self != py::self); + py::class_(m, "PersonId").def(py::init([](uint32_t id) { + return mio::abm::PersonId{id}; + })); + py::class_(m, "Person") .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_)) @@ -169,8 +173,8 @@ PYBIND11_MODULE(_simulation_abm, m) }); //copying and moving of ranges enabled below, see PYMIO_IGNORE_VALUE_TYPE - pymio::bind_Range().get_locations())>(m, "_WorldLocationsRange"); - pymio::bind_Range().get_persons())>(m, "_WorldPersonsRange"); + pymio::bind_Range().get_locations())>(m, "_WorldLocationsRange"); + pymio::bind_Range().get_persons())>(m, "_WorldPersonsRange"); py::class_(m, "Trip") .def(py::init( &mio::abm::World::add_person), py::arg("location_id"), py::arg("age_group")) + .def("get_person", + static_cast(&mio::abm::World::get_person), + py::arg("id"), py::return_value_policy::reference_internal) .def_property_readonly( "locations", static_cast< diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index a77d1c31d4..4eca1cb8aa 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -70,10 +70,11 @@ def test_persons(self): home_id = world.add_location(abm.LocationType.Home) social_event_id = world.add_location(abm.LocationType.SocialEvent) - p1 = world.add_person( - home_id, mio.AgeGroup(2)) - p2 = world.add_person( - social_event_id, mio.AgeGroup(5)) + p1_id = world.add_person(home_id, mio.AgeGroup(2)) + p2_id = world.add_person(social_event_id, mio.AgeGroup(5)) + + p1 = world.get_person(p1_id) + p2 = world.get_person(p2_id) # check persons self.assertEqual(len(world.persons), 2) @@ -93,10 +94,10 @@ def test_simulation(self): home_id = abm.LocationId(0, abm.LocationType.Home) social_event_id = abm.LocationId(0, abm.LocationType.SocialEvent) work_id = abm.LocationId(0, abm.LocationType.Work) - p1 = world.add_person( - home_id, mio.AgeGroup(0)) - p2 = world.add_person( - home_id, mio.AgeGroup(2)) + p1_id = world.add_person(home_id, mio.AgeGroup(0)) + p2_id = world.add_person(home_id, mio.AgeGroup(2)) + p1 = world.get_person(p1_id) + p2 = world.get_person(p2_id) for type in abm.LocationType.values(): p1.set_assigned_location(abm.LocationId(0, type)) p2.set_assigned_location(abm.LocationId(0, type)) From f98a20b1e1a68f6d7de74365b31a92aac81e9489 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:33:25 +0100 Subject: [PATCH 30/54] remove abm.h this header only included other abm headers --- cpp/examples/abm_history_object.cpp | 9 +++--- cpp/examples/abm_minimal.cpp | 8 +++--- cpp/models/abm/abm.h | 42 ---------------------------- cpp/models/abm/analyze_result.h | 3 -- cpp/simulations/abm.cpp | 7 +++-- cpp/simulations/abm_braunschweig.cpp | 21 ++++++++------ cpp/tests/test_abm_household.cpp | 1 - cpp/tests/test_analyze_result.cpp | 6 ++-- 8 files changed, 28 insertions(+), 69 deletions(-) delete mode 100644 cpp/models/abm/abm.h diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index ce4de646a7..33f26e47a0 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -17,16 +17,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "abm/abm.h" #include "abm/household.h" -#include +#include "abm/lockdown_rules.h" +#include "abm/simulation.h" #include "abm/world.h" -#include "memilio/io/io.h" #include "abm/location_type.h" +#include "memilio/io/history.h" + #include #include -#include -#include "memilio/io/history.h" std::string convert_loc_id_to_string(std::tuple tuple_id) { diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index e9915a6811..7744b495ff 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -17,13 +17,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "abm/abm.h" #include "abm/household.h" -#include -#include -#include +#include "abm/lockdown_rules.h" +#include "abm/world.h" #include "abm/common_abm_loggers.h" +#include + int main() { // This is a minimal example with children and adults < 60 year old. diff --git a/cpp/models/abm/abm.h b/cpp/models/abm/abm.h deleted file mode 100644 index 4a53ff52c8..0000000000 --- a/cpp/models/abm/abm.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Copyright (C) 2020-2024 MEmilio -* -* Authors: Daniel Abele, Majid Abedi, Elisabeth Kluth -* -* 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. -*/ -/** single include header for ABM */ - -#ifndef MIO_ABM_H -#define MIO_ABM_H - -#include "abm/parameters.h" -#include "abm/simulation.h" -#include "abm/world.h" -#include "abm/person.h" -#include "abm/location.h" -#include "abm/location_type.h" -#include "memilio/math/interpolation.h" -#include "memilio/utils/random_number_generator.h" -#include "abm/migration_rules.h" -#include "abm/testing_strategy.h" -#include "abm/infection.h" -#include "abm/infection_state.h" -#include "abm/virus_variant.h" -#include "abm/vaccine.h" -#include "abm/household.h" -#include "abm/lockdown_rules.h" - -#endif diff --git a/cpp/models/abm/analyze_result.h b/cpp/models/abm/analyze_result.h index 0e93be84a9..05107af313 100644 --- a/cpp/models/abm/analyze_result.h +++ b/cpp/models/abm/analyze_result.h @@ -20,11 +20,8 @@ #ifndef ABM_ANALYZE_RESULT_H #define ABM_ANALYZE_RESULT_H -#include "abm/simulation.h" #include "abm/parameters.h" -#include "memilio/data/analyze_result.h" -#include #include namespace mio diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 05b73d25fa..0b8e61e967 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -17,13 +17,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "abm/abm.h" #include "abm/analyze_result.h" +#include "abm/common_abm_loggers.h" +#include "abm/household.h" +#include "abm/lockdown_rules.h" #include "memilio/io/result_io.h" +#include "memilio/math/interpolation.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/uncertain_value.h" -#include "boost/filesystem.hpp" -#include "abm/common_abm_loggers.h" namespace fs = boost::filesystem; diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index ade955d314..ce7f3b5e55 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -17,18 +17,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include -#include -#include -#include "abm/abm.h" -#include "abm/person_id.h" +#include "abm/common_abm_loggers.h" +#include "abm/lockdown_rules.h" +#include "abm/simulation.h" +#include "abm/world.h" +#include "memilio/epidemiology/age_group.h" +#include "memilio/io/io.h" #include "memilio/io/result_io.h" +#include "memilio/math/interpolation.h" #include "memilio/utils/uncertain_value.h" #include "boost/algorithm/string/split.hpp" #include "boost/algorithm/string/classification.hpp" -#include "abm/vaccine.h" -#include "abm/common_abm_loggers.h" + +#include +#include +#include namespace fs = boost::filesystem; @@ -1022,7 +1025,7 @@ mio::IOResult run(const std::string& input_file, const fs::path& result_di // Option to save the current run result to file if (save_result_result && save_single_runs) { auto result_dir_run = result_dir / ("abm_result_run_" + std::to_string(run_idx) + ".h5"); - BOOST_OUTCOME_TRY(save_result(ensemble_results.back(), loc_ids, 1, result_dir_run.string())); + BOOST_OUTCOME_TRY(mio::save_result(ensemble_results.back(), loc_ids, 1, result_dir_run.string())); } write_log_to_file_person_and_location_data(historyPersonInf); write_log_to_file_trip_data(historyPersonInfDelta); diff --git a/cpp/tests/test_abm_household.cpp b/cpp/tests/test_abm_household.cpp index a04b78e126..4ecadb2413 100644 --- a/cpp/tests/test_abm_household.cpp +++ b/cpp/tests/test_abm_household.cpp @@ -18,7 +18,6 @@ * limitations under the License. */ #include "abm/household.h" -#include "abm/abm.h" #include "abm_helpers.h" #include diff --git a/cpp/tests/test_analyze_result.cpp b/cpp/tests/test_analyze_result.cpp index 8648171b6a..0b2729f6d3 100644 --- a/cpp/tests/test_analyze_result.cpp +++ b/cpp/tests/test_analyze_result.cpp @@ -17,10 +17,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "memilio/compartments/simulation.h" -#include "ode_secir/analyze_result.h" #include "abm/analyze_result.h" +#include "abm/world.h" #include "matchers.h" +#include "memilio/compartments/simulation.h" +#include "ode_secir/analyze_result.h" + #include "gtest/gtest.h" #include "gmock/gmock.h" From 5363a8d1ee56f8c6fad5b928dc1cebec2268a592 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:23:57 +0100 Subject: [PATCH 31/54] update include in abm simulation binding --- pycode/memilio-simulation/memilio/simulation/abm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 5519a381c2..a26fbe92da 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -22,7 +22,7 @@ #include "utils/custom_index_array.h" #include "utils/parameter_set.h" #include "utils/index.h" -#include "abm/abm.h" +#include "abm/simulation.h" #include "pybind11/attr.h" #include "pybind11/cast.h" #include "pybind11/pybind11.h" From 9da767c1ffe2a298414b1640ea4734a1b032dd34 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:19:23 +0100 Subject: [PATCH 32/54] rename files functions.* to model_functions.* --- cpp/models/abm/CMakeLists.txt | 10 +++++----- cpp/models/abm/{functions.cpp => model_functions.cpp} | 2 +- cpp/models/abm/{functions.h => model_functions.h} | 6 +++--- cpp/models/abm/world.h | 3 +-- cpp/tests/test_abm_migration_rules.cpp | 3 ++- cpp/tests/test_abm_person.cpp | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) rename cpp/models/abm/{functions.cpp => model_functions.cpp} (99%) rename cpp/models/abm/{functions.h => model_functions.h} (97%) diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index e8cc12348e..d99d992fda 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -1,9 +1,4 @@ add_library(abm - functions.cpp - functions.h - person_id.h - personal_rng.cpp - personal_rng.h location.cpp location.h household.cpp @@ -12,6 +7,9 @@ add_library(abm simulation.h person.cpp person.h + person_id.h + personal_rng.cpp + personal_rng.h testing_strategy.cpp testing_strategy.h world.cpp @@ -21,6 +19,8 @@ add_library(abm parameters.cpp migration_rules.cpp migration_rules.h + model_functions.cpp + model_functions.h trip_list.cpp trip_list.h lockdown_rules.cpp diff --git a/cpp/models/abm/functions.cpp b/cpp/models/abm/model_functions.cpp similarity index 99% rename from cpp/models/abm/functions.cpp rename to cpp/models/abm/model_functions.cpp index 61cc449873..0208731c29 100644 --- a/cpp/models/abm/functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -18,7 +18,7 @@ * limitations under the License. */ -#include "abm/functions.h" +#include "abm/model_functions.h" #include "abm/location.h" #include "abm/person.h" #include "abm/random_events.h" diff --git a/cpp/models/abm/functions.h b/cpp/models/abm/model_functions.h similarity index 97% rename from cpp/models/abm/functions.h rename to cpp/models/abm/model_functions.h index 03982f4ad5..53dc95b073 100644 --- a/cpp/models/abm/functions.h +++ b/cpp/models/abm/model_functions.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef FUNCTIONS_H_ -#define FUNCTIONS_H_ +#ifndef MIO_ABM_MODEL_FUNCTIONS_H_ +#define MIO_ABM_MODEL_FUNCTIONS_H_ #include "abm/location.h" #include "abm/person.h" @@ -96,4 +96,4 @@ bool migrate(Person& person, const Location& destination, const TransportMode mo } // namespace abm } // namespace mio -#endif +#endif // MIO_ABM_MODEL_FUNCTIONS_H_ diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 8ceea7cb55..d2ecae2db2 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -20,7 +20,7 @@ #ifndef MIO_ABM_WORLD_H #define MIO_ABM_WORLD_H -#include "abm/functions.h" +#include "abm/model_functions.h" #include "abm/location_type.h" #include "abm/movement_data.h" #include "abm/parameters.h" @@ -80,7 +80,6 @@ class World parameters = params; } - //type is move-only for stable references of persons/locations World(const World& other) : parameters(other.parameters) , m_local_population_cache() diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 75bce23a76..75fdca92f0 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -17,7 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "abm/functions.h" +#include "abm/location_type.h" +#include "abm/model_functions.h" #include "abm/migration_rules.h" #include "abm/person.h" #include "abm_helpers.h" diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index ee7a687522..85fb758bf9 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "abm/functions.h" +#include "abm/model_functions.h" #include "abm/location_type.h" #include "abm/migration_rules.h" #include "abm/person.h" From 17544fd5c2067baffdb81495af4c83be71e893ea Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:20:36 +0100 Subject: [PATCH 33/54] add a test for random_migration --- cpp/tests/test_abm_migration_rules.cpp | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 75fdca92f0..5cbe0b75c2 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -24,6 +24,46 @@ #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" +TEST(TestMigrationRules, random_migration) +{ + ScopedMockDistribution>>> mock_exp_dist; + EXPECT_CALL(mock_exp_dist.get_mock(), invoke) + .Times(testing::Exactly(2)) + // values for v in mio::abm::random_transition + .WillOnce(testing::Return((t + dt) / 2.)) + .WillOnce(testing::Return(t + 2. * dt)); + + ScopedMockDistribution>>> mock_disc_dist; + EXPECT_CALL(mock_disc_dist.get_mock(), invoke) + .Times(testing::Exactly(1)) + // arbitrary value for random_idx in mio::abm::random_transition + .WillOnce(testing::Return(2)); + + int t = 0, dt = 1; + auto rng = mio::RandomNumberGenerator(); + auto default_type = mio::abm::LocationType::Cemetery; + auto person = mio::abm::Person(rng, {0, default_type}, age_group_15_to_34); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, person); + auto params = mio::abm::Parameters(num_age_groups); + + const auto random_migration = [&]() { + return mio::abm::random_migration(p_rng, person, mio::abm::TimePoint{t}, mio::abm::days(dt), params); + }; + + params.set(mio::abm::TimePoint{t + 2 * dt}); + + const auto dest0 = random_migration(); + EXPECT_NE(dest0, default_type) << "should return a new location type (via random_transition)"; + + const auto dest1 = random_migration(); + EXPECT_EQ(dest1, default_type) << "should return current location type (via random_transition)"; + + params.set(mio::abm::TimePoint{t}); + + const auto dest2 = random_migration(); + EXPECT_EQ(dest2, default_type) << "should return current location type"; +} + TEST(TestMigrationRules, student_goes_to_school) { auto rng = mio::RandomNumberGenerator(); From df5f7248efded00ff04ea7403e2aa0d5b6a7ad8b Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:30:38 +0100 Subject: [PATCH 34/54] fix initialization order in new test --- cpp/tests/test_abm_migration_rules.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 5cbe0b75c2..3c0cbd6323 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -26,6 +26,13 @@ TEST(TestMigrationRules, random_migration) { + int t = 0, dt = 1; + auto rng = mio::RandomNumberGenerator(); + auto default_type = mio::abm::LocationType::Cemetery; + auto person = mio::abm::Person(rng, {0, default_type}, age_group_15_to_34); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, person); + auto params = mio::abm::Parameters(num_age_groups); + ScopedMockDistribution>>> mock_exp_dist; EXPECT_CALL(mock_exp_dist.get_mock(), invoke) .Times(testing::Exactly(2)) @@ -39,13 +46,6 @@ TEST(TestMigrationRules, random_migration) // arbitrary value for random_idx in mio::abm::random_transition .WillOnce(testing::Return(2)); - int t = 0, dt = 1; - auto rng = mio::RandomNumberGenerator(); - auto default_type = mio::abm::LocationType::Cemetery; - auto person = mio::abm::Person(rng, {0, default_type}, age_group_15_to_34); - auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, person); - auto params = mio::abm::Parameters(num_age_groups); - const auto random_migration = [&]() { return mio::abm::random_migration(p_rng, person, mio::abm::TimePoint{t}, mio::abm::days(dt), params); }; From 751e81a9c409afd7f0ed34193cdda98c9789f337 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 13 May 2024 07:32:03 +0200 Subject: [PATCH 35/54] [ci skip] Apply suggestions from code review Co-authored-by: Khoa Nguyen <4763945+khoanguyen-dev@users.noreply.github.com> --- cpp/models/abm/infection.h | 2 +- cpp/models/abm/testing_strategy.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index 19a86343ef..ca9dae9bdd 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -53,7 +53,7 @@ class Infection /** * @brief Create an Infection for a single Person. * Draws a random infection course. - * @param[inout] rng PersonalRandomNumberGenerator for the Person. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] virus Virus type of the Infection. * @param[in] age AgeGroup to determine the ViralLoad course. * @param[in] params Parameters of the Model. diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index d54d273f0d..721971abd0 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -136,7 +136,7 @@ class TestingScheme /** * @brief Runs the TestingScheme and potentially tests a Person. - * @param[inout] rng PersonalRandomNumberGenerator for the Person being tested. + * @param[inout] rng PersonalRandomNumberGenerator of the Person being tested. * @param[in] person Person to check. * @param[in] t TimePoint when to run the scheme. * @return If the person is allowed to enter the Location by the scheme. @@ -213,7 +213,7 @@ class TestingStrategy /** * @brief Runs the TestingStrategy and potentially tests a Person. - * @param[inout] rng PersonalRandomNumberGenerator for the Person being tested. + * @param[inout] rng PersonalRandomNumberGenerator of the Person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the strategy. From bde288f90d35b0ee407f4ce26adb2194e410ead6 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:58:53 +0200 Subject: [PATCH 36/54] rename iterators --- cpp/models/abm/world.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index edf1142767..409b7f2c6f 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -80,8 +80,8 @@ void World::interaction(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t i = 0; i < num_persons; ++i) { - interact(i, t, dt); + for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { + interact(person_id, t, dt); } } @@ -89,21 +89,21 @@ void World::migration(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t i = 0; i < num_persons; ++i) { - Person& person = m_persons[i]; + for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { + Person& person = m_persons[person_id]; auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); auto try_migration_rule = [&](auto rule) -> bool { //run migration rule and check if migration can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); - const Location& target_location = get_location(find_location(target_type, i)); + const Location& target_location = get_location(find_location(target_type, person_id)); const LocationId current_location = person.get_location(); if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { if (target_location.get_id() != current_location && get_number_persons(target_location.get_id()) < target_location.get_capacity().persons) { bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { - migrate(i, target_location.get_id()); + migrate(person_id, target_location.get_id()); } return true; } From 361c58662e6632cc754965540c0f29fe3c230035 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:31:35 +0200 Subject: [PATCH 37/54] remove trailing underscore from include guards --- cpp/models/abm/model_functions.h | 6 +++--- cpp/models/abm/person_id.h | 6 +++--- cpp/models/abm/personal_rng.cpp | 2 +- cpp/models/abm/personal_rng.h | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/models/abm/model_functions.h b/cpp/models/abm/model_functions.h index 53dc95b073..6e4310c11a 100644 --- a/cpp/models/abm/model_functions.h +++ b/cpp/models/abm/model_functions.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef MIO_ABM_MODEL_FUNCTIONS_H_ -#define MIO_ABM_MODEL_FUNCTIONS_H_ +#ifndef MIO_ABM_MODEL_FUNCTIONS_H +#define MIO_ABM_MODEL_FUNCTIONS_H #include "abm/location.h" #include "abm/person.h" @@ -96,4 +96,4 @@ bool migrate(Person& person, const Location& destination, const TransportMode mo } // namespace abm } // namespace mio -#endif // MIO_ABM_MODEL_FUNCTIONS_H_ +#endif // MIO_ABM_MODEL_FUNCTIONS_H diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index cb4dccc6ae..4f99aafc05 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef MIO_ABM_PERSON_ID_H_ -#define MIO_ABM_PERSON_ID_H_ +#ifndef MIO_ABM_PERSON_ID_H +#define MIO_ABM_PERSON_ID_H #include "memilio/utils/type_safe.h" #include @@ -48,4 +48,4 @@ struct PersonId : mio::TypeSafe, public OperatorComparison

Date: Mon, 1 Jul 2024 14:01:10 +0200 Subject: [PATCH 38/54] delete developement artifacts --- cpp/tests/test_abm_location.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 10daf754a0..af86aa58e3 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -27,16 +27,11 @@ TEST(TestLocation, copyLocation) { auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - // auto person = make_test_person(location, age_group_5_to_14, mio::abm::InfectionState::InfectedSymptoms); - // EXPECT_EQ(location.get_number_persons(), 0); - // location.add_person(person); - // EXPECT_EQ(location.get_number_persons(), 1); auto copied_location = location; ASSERT_EQ(copied_location.get_type(), mio::abm::LocationType::School); ASSERT_EQ(copied_location.get_index(), location.get_index()); ASSERT_EQ(copied_location.get_cells().size(), location.get_cells().size()); - // EXPECT_EQ(copied_location.get_number_persons(), 0); } TEST(TestLocation, initCell) From 62d2f833b7b6b811505efb3d08a99077485ac473 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:25:38 +0200 Subject: [PATCH 39/54] [ci skip][wip] move location type to location --- cpp/examples/abm_history_object.cpp | 20 ++--- cpp/examples/abm_minimal.cpp | 13 ++-- cpp/examples/cli.cpp | 77 +++++++++++++++++++ cpp/models/abm/common_abm_loggers.h | 25 +++--- cpp/models/abm/household.cpp | 6 +- cpp/models/abm/location.cpp | 12 +-- cpp/models/abm/location.h | 72 ++++++++---------- cpp/models/abm/location_id.h | 56 ++++++++++++++ cpp/models/abm/location_type.h | 100 ------------------------ cpp/models/abm/migration_rules.cpp | 20 ++--- cpp/models/abm/model_functions.cpp | 11 +-- cpp/models/abm/person.cpp | 26 +++---- cpp/models/abm/person.h | 29 +++---- cpp/models/abm/person_id.h | 5 ++ cpp/models/abm/personal_rng.cpp | 2 +- cpp/models/abm/testing_strategy.cpp | 35 +++++---- cpp/models/abm/testing_strategy.h | 32 ++++++-- cpp/models/abm/trip_list.cpp | 1 + cpp/models/abm/trip_list.h | 33 ++++---- cpp/models/abm/world.cpp | 30 ++++---- cpp/models/abm/world.h | 28 ++++--- cpp/simulations/abm.cpp | 15 ++-- cpp/simulations/abm_braunschweig.cpp | 52 ++++++------- cpp/tests/abm_helpers.cpp | 2 +- cpp/tests/test_abm_location.cpp | 23 ++---- cpp/tests/test_abm_person.cpp | 8 +- cpp/tests/test_abm_simulation.cpp | 14 ++-- cpp/tests/test_abm_world.cpp | 110 +++++++++++++-------------- cpp/tests/test_json_serializer.cpp | 41 +++++----- 29 files changed, 468 insertions(+), 430 deletions(-) create mode 100644 cpp/models/abm/location_id.h diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index b7a3576a4b..1590502b05 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -77,7 +77,6 @@ int main() // Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 and 35-59) world.parameters.get().set_multiple({age_group_15_to_34, age_group_35_to_59}, true); - // There are 3 households for each household group. int n_households = 3; @@ -149,19 +148,20 @@ int main() } // Assign locations to the people - for (auto& person : persons) { + for (auto& person : world.get_persons()) { + const auto pid = person.get_id(); //assign shop and event - person.set_assigned_location(event); - person.set_assigned_location(shop); + world.assign_location(pid, event); + world.assign_location(pid, shop); //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); + world.assign_location(pid, hospital); + world.assign_location(pid, icu); //assign work/school to people depending on their age - if (person.get_age() == age_group_5_to_14) { - person.set_assigned_location(school); + if (person.get_age() == age_group_0_to_4) { + world.assign_location(pid, school); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - person.set_assigned_location(work); + world.assign_location(pid, work); } } @@ -186,7 +186,7 @@ int main() { Type location_ids{}; for (auto& location : sim.get_world().get_locations()) { - location_ids.push_back(std::make_tuple(location.get_type(), location.get_index())); + location_ids.push_back(std::make_tuple(location.get_type(), location.get_id().get())); } return location_ids; } diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 0fa3c7e934..b9b3bd2b43 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -128,18 +128,19 @@ int main() // Assign locations to the people for (auto& person : world.get_persons()) { + const auto id = person.get_id(); //assign shop and event - person.set_assigned_location(event); - person.set_assigned_location(shop); + world.assign_location(id, event); + world.assign_location(id, shop); //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); + world.assign_location(id, hospital); + world.assign_location(id, icu); //assign work/school to people depending on their age if (person.get_age() == age_group_0_to_4) { - person.set_assigned_location(school); + world.assign_location(id, school); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - person.set_assigned_location(work); + world.assign_location(id, work); } } diff --git a/cpp/examples/cli.cpp b/cpp/examples/cli.cpp index 7dcf0375d7..44665b3105 100644 --- a/cpp/examples/cli.cpp +++ b/cpp/examples/cli.cpp @@ -1,5 +1,15 @@ #include "memilio/io/cli.h" +#include "memilio/io/io.h" +#include "memilio/io/json_serializer.h" +#include +#include +#include +#include +#include +#include +#include +#include #include struct Name { @@ -56,8 +66,75 @@ struct Greeting { } }; +using HourlyContactMatrix = std::array; + +mio::IOResult read_hourly_contact_matrix(const std::string& csv_file) +{ + std::ifstream file(csv_file); + if (!file.good()) { + return mio::failure(mio::StatusCode::FileNotFound, "Could not open " + csv_file + "."); + } + + std::string reader; + + std::vector matrix_entries; + int current_hour = 0; + int t, row, col; + double val; + + HourlyContactMatrix hcm; + + // skip first line + std::getline(file, reader); + // possible EOF here + // read csv + while (std::getline(file, reader)) { + int status = sscanf(reader.c_str(), "%i,%i,%i,%lf\n", &t, &row, &col, &val); + + if (status != 4) { + return mio::failure(mio::StatusCode::InvalidFileFormat, + "Unexpected format while reading " + csv_file + ". Line reads \"" + reader + "\""); + } + + printf("%i,%i,%i,%lf\n", t, row, col, val); + + if (t > current_hour) { + size_t n = std::round(std::sqrt(matrix_entries.size())); + hcm[current_hour] = Eigen::MatrixXd(n, n); + for (size_t i = 0; i < matrix_entries.size(); i++) { + hcm[current_hour].data()[i] = matrix_entries[i]; + } + matrix_entries.clear(); + ++current_hour; + } + + matrix_entries.push_back(val); + } + + size_t n = std::round(std::sqrt(matrix_entries.size())); + hcm[current_hour] = Eigen::MatrixXd(n, n); + for (size_t i = 0; i < matrix_entries.size(); i++) { + hcm[current_hour].data()[i] = matrix_entries[i]; + } + + return mio::success(hcm); +} + int main(int argc, char** argv) { + std::string filename = "/home/schm_r6/Documents/24h_networks_csv/office_5_5.csv"; + + auto res = read_hourly_contact_matrix(filename); + + if (!res) { + std::cout << res.error().formatted_message(); + return res.error().code().value(); + } + + std::cout << mio::serialize_json(res.value()).value() << "\n"; + + return 0; + if (argc == 1) { // Print this if no arguments were given std::cout << "This is a small example on how to use the command line interface. " "Use \"-h\" to show the help dialogue.\n"; diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index d956f7dc82..5c8540a579 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -79,7 +79,8 @@ constexpr mio::abm::ActivityType guess_activity_type(mio::abm::LocationType curr * @brief Logger to log the LocationInformation of the simulation. */ struct LogLocationInformation : mio::LogOnce { - using Type = std::vector>; + using Type = std::vector< + std::tuple>; /** * @brief Log the LocationInformation of the simulation. * @param[in] sim The simulation of the abm. @@ -93,15 +94,14 @@ struct LogLocationInformation : mio::LogOnce { static Type log(const mio::abm::Simulation& sim) { Type location_information{}; - for (auto&& location : sim.get_world().get_locations()) { + for (auto& location : sim.get_world().get_locations()) { auto n_cells = location.get_cells().size(); int loc_capacity = 0; for (int i = 0; i < (int)n_cells; i++) { loc_capacity += location.get_capacity(i).persons; } - location_information.push_back(std::make_tuple(location.get_index(), location.get_type(), - location.get_geographical_location(), n_cells, - loc_capacity)); + location_information.push_back(std::make_tuple( + location.get_id(), location.get_type(), location.get_geographical_location(), n_cells, loc_capacity)); } return location_information; } @@ -111,7 +111,7 @@ struct LogLocationInformation : mio::LogOnce { * @brief Logger to log the Person%s Information in the simulation. */ struct LogPersonInformation : mio::LogOnce { - using Type = std::vector>; + using Type = std::vector>; /** * @brief Log the LocationInformation of the simulation. * @param[in] sim The simulation of the abm. @@ -126,8 +126,7 @@ struct LogPersonInformation : mio::LogOnce { person_information.reserve(sim.get_world().get_persons().size()); for (auto& person : sim.get_world().get_persons()) { person_information.push_back(std::make_tuple( - person.get_person_id(), - sim.get_world().find_location(mio::abm::LocationType::Home, person.get_person_id()).get_index(), + person.get_id(), sim.get_world().find_location(mio::abm::LocationType::Home, person.get_id()), person.get_age())); } return person_information; @@ -138,8 +137,8 @@ struct LogPersonInformation : mio::LogOnce { * @brief Logger to log Movement Data of the agents in the simulation. */ struct LogDataForMovement : mio::LogAlways { - using Type = std::vector>; + using Type = std::vector>; /** * @brief Log the Movement Data of the agents in the simulation. * @param[in] sim The simulation of the abm. @@ -155,9 +154,9 @@ struct LogDataForMovement : mio::LogAlways { { Type movement_data{}; for (Person p : sim.get_world().get_persons()) { - movement_data.push_back(std::make_tuple( - p.get_person_id(), p.get_location().get_index(), sim.get_time(), p.get_last_transport_mode(), - guess_activity_type(p.get_location().get_type()), p.get_infection_state(sim.get_time()))); + movement_data.push_back( + std::make_tuple(p.get_id(), p.get_location(), sim.get_time(), p.get_last_transport_mode(), + guess_activity_type(p.get_location_type()), p.get_infection_state(sim.get_time()))); } return movement_data; } diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index 92941563b1..c6afa00399 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -19,7 +19,7 @@ */ #include "abm/household.h" -#include "abm/person.h" +#include "abm/person_id.h" #include "abm/location.h" #include "memilio/utils/random_number_generator.h" @@ -69,8 +69,8 @@ void add_household_to_world(World& world, const Household& household) std::tie(member, count) = memberTouple; for (int j = 0; j < count; j++) { auto age_group = pick_age_group_from_age_distribution(world.get_rng(), member.get_age_weights()); - auto& person = world.get_person(world.add_person(home, age_group)); - person.set_assigned_location(home); + auto person = world.add_person(home, age_group); + world.assign_location(person, home); } } } diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index 3e93a3420d..81d803471e 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -17,9 +17,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/location_type.h" #include "abm/mask_type.h" #include "abm/location.h" -#include "abm/parameters.h" #include "abm/random_events.h" namespace mio @@ -27,8 +27,9 @@ namespace mio namespace abm { -Location::Location(LocationId loc_id, size_t num_agegroups, uint32_t num_cells) - : m_id(loc_id) +Location::Location(LocationType loc_type, LocationId loc_id, size_t num_agegroups, uint32_t num_cells) + : m_type(loc_type) + , m_id(loc_id) , m_parameters(num_agegroups) , m_cells(num_cells) , m_required_mask(MaskType::Community) @@ -37,11 +38,6 @@ Location::Location(LocationId loc_id, size_t num_agegroups, uint32_t num_cells) assert(num_cells > 0 && "Number of cells has to be larger than 0."); } -Location Location::copy() const -{ - return *this; -} - /* For every cell in a location we have a transmission factor that is nomalized to m_capacity.volume / m_capacity.persons of the location "Home", which is 66. We multiply this rate with the individual size of each cell to obtain a "space per person" factor. diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 1e18903ee8..850dea052a 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -20,6 +20,7 @@ #ifndef MIO_ABM_LOCATION_H #define MIO_ABM_LOCATION_H +#include "abm/location_id.h" #include "abm/mask_type.h" #include "abm/parameters.h" #include "abm/location_type.h" @@ -31,6 +32,24 @@ namespace mio namespace abm { +struct GeographicalLocation { + double latitude; + double longitude; + + /** + * @brief Compare two GeographicalLocation%s. + */ + bool operator==(const GeographicalLocation& other) const + { + return (latitude == other.latitude && longitude == other.longitude); + } + + bool operator!=(const GeographicalLocation& other) const + { + return !(latitude == other.latitude && longitude == other.longitude); + } +}; + struct CellIndex : public mio::Index { CellIndex(size_t i) : mio::Index(i) @@ -76,25 +95,14 @@ struct Cell { class Location { public: - /** - * @brief Construct a Location of a certain LocationId. - * @param[in] loc_id The #LocationId. - * @param[in] num_agegroups [Default: 1] The number of age groups in the model. - * @param[in] num_cells [Default: 1] The number of Cell%s in which the Location is divided. - */ - explicit Location(LocationId loc_id, size_t num_agegroups = 1, uint32_t num_cells = 1); - /** * @brief Construct a Location with provided parameters. * @param[in] loc_type The #LocationType. - * @param[in] index The index of the Location. + * @param[in] loc_id The index of the Location in the World. * @param[in] num_agegroups [Default: 1] The number of age groups in the model. * @param[in] num_cells [Default: 1] The number of Cell%s in which the Location is divided. */ - explicit Location(LocationType loc_type, uint32_t loc_index, size_t num_agegroups = 1, uint32_t num_cells = 1) - : Location(LocationId{loc_index, loc_type}, num_agegroups, num_cells) - { - } + explicit Location(LocationType loc_type, LocationId loc_id, size_t num_agegroups = 1, uint32_t num_cells = 1); /** * @brief Construct a copy of a Location with a new ID. @@ -107,12 +115,6 @@ class Location m_id = id; } - /** - * @brief Return a copy of this #Location object with an empty m_persons. - * @param[in] num_agegroups The number of age groups in the model. - */ - Location copy() const; - /** * @brief Compare two Location%s. */ @@ -132,16 +134,16 @@ class Location */ LocationType get_type() const { - return m_id.type; + return m_type; } /** - * @brief Get the index of this Location. - * @return The index of the Location. + * @brief Get the location's identifier in a World. + * @return The location's LocationId by value. */ - unsigned get_index() const + LocationId get_id() const { - return m_id.index; + return m_id; } /** @@ -238,8 +240,8 @@ class Location void serialize(IOContext& io) const { auto obj = io.create_object("Location"); - obj.add_element("index", m_id.index); - obj.add_element("type", m_id.type); + obj.add_element("index", m_id); + obj.add_element("type", m_type); } /** @@ -250,12 +252,12 @@ class Location static IOResult deserialize(IOContext& io) { auto obj = io.expect_object("Location"); - auto index = obj.expect_element("index", Tag{}); - auto type = obj.expect_element("type", Tag{}); + auto index = obj.expect_element("index", Tag{}); + auto type = obj.expect_element("type", Tag{}); return apply( io, [](auto&& index_, auto&& type_) { - return Location{LocationId{index_, LocationType(type_)}}; + return Location{type_, index_}; }, index, type); } @@ -278,17 +280,9 @@ class Location m_geographical_location = location; } - /** - * @brief Get the location's identifier in a World. - * @return The location's LocationId by value. - */ - LocationId get_id() const - { - return m_id; - } - private: - LocationId m_id; ///< Id of the Location including type and index. + LocationType m_type; ///< Type of the Location. + LocationId m_id; ///< Id of the Location. Set by the World owning it. LocalInfectionParameters m_parameters; ///< Infection parameters for the Location. std::vector m_cells{}; ///< A vector of all Cell%s that the Location is divided in. MaskType m_required_mask; ///< Least secure type of Mask that is needed to enter the Location. diff --git a/cpp/models/abm/location_id.h b/cpp/models/abm/location_id.h new file mode 100644 index 0000000000..4597f36bf0 --- /dev/null +++ b/cpp/models/abm/location_id.h @@ -0,0 +1,56 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Rene Schmieding +* +* 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_LOCATION_ID_H +#define MIO_ABM_LOCATION_ID_H + +#include "memilio/utils/type_safe.h" +#include + +namespace mio +{ +namespace abm +{ + +/// Unique identifier for a Location within a World. +struct LocationId : mio::TypeSafe, public OperatorComparison { + /// @brief Create an ID. + LocationId(uint32_t id) + : mio::TypeSafe(id) + { + } + + /// @brief Create an invalid ID. + LocationId() + : mio::TypeSafe(std::numeric_limits::max()) + { + } + + /// @brief Value for invalid IDs. + const static LocationId invalid_id() + { + return LocationId(); + } +}; + +} // namespace abm +} // namespace mio + +#endif // MIO_ABM_LOCATION_ID_H diff --git a/cpp/models/abm/location_type.h b/cpp/models/abm/location_type.h index 484f0edcaf..47eea8405f 100644 --- a/cpp/models/abm/location_type.h +++ b/cpp/models/abm/location_type.h @@ -20,10 +20,7 @@ #ifndef MIO_ABM_LOCATION_TYPE_H #define MIO_ABM_LOCATION_TYPE_H -#include "memilio/io/io.h" #include -#include -#include namespace mio { @@ -50,104 +47,7 @@ enum class LocationType : std::uint32_t Count //last! }; -static constexpr uint32_t INVALID_LOCATION_INDEX = std::numeric_limits::max(); - -/** - * LocationId identifies a Location uniquely. It consists of the LocationType of the Location and an Index. - * The index corresponds to the index into the structure m_locations from world, where all Locations are saved. - */ -struct LocationId { - uint32_t index; - LocationType type; // TODO: move to location - - bool operator==(const LocationId& rhs) const - { - return (index == rhs.index && type == rhs.type); - } - - bool operator!=(const LocationId& rhs) const - { - return !(index == rhs.index && type == rhs.type); - } - - bool operator<(const LocationId& rhs) const - { - if (type == rhs.type) { - return index < rhs.index; - } - return (type < rhs.type); - } - - // same interface as location - uint32_t get_index() const - { - return index; - } - - // same interface as location - LocationType get_type() const - { - return type; - } - - /** - * serialize this. - * @see mio::serialize - */ - template - void serialize(IOContext& io) const - { - auto obj = io.create_object("LocationId"); - obj.add_element("index", index); - obj.add_element("type", type); - } - - /** - * deserialize an object of this class. - * @see mio::deserialize - */ - template - static IOResult deserialize(IOContext& io) - { - auto obj = io.expect_object("LocationId"); - auto i = obj.expect_element("index", mio::Tag{}); - auto t = obj.expect_element("type", mio::Tag{}); - return apply( - io, - [](auto&& index_, auto&& type_) { - return LocationId{index_, type_}; - }, - i, t); - } -}; - -struct GeographicalLocation { - double latitude; - double longitude; - - /** - * @brief Compare two Location%s. - */ - bool operator==(const GeographicalLocation& other) const - { - return (latitude == other.latitude && longitude == other.longitude); - } - - bool operator!=(const GeographicalLocation& other) const - { - return !(latitude == other.latitude && longitude == other.longitude); - } -}; - } // namespace abm } // namespace mio -template <> -struct std::hash { - std::size_t operator()(const mio::abm::LocationId& loc_id) const - { - return (std::hash()(loc_id.index)) ^ (std::hash()(static_cast(loc_id.type))); - } -}; - #endif diff --git a/cpp/models/abm/migration_rules.cpp b/cpp/models/abm/migration_rules.cpp index 042af98449..00844a9052 100644 --- a/cpp/models/abm/migration_rules.cpp +++ b/cpp/models/abm/migration_rules.cpp @@ -30,7 +30,7 @@ namespace abm LocationType random_migration(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); auto make_transition = [current_loc](auto l) { return std::make_pair(l, l == current_loc ? 0. : 1.); }; @@ -46,7 +46,7 @@ LocationType random_migration(PersonalRandomNumberGenerator& rng, const Person& LocationType go_to_school(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); if (current_loc == LocationType::Home && t < params.get() && t.day_of_week() < 5 && person.get_go_to_school_time(params) >= t.time_since_midnight() && @@ -65,7 +65,7 @@ LocationType go_to_school(PersonalRandomNumberGenerator& /*rng*/, const Person& LocationType go_to_work(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); if (current_loc == LocationType::Home && t < params.get() && params.get()[person.get_age()] && t.day_of_week() < 5 && @@ -84,7 +84,7 @@ LocationType go_to_work(PersonalRandomNumberGenerator& /*rng*/, const Person& pe LocationType go_to_shop(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); //leave if (t.day_of_week() < 6 && t.hour_of_day() > 7 && t.hour_of_day() < 22 && current_loc == LocationType::Home && !person.is_in_quarantine(t, params)) { @@ -103,7 +103,7 @@ LocationType go_to_shop(PersonalRandomNumberGenerator& rng, const Person& person LocationType go_to_event(PersonalRandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, const Parameters& params) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); //leave if (current_loc == LocationType::Home && t < params.get() && ((t.day_of_week() <= 4 && t.hour_of_day() >= 19) || (t.day_of_week() >= 5 && t.hour_of_day() >= 10)) && @@ -125,7 +125,7 @@ LocationType go_to_event(PersonalRandomNumberGenerator& rng, const Person& perso LocationType go_to_quarantine(PersonalRandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan /*dt*/, const Parameters& params) { - auto current_loc = person.get_location().get_type(); + 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; @@ -136,7 +136,7 @@ LocationType go_to_quarantine(PersonalRandomNumberGenerator& /*rng*/, const Pers LocationType go_to_hospital(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); if (person.get_infection_state(t) == InfectionState::InfectedSevere) { return LocationType::Hospital; } @@ -146,7 +146,7 @@ LocationType go_to_hospital(PersonalRandomNumberGenerator& /*rng*/, const Person LocationType go_to_icu(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); if (person.get_infection_state(t) == InfectionState::InfectedCritical) { return LocationType::ICU; } @@ -156,7 +156,7 @@ LocationType go_to_icu(PersonalRandomNumberGenerator& /*rng*/, const Person& per LocationType return_home_when_recovered(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); if ((current_loc == LocationType::Hospital || current_loc == LocationType::ICU) && person.get_infection_state(t) == InfectionState::Recovered) { return LocationType::Home; @@ -167,7 +167,7 @@ LocationType return_home_when_recovered(PersonalRandomNumberGenerator& /*rng*/, LocationType get_buried(PersonalRandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, const Parameters& /*params*/) { - auto current_loc = person.get_location().get_type(); + auto current_loc = person.get_location_type(); if (person.get_infection_state(t) == InfectionState::Dead) { return LocationType::Cemetery; } diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index 0208731c29..776955a71d 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -101,13 +101,8 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExposureRates& local_contact_exposure, const Person& person, const Location& location, const TimePoint t, const TimeSpan dt) { - assert([&]() { - if (person.get_location() != location.get_id()) { - mio::log_warning("Person with id {} is not at Location with id {}", person.get_person_id().get(), - location.get_index()); - } - return true; - }()); + mio::log_debug("Person with id {} is not at Location with id {}", person.get_id().get(), location.get_id().get()); + if (person.is_infected(t)) { auto& infection = person.get_infection(); auto virus = infection.get_virus_variant(); @@ -134,7 +129,7 @@ bool migrate(Person& person, const Location& destination, const TransportMode mo })); // make sure cell indices are valid if (person.get_location() != destination.get_id()) { - person.set_location(destination.get_id()); + person.set_location(destination.get_type(), destination.get_id()); person.get_cells() = cells; person.set_last_transport_mode(mode); diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index af86249042..48504e7479 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -31,9 +31,11 @@ namespace mio namespace abm { -Person::Person(mio::RandomNumberGenerator& rng, LocationId location, AgeGroup age, PersonId person_id) +Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location, AgeGroup age, + PersonId person_id) : m_location(location) - , m_assigned_locations((uint32_t)LocationType::Count, INVALID_LOCATION_INDEX) + , m_location_type(location_type) + , m_assigned_locations((uint32_t)LocationType::Count, LocationId::invalid_id()) , m_quarantine_start(TimePoint(-(std::numeric_limits::max() / 2))) , m_age(age) , m_time_at_location(0) @@ -57,13 +59,6 @@ Person::Person(const Person& other, PersonId id) m_person_id = id; } -Person Person::copy_person(LocationId location) -{ - Person copy = *this; - copy.set_location(location); - return copy; -} - bool Person::is_infected(TimePoint t) const { if (m_infections.empty()) { @@ -97,9 +92,10 @@ LocationId Person::get_location() const return m_location; } -void Person::set_location(LocationId id) +void Person::set_location(LocationType type, LocationId id) { m_location = id; + m_location_type = type; m_time_at_location = TimeSpan(0); } @@ -113,18 +109,18 @@ Infection& Person::get_infection() return m_infections.back(); } -void Person::set_assigned_location(LocationId location) +void Person::set_assigned_location(LocationType type, LocationId id) { /* TODO: This is not safe if the location is not the same as added in the world, e.g. the index is wrong. We need to check this. * For now only use it like this: auto home_id = world.add_location(mio::abm::LocationType::Home); * person.set_assigned_location(home); */ - m_assigned_locations[(uint32_t)location.get_type()] = location.get_index(); + m_assigned_locations[static_cast(type)] = id; } -uint32_t Person::get_assigned_location_index(LocationType type) const +LocationId Person::get_assigned_location(LocationType type) const { - return m_assigned_locations[(uint32_t)type]; + return m_assigned_locations[static_cast(type)]; } bool Person::goes_to_work(TimePoint t, const Parameters& params) const @@ -189,7 +185,7 @@ bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const T } } -PersonId Person::get_person_id() const +PersonId Person::get_id() const { return m_person_id; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 8c4604e04f..aa33a4fcf1 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -22,8 +22,9 @@ #include "abm/infection.h" #include "abm/infection_state.h" -#include "abm/location_type.h" +#include "abm/location_id.h" #include "abm/location.h" +#include "abm/location_type.h" #include "abm/parameters.h" #include "abm/person_id.h" #include "abm/personal_rng.h" @@ -52,17 +53,11 @@ class Person * @param[in] age The AgeGroup of the Person. * @param[in] person_id Index of the Person. */ - explicit Person(mio::RandomNumberGenerator& rng, LocationId location, AgeGroup age, + explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location, AgeGroup age, PersonId person_id = PersonId::invalid_id()); explicit Person(const Person& other, PersonId id); - /** - * @brief Create a copy of this #Person object with a new Location. - * @param[in, out] location The new #Location of the Person. - */ - Person copy_person(LocationId location); - /** * @brief Compare two Person%s. */ @@ -129,11 +124,16 @@ class Person */ LocationId get_location() const; + LocationType get_location_type() const + { + return m_location_type; + } + /** * @brief Change the location of the person. * @param[in] id The new location. */ - void set_location(LocationId id); + void set_location(LocationType type, LocationId id); /** * @brief Get the time the Person has been at its current Location. @@ -168,7 +168,7 @@ class Person * Location of a certain #LocationType. * @param[in] id The LocationId of the Location. */ - void set_assigned_location(LocationId id); + void set_assigned_location(LocationType type, LocationId id); /** * @brief Returns the index of an assigned Location of the Person. @@ -176,13 +176,13 @@ class Person * @param[in] type #LocationType of the assigned Location. * @return The index in the LocationId of the assigned Location. */ - uint32_t get_assigned_location_index(LocationType type) const; + LocationId get_assigned_location(LocationType type) 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; } @@ -257,7 +257,7 @@ class Person * The PersonId should correspond to the index in m_persons in world. * @return The PersonId. */ - PersonId get_person_id() const; + PersonId get_id() const; /** * @brief Get index of Cell%s of the Person. @@ -422,7 +422,8 @@ class Person private: LocationId m_location; ///< Current Location of the Person. - std::vector m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the + LocationType m_location_type; ///< Type of the current Location. + 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 Vaccination%s the Person has received. std::vector m_infections; ///< Vector with all Infection%s the Person had. diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index 4f99aafc05..7c298191ee 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -29,16 +29,21 @@ namespace mio namespace abm { +/// Unique identifier for a Person within a World. struct PersonId : mio::TypeSafe, public OperatorComparison { + /// @brief Create an ID. PersonId(uint32_t id) : mio::TypeSafe(id) { } + + /// @brief Create an invalid ID. PersonId() : mio::TypeSafe(std::numeric_limits::max()) { } + /// @brief Value for invalid IDs. const static PersonId invalid_id() { return PersonId(); diff --git a/cpp/models/abm/personal_rng.cpp b/cpp/models/abm/personal_rng.cpp index b8a87e5247..b6eb54f675 100644 --- a/cpp/models/abm/personal_rng.cpp +++ b/cpp/models/abm/personal_rng.cpp @@ -34,7 +34,7 @@ PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key } PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) - : PersonalRandomNumberGenerator(rng.get_key(), person.get_person_id(), person.get_rng_counter()) + : PersonalRandomNumberGenerator(rng.get_key(), person.get_id(), person.get_rng_counter()) { } diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index 63c67912df..6a84da7714 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -19,7 +19,10 @@ */ #include "abm/testing_strategy.h" +#include "abm/location_id.h" #include "memilio/utils/random_number_generator.h" +#include +#include namespace mio { @@ -113,21 +116,23 @@ bool TestingScheme::run_scheme(PersonalRandomNumberGenerator& rng, Person& perso return true; } -TestingStrategy::TestingStrategy( - const std::unordered_map>& location_to_schemes_map) +TestingStrategy::TestingStrategy(const std::unordered_map, + std::vector, hash>& location_to_schemes_map) : m_location_to_schemes_map(location_to_schemes_map.begin(), location_to_schemes_map.end()) { } -void TestingStrategy::add_testing_scheme(const LocationId& loc_id, const TestingScheme& scheme) +void TestingStrategy::add_testing_scheme(const LocationType& loc_type, const LocationId& loc_id, + const TestingScheme& scheme) { + auto key = std::tie(loc_type, loc_id); auto iter_schemes = - std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [loc_id](auto& p) { - return p.first == loc_id; + std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [&key](const auto& p) { + return p == key; }); if (iter_schemes == m_location_to_schemes_map.end()) { //no schemes for this location yet, add a new list with one scheme - m_location_to_schemes_map.emplace_back(loc_id, std::vector(1, scheme)); + m_location_to_schemes_map.emplace_back(key, std::vector(1, scheme)); } else { //add scheme to existing vector if the scheme doesn't exist yet @@ -138,11 +143,13 @@ void TestingStrategy::add_testing_scheme(const LocationId& loc_id, const Testing } } -void TestingStrategy::remove_testing_scheme(const LocationId& loc_id, const TestingScheme& scheme) +void TestingStrategy::remove_testing_scheme(const LocationType& loc_type, const LocationId& loc_id, + const TestingScheme& scheme) { + auto key = std::tie(loc_type, loc_id); auto iter_schemes = - std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [loc_id](auto& p) { - return p.first == loc_id; + std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [&key](const auto& p) { + return std::get<0>(p) == std::get<0>(key) && std::get<1>(p) == std::get<1>(key); }); if (iter_schemes != m_location_to_schemes_map.end()) { //remove the scheme from the list @@ -158,7 +165,7 @@ void TestingStrategy::remove_testing_scheme(const LocationId& loc_id, const Test void TestingStrategy::update_activity_status(TimePoint t) { - for (auto& [_, testing_schemes] : m_location_to_schemes_map) { + for (auto& [_type, _id, testing_schemes] : m_location_to_schemes_map) { for (auto& scheme : testing_schemes) { scheme.update_activity_status(t); } @@ -175,11 +182,11 @@ bool TestingStrategy::run_strategy(PersonalRandomNumberGenerator& rng, Person& p //lookup schemes for this specific location as well as the location type //lookup in std::vector instead of std::map should be much faster unless for large numbers of schemes - for (auto loc_key : {LocationId{location.get_index(), location.get_type()}, - LocationId{INVALID_LOCATION_INDEX, location.get_type()}}) { + for (auto key : {std::make_pair(location.get_type(), location.get_id()), + std::make_pair(location.get_type(), LocationId::invalid_id())}) { auto iter_schemes = - std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [loc_key](auto& p) { - return p.first == loc_key; + std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [key](auto& p) { + return p.first == key; }); if (iter_schemes != m_location_to_schemes_map.end()) { //apply all testing schemes that are found diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 721971abd0..5002442ba1 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -21,13 +21,14 @@ #define MIO_ABM_TESTING_SCHEME_H #include "abm/config.h" +#include "abm/location_id.h" +#include "abm/location_type.h" #include "abm/parameters.h" #include "abm/person.h" #include "abm/location.h" #include "abm/time.h" -#include "memilio/utils/random_number_generator.h" #include -#include +#include namespace mio { @@ -158,20 +159,35 @@ class TestingScheme */ class TestingStrategy { + struct hash { + std::size_t operator()(const std::pair& key) const + { + return std::hash{}(static_cast(key.first)) ^ + std::hash{}(static_cast(key.second)); + } + }; + public: + struct Entry { + LocationType type; + LocationId id; + std::vector schemes; + }; + /** * @brief Create a TestingStrategy. * @param[in] testing_schemes Vector of TestingSchemes that are checked for testing. */ TestingStrategy() = default; - explicit TestingStrategy(const std::unordered_map>& location_to_schemes_map); + explicit TestingStrategy(const std::unordered_map, std::vector, + hash>& location_to_schemes_map); /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at a certain Location. * @param[in] loc_id LocationId key for TestingScheme to be added. * @param[in] scheme TestingScheme to be added. */ - void add_testing_scheme(const LocationId& loc_id, const TestingScheme& scheme); + void add_testing_scheme(const LocationType& loc_type, const LocationId& loc_id, const TestingScheme& scheme); /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at a certain LocationType. @@ -182,7 +198,7 @@ class TestingStrategy */ void add_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme) { - add_testing_scheme(LocationId{INVALID_LOCATION_INDEX, loc_type}, scheme); + add_testing_scheme(loc_type, LocationId::invalid_id(), scheme); } /** @@ -190,7 +206,7 @@ class TestingStrategy * @param[in] loc_id LocationId key for TestingScheme to be remove. * @param[in] scheme TestingScheme to be removed. */ - void remove_testing_scheme(const LocationId& loc_id, const TestingScheme& scheme); + void remove_testing_scheme(const LocationType& loc_type, const LocationId& loc_id, const TestingScheme& scheme); /** * @brief Remove a TestingScheme from the set of schemes that are checked for testing at a certain Location. @@ -201,7 +217,7 @@ class TestingStrategy */ void remove_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme) { - remove_testing_scheme(LocationId{INVALID_LOCATION_INDEX, loc_type}, scheme); + remove_testing_scheme(loc_type, LocationId::invalid_id(), scheme); } /** @@ -222,7 +238,7 @@ class TestingStrategy bool run_strategy(PersonalRandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t); private: - std::vector>> + std::vector>> m_location_to_schemes_map; ///< Set of schemes that are checked for testing. }; diff --git a/cpp/models/abm/trip_list.cpp b/cpp/models/abm/trip_list.cpp index af8bc806dd..c9991bdb1f 100644 --- a/cpp/models/abm/trip_list.cpp +++ b/cpp/models/abm/trip_list.cpp @@ -19,6 +19,7 @@ */ #include "abm/trip_list.h" #include "abm/random_events.h" +#include "memilio/utils/stl_util.h" namespace mio { diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index 639df7c0e7..627afef7f1 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -20,8 +20,10 @@ #ifndef MIO_ABM_TRIP_LIST_H #define MIO_ABM_TRIP_LIST_H -#include "abm/location_type.h" -#include "abm/person.h" +#include "abm/location_id.h" +#include "abm/movement_data.h" +#include "abm/person_id.h" +#include "abm/time.h" namespace mio { @@ -96,10 +98,8 @@ struct Trip { auto obj = io.create_object("Trip"); obj.add_element("person_id", person_id); obj.add_element("time", time.seconds()); - obj.add_element("destination_index", migration_destination.index); - obj.add_element("destination_type", migration_destination.type); - obj.add_element("origin_index", migration_origin.index); - obj.add_element("origin_type", migration_origin.type); + obj.add_element("destination", migration_destination); + obj.add_element("origin", migration_origin); } /** @@ -109,22 +109,17 @@ struct Trip { template static IOResult deserialize(IOContext& io) { - auto obj = io.expect_object("Trip"); - auto person_id = obj.expect_element("person_id", Tag{}); - auto time = obj.expect_element("time", Tag{}); - auto destination_index = obj.expect_element("destination_index", Tag{}); - auto destination_type = obj.expect_element("destination_type", Tag{}); - auto origin_index = obj.expect_element("origin_index", Tag{}); - auto origin_type = obj.expect_element("origin_type", Tag{}); + auto obj = io.expect_object("Trip"); + auto person_id = obj.expect_element("person_id", Tag{}); + auto time = obj.expect_element("time", Tag{}); + auto destination_id = obj.expect_element("destination", Tag{}); + auto origin_id = obj.expect_element("origin", Tag{}); return apply( io, - [](auto&& person_id_, auto&& time_, auto&& destination_index_, auto&& destination_type_, - auto&& origin_index_, auto&& origin_type_) { - return Trip(person_id_, TimePoint(time_), - LocationId{destination_index_, LocationType(destination_type_)}, - LocationId{origin_index_, LocationType(origin_type_)}); + [](auto&& person_id_, auto&& time_, auto&& destination_id_, auto&& origin_id_) { + return Trip(person_id_, TimePoint(time_), destination_id_, origin_id_); }, - person_id, time, destination_index, destination_type, origin_index, origin_type); + person_id, time, destination_id, origin_id); } }; diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 409b7f2c6f..60bc57ae50 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -18,10 +18,12 @@ * limitations under the License. */ #include "abm/world.h" +#include "abm/location_id.h" #include "abm/location_type.h" #include "abm/person.h" #include "abm/location.h" #include "abm/migration_rules.h" +#include "memilio/epidemiology/age_group.h" #include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" #include "memilio/utils/stl_util.h" @@ -33,7 +35,7 @@ namespace abm LocationId World::add_location(LocationType type, uint32_t num_cells) { - LocationId id = {static_cast(m_locations.size()), type}; + LocationId id{static_cast(m_locations.size())}; m_locations.emplace_back(id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; @@ -47,22 +49,22 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) PersonId World::add_person(const LocationId id, AgeGroup age) { - return add_person(Person(m_rng, id, age)); + return add_person(Person(m_rng, get_location(id).get_type(), id, age)); } PersonId World::add_person(Person&& person) { - assert(person.get_location().index != INVALID_LOCATION_INDEX); - assert(person.get_location().index < m_locations.size()); - assert(person.get_age().get() < parameters.get_num_groups()); + assert(person.get_location() != LocationId::invalid_id()); + assert(person.get_location() < (LocationId)m_locations.size()); + assert(person.get_age() < (AgeGroup)parameters.get_num_groups()); PersonId new_id{static_cast(m_persons.size())}; m_persons.emplace_back(person, new_id); auto& new_person = m_persons.back(); - new_person.set_assigned_location(m_cemetery_id); + new_person.set_assigned_location(LocationType::Cemetery, m_cemetery_id); if (m_is_local_population_cache_valid) { - ++m_local_population_cache[new_person.get_location().index]; + ++m_local_population_cache[new_person.get_location().get()]; } return new_id; } @@ -148,7 +150,7 @@ void World::migration(TimePoint t, TimeSpan dt) auto& target_location = get_location(trip.migration_destination); if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); - migrate(person.get_person_id(), target_location.get_id(), trip.trip_mode); + migrate(person.get_id(), target_location.get_id(), trip.trip_mode); } } m_trip_list.increase_index(); @@ -172,7 +174,7 @@ void World::build_compute_local_population_cache() const } // implicit taskloop barrier PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_persons; i++) { - ++m_local_population_cache[m_persons[i].get_location().index]; + ++m_local_population_cache[m_persons[i].get_location().get()]; } // implicit taskloop barrier } // implicit single barrier } @@ -227,10 +229,10 @@ void World::compute_exposure_caches(TimePoint t, TimeSpan dt) PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_persons; ++i) { const Person& person = m_persons[i]; - const auto location = person.get_location().index; + const auto location = person.get_location().get(); mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, - get_location(person.get_person_id()), t, dt); + get_location(person.get_id()), t, dt); } // implicit taskloop barrier } // implicit single barrier } @@ -268,9 +270,9 @@ auto World::get_persons() -> Range> LocationId World::find_location(LocationType type, const PersonId person) const { - auto index = get_person(person).get_assigned_location_index(type); - assert(index != INVALID_LOCATION_INDEX && "unexpected error."); - return {index, type}; + auto location_id = get_person(person).get_assigned_location(type); + assert(location_id != LocationId::invalid_id() && "No location."); + return location_id; } size_t World::get_subpopulation_combined(TimePoint t, InfectionState s) const diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index d2ecae2db2..1e8e7d178c 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -26,6 +26,7 @@ #include "abm/parameters.h" #include "abm/location.h" #include "abm/person.h" +#include "abm/person_id.h" #include "abm/time.h" #include "abm/trip_list.h" #include "abm/random_events.h" @@ -209,6 +210,11 @@ class World */ LocationId find_location(LocationType type, const PersonId person) const; + void assign_location(PersonId assignee, LocationId location) + { + get_person(assignee).set_assigned_location(get_location(location).get_type(), location); + } + /** * @brief Get the number of Persons in one #InfectionState at all Location%s. * @param[in] t Specified #TimePoint. @@ -346,7 +352,7 @@ class World if (!m_is_local_population_cache_valid) { build_compute_local_population_cache(); } - return m_local_population_cache[location.index]; + return m_local_population_cache[location.get()]; } // move a person to another location. this requires that location is part of this world. @@ -366,8 +372,8 @@ class World if (has_moved) { m_are_exposure_caches_valid = false; if (m_is_local_population_cache_valid) { - --m_local_population_cache[origin.index]; - ++m_local_population_cache[destination.index]; + --m_local_population_cache[origin.get()]; + ++m_local_population_cache[destination.get()]; } } } @@ -388,8 +394,8 @@ class World } auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person)); mio::abm::interact(personal_rng, get_person(person), get_location(person), - m_air_exposure_rates_cache[get_location(person).get_index()], - m_contact_exposure_rates_cache[get_location(person).get_index()], t, dt, parameters); + m_air_exposure_rates_cache[get_location(person).get_id().get()], + m_contact_exposure_rates_cache[get_location(person).get_id().get()], t, dt, parameters); } /** @@ -400,16 +406,16 @@ class World */ const Location& get_location(LocationId id) const { - assert(id.index != INVALID_LOCATION_INDEX); - assert(id.index < m_locations.size()); - return m_locations[id.index]; + assert(id != LocationId::invalid_id()); + assert(id < LocationId(m_locations.size())); + return m_locations[id.get()]; } Location& get_location(LocationId id) { - assert(id.index != INVALID_LOCATION_INDEX); - assert(id.index < m_locations.size()); - return m_locations[id.index]; + assert(id != LocationId::invalid_id()); + assert(id < LocationId(m_locations.size())); + return m_locations[id.get()]; } /** @} */ diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 7d67d3698c..78ce0f2a40 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -328,7 +328,7 @@ void create_assign_locations(mio::abm::World& world) auto start_date = mio::abm::TimePoint(0); auto end_date = mio::abm::TimePoint(0) + mio::abm::days(60); - auto probability = mio:: UncertainValue<>(); + auto probability = mio::UncertainValue<>(); assign_uniform_distribution(probability, 0.5, 1.0); auto test_type = mio::abm::AntigenTest(); @@ -379,21 +379,22 @@ void create_assign_locations(mio::abm::World& world) //Assign locations to the people auto persons = world.get_persons(); for (auto& person : persons) { + const auto id = person.get_id(); //assign shop and event - person.set_assigned_location(event); + world.assign_location(id, event); counter_event++; - person.set_assigned_location(shop); + world.assign_location(id, shop); counter_shop++; //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); + world.assign_location(id, hospital); + world.assign_location(id, icu); //assign work/school to people depending on their age if (person.get_age() == age_group_5_to_14) { - person.set_assigned_location(school); + world.assign_location(id, school); counter_school++; } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - person.set_assigned_location(work); + world.assign_location(id, work); counter_work++; } //add new school/work/shop if needed diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index 70ababe0dd..04b36fe604 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -18,7 +18,9 @@ * limitations under the License. */ #include "abm/common_abm_loggers.h" +#include "abm/location_id.h" #include "abm/lockdown_rules.h" +#include "abm/person.h" #include "abm/simulation.h" #include "abm/world.h" #include "memilio/epidemiology/age_group.h" @@ -218,9 +220,9 @@ void create_world_from_data(mio::abm::World& world, const std::string& filename, count_of_titles++; } - std::map locations = {}; - std::map persons = {}; - std::map person_ids = {}; + std::map locations = {}; + std::map pids_data_to_world = {}; + std::map person_ids = {}; std::map> locations_before; std::map> locations_after; @@ -341,8 +343,8 @@ void create_world_from_data(mio::abm::World& world, const std::string& filename, split_line(line, &row); line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - uint32_t person_id = row[index["puid"]]; - if (person_ids.find(person_id) == person_ids.end()) + uint32_t person_data_id = row[index["puid"]]; + if (person_ids.find(person_data_id) == person_ids.end()) break; uint32_t age = row[index["age"]]; @@ -357,35 +359,33 @@ void create_world_from_data(mio::abm::World& world, const std::string& filename, auto target_location = locations.find(target_location_id)->second; auto start_location = locations.find(start_location_id)->second; - auto it_person = persons.find(person_id); + auto pid_itr = pids_data_to_world.find(person_data_id); - if (it_person == persons.end()) { - auto it_first_location_id = locations_before.find(person_id); + if (pid_itr == pids_data_to_world.end()) { // person has not been added to world yet + auto it_first_location_id = locations_before.find(person_data_id); if (it_first_location_id == locations_before.end()) { - it_first_location_id = locations_after.find(person_id); + it_first_location_id = locations_after.find(person_data_id); } auto first_location_id = it_first_location_id->second.first; auto first_location = locations.find(first_location_id)->second; - auto& person = - world.get_person(world.add_person(first_location, determine_age_group(age))); // TODO: is this safe? - auto home = locations.find(home_id)->second; - person.set_assigned_location(home); - person.set_assigned_location(hospital); - person.set_assigned_location(icu); - persons.insert({person_id, person}); // TODO: why? what? - it_person = persons.find(person_id); + auto person_world_id = world.add_person(first_location, determine_age_group(age)); + auto home = locations.find(home_id)->second; + world.assign_location(person_world_id, home); + world.assign_location(person_world_id, hospital); + world.assign_location(person_world_id, icu); + pid_itr = pids_data_to_world.insert_or_assign(person_data_id, person_world_id).first; } - it_person->second.set_assigned_location( + world.assign_location( + pid_itr->second, target_location); //This assumes that we only have in each tripchain only one location type for each person if (locations.find(start_location_id) == locations.end()) { // For trips where the start location is not known use Home instead - start_location = {it_person->second.get_assigned_location_index(mio::abm::LocationType::Home), - mio::abm::LocationType::Home}; + start_location = world.get_person(pid_itr->second).get_assigned_location(mio::abm::LocationType::Home); } world.get_trip_list().add_trip(mio::abm::Trip( - it_person->second.get_person_id(), mio::abm::TimePoint(0) + mio::abm::minutes(trip_start), target_location, - start_location, mio::abm::TransportMode(transport_mode), mio::abm::ActivityType(acticity_end))); + pid_itr->second, mio::abm::TimePoint(0) + mio::abm::minutes(trip_start), target_location, start_location, + mio::abm::TransportMode(transport_mode), mio::abm::ActivityType(acticity_end))); } world.get_trip_list().use_weekday_trips_on_weekend(); } @@ -968,14 +968,14 @@ void write_log_to_file_trip_data(const T& history) })) { start_index--; } - auto start_location_pointer = + auto start_location_iterator = std::lower_bound(std::begin(movement_data[start_index]), std::end(movement_data[start_index]), movement_data[movement_data_index][trip_index], [](const Type& v1, const Type& v2) { return std::get<0>(v1) < std::get<0>(v2); }); - int start_location = (int)std::get<1>(*start_location_pointer); + auto start_location = (int)std::get<1>(*start_location_iterator).get(); - auto end_location = (int)std::get<1>(movement_data[movement_data_index][trip_index]); + auto end_location = (int)std::get<1>(movement_data[movement_data_index][trip_index]).get(); auto start_time = (int)std::get<2>(movement_data[movement_data_index][trip_index]).seconds(); auto end_time = (int)std::get<2>(movement_data[movement_data_index][trip_index]).seconds(); @@ -1019,7 +1019,7 @@ mio::IOResult run(const std::string& input_file, const fs::path& result_di // Collect the id of location in world. std::vector loc_ids; for (auto& location : sim.get_world().get_locations()) { - loc_ids.push_back(location.get_index()); + loc_ids.push_back(location.get_id().get()); } // Advance the world to tmax sim.advance(tmax, historyPersonInf, historyTimeSeries, historyPersonInfDelta); diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 21ec73c750..85f9edf765 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -27,7 +27,7 @@ mio::abm::Person make_test_person(mio::abm::Location& location, mio::AgeGroup ag { assert(age.get() < params.get_num_groups()); auto rng = mio::RandomNumberGenerator(); - mio::abm::Person p(rng, location.get_id(), age); + mio::abm::Person p(rng, location.get_type(), location.get_id(), age); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index af86aa58e3..2e95746ca9 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -18,32 +18,23 @@ * limitations under the License. */ +#include "abm/location_id.h" #include "abm/parameters.h" #include "abm/person.h" #include "abm/world.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" -TEST(TestLocation, copyLocation) -{ - auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - - auto copied_location = location; - ASSERT_EQ(copied_location.get_type(), mio::abm::LocationType::School); - ASSERT_EQ(copied_location.get_index(), location.get_index()); - ASSERT_EQ(copied_location.get_cells().size(), location.get_cells().size()); -} - TEST(TestLocation, initCell) { mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 2); ASSERT_EQ(location.get_cells().size(), 2); } -TEST(TestLocation, getIndex) +TEST(TestLocation, getId) { mio::abm::Location location(mio::abm::LocationType::Home, 0, num_age_groups); - ASSERT_EQ((int)location.get_index(), 0); + ASSERT_EQ(location.get_id(), mio::abm::LocationId(0)); } TEST(TestLocation, reachCapacity) @@ -83,10 +74,10 @@ TEST(TestLocation, reachCapacity) auto p1 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::InfectedNoSymptoms); auto p2 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible); - world.get_person(p1).set_assigned_location(school_id); - world.get_person(p2).set_assigned_location(school_id); - world.get_person(p1).set_assigned_location(home_id); - world.get_person(p2).set_assigned_location(home_id); + world.get_person(p1).set_assigned_location(mio::abm::LocationType::School, school_id); + world.get_person(p2).set_assigned_location(mio::abm::LocationType::School, school_id); + world.get_person(p1).set_assigned_location(mio::abm::LocationType::Home, home_id); + world.get_person(p2).set_assigned_location(mio::abm::LocationType::Home, home_id); world.get_location(school_id).set_capacity(1, 66); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 8baeeb1173..5c2f48b1b2 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -36,7 +36,7 @@ TEST(TestPerson, init) EXPECT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(person.get_location(), location.get_id()); - EXPECT_EQ(person.get_person_id(), mio::abm::PersonId::invalid_id()); + EXPECT_EQ(person.get_id(), mio::abm::PersonId::invalid_id()); } TEST(TestPerson, copyPerson) @@ -50,7 +50,7 @@ TEST(TestPerson, copyPerson) EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(copied_person.get_location(), copied_location.get_id()); - EXPECT_EQ(copied_person.get_person_id(), mio::abm::PersonId::invalid_id()); + EXPECT_EQ(copied_person.get_id(), mio::abm::PersonId::invalid_id()); } TEST(TestPerson, migrate) @@ -87,10 +87,10 @@ TEST(TestPerson, setGetAssignedLocation) mio::abm::Location location(mio::abm::LocationType::Work, 2, num_age_groups); auto person = mio::abm::Person(rng, location.get_id(), age_group_35_to_59); person.set_assigned_location(location.get_id()); - EXPECT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 2); + EXPECT_EQ((int)person.get_assigned_location(mio::abm::LocationType::Work), 2); person.set_assigned_location({4, mio::abm::LocationType::Work}); - EXPECT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 4); + EXPECT_EQ((int)person.get_assigned_location(mio::abm::LocationType::Work), 4); } TEST(TestPerson, quarantine) diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index e3334907a6..5863d64116 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -108,13 +108,13 @@ TEST(TestSimulation, advanceWithCommonHistory) mio::abm::TripList& trip_list = world.get_trip_list(); // We add trips for person two to test the history and if it is working correctly - mio::abm::Trip trip1(person2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id); - mio::abm::Trip trip2(person2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id); - mio::abm::Trip trip3(person2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(4), hospital_id); - mio::abm::Trip trip4(person2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(5), social_id); - mio::abm::Trip trip5(person2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(6), basics_id); - mio::abm::Trip trip6(person2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(7), public_id); - mio::abm::Trip trip7(person2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), home_id); + mio::abm::Trip trip1(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id); + mio::abm::Trip trip2(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id); + mio::abm::Trip trip3(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(4), hospital_id); + mio::abm::Trip trip4(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(5), social_id); + mio::abm::Trip trip5(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(6), basics_id); + mio::abm::Trip trip6(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(7), public_id); + mio::abm::Trip trip7(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), home_id); trip_list.add_trip(trip1); trip_list.add_trip(trip2); diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 8c087c9494..93d3c151a2 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -27,7 +27,7 @@ TEST(TestWorld, init) EXPECT_EQ(world.get_locations().size(), 1); EXPECT_EQ(world.get_locations()[0].get_type(), mio::abm::LocationType::Cemetery); - ASSERT_THAT(world.get_persons(), testing::ElementsAre()); + EXPECT_THAT(world.get_persons(), testing::ElementsAre()); } TEST(TestWorld, addLocation) @@ -38,8 +38,8 @@ TEST(TestWorld, addLocation) auto work_id = world.add_location(mio::abm::LocationType::Work); auto home_id = world.add_location(mio::abm::LocationType::Home); - ASSERT_EQ((int)school_id1.index, 1); - ASSERT_EQ((int)school_id2.index, 2); + ASSERT_EQ(school_id1.get(), 1); + ASSERT_EQ(school_id2.get(), 2); auto& school1 = world.get_location(school_id1); auto& school2 = world.get_location(school_id2); @@ -106,9 +106,9 @@ TEST(TestWorld, findLocation) auto person_id = add_test_person(world, home_id); auto& person = world.get_person(person_id); - person.set_assigned_location(home_id); - person.set_assigned_location(work_id); - person.set_assigned_location(school_id); + person.set_assigned_location(mio::abm::LocationType::Home, home_id); + person.set_assigned_location(mio::abm::LocationType::School, work_id); + person.set_assigned_location(mio::abm::LocationType::Work, school_id); ASSERT_EQ(world.find_location(mio::abm::LocationType::Work, person_id), work_id); ASSERT_EQ(world.find_location(mio::abm::LocationType::School, person_id), school_id); @@ -154,9 +154,9 @@ TEST(TestWorld, evolveStateTransition) auto& p2 = world.get_persons()[1]; auto& p3 = world.get_persons()[2]; - p1.set_assigned_location(location1); - p2.set_assigned_location(location1); - p3.set_assigned_location(location2); + p1.set_assigned_location(mio::abm::LocationType::School, location1); + p2.set_assigned_location(mio::abm::LocationType::School, location1); + p3.set_assigned_location(mio::abm::LocationType::Work, location2); //setup mock so p2 becomes infected ScopedMockDistribution>>> @@ -213,12 +213,12 @@ TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a uni auto& p1 = world.get_person(pid1); auto& p2 = world.get_person(pid2); - p1.set_assigned_location(school_id); - p2.set_assigned_location(school_id); - p1.set_assigned_location(work_id); - p2.set_assigned_location(work_id); - p1.set_assigned_location(home_id); - p2.set_assigned_location(home_id); + p1.set_assigned_location(mio::abm::LocationType::School, school_id); + p2.set_assigned_location(mio::abm::LocationType::School, school_id); + p1.set_assigned_location(mio::abm::LocationType::Work, work_id); + p2.set_assigned_location(mio::abm::LocationType::Work, work_id); + p1.set_assigned_location(mio::abm::LocationType::Home, home_id); + p2.set_assigned_location(mio::abm::LocationType::Home, home_id); ScopedMockDistribution>>> mock_exponential_dist; @@ -280,24 +280,24 @@ TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a uni auto& p4 = world.get_person(pid4); auto& p5 = world.get_person(pid5); - p1.set_assigned_location(event_id); - p2.set_assigned_location(event_id); - p1.set_assigned_location(work_id); - p2.set_assigned_location(work_id); - p1.set_assigned_location(home_id); - p2.set_assigned_location(home_id); - p3.set_assigned_location(home_id); - p4.set_assigned_location(home_id); - p3.set_assigned_location(hospital_id); - p4.set_assigned_location(hospital_id); - p5.set_assigned_location(event_id); - p5.set_assigned_location(work_id); - p5.set_assigned_location(home_id); + p1.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id); + p2.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id); + p1.set_assigned_location(mio::abm::LocationType::Work, work_id); + p2.set_assigned_location(mio::abm::LocationType::Work, work_id); + p1.set_assigned_location(mio::abm::LocationType::Home, home_id); + p2.set_assigned_location(mio::abm::LocationType::Home, home_id); + p3.set_assigned_location(mio::abm::LocationType::Home, home_id); + p4.set_assigned_location(mio::abm::LocationType::Home, home_id); + p3.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id); + p4.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id); + p5.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id); + p5.set_assigned_location(mio::abm::LocationType::Work, work_id); + p5.set_assigned_location(mio::abm::LocationType::Home, home_id); mio::abm::TripList& data = world.get_trip_list(); - mio::abm::Trip trip1(p1.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id); - mio::abm::Trip trip2(p2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id); - mio::abm::Trip trip3(p5.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id); + mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id); + mio::abm::Trip trip2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id); + mio::abm::Trip trip3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id); data.add_trip(trip1); data.add_trip(trip2); data.add_trip(trip3); @@ -320,9 +320,9 @@ TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a uni EXPECT_EQ(world.get_number_persons(home_id), 1); EXPECT_EQ(world.get_number_persons(hospital_id), 1); - world.migrate(p1.get_person_id(), home_id); - world.migrate(p2.get_person_id(), home_id); - world.migrate(p5.get_person_id(), home_id); + world.migrate(p1.get_id(), home_id); + world.migrate(p2.get_id(), home_id); + world.migrate(p5.get_id(), home_id); t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); world.get_trip_list().reset_index(); @@ -339,12 +339,12 @@ TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a uni EXPECT_EQ(world.get_number_persons(home_id), 2); bool weekend = true; - mio::abm::Trip tripweekend1(p1.get_person_id(), - mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), event_id); - mio::abm::Trip tripweekend2(p2.get_person_id(), - mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), home_id); - mio::abm::Trip tripweekend3(p5.get_person_id(), - mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), work_id); + mio::abm::Trip tripweekend1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + event_id); + mio::abm::Trip tripweekend2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + home_id); + mio::abm::Trip tripweekend3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + work_id); data.add_trip(tripweekend1, weekend); data.add_trip(tripweekend2, weekend); data.add_trip(tripweekend3, weekend); @@ -387,33 +387,33 @@ TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a uni auto& p_dead = world.get_persons()[0]; auto& p_severe = world.get_persons()[1]; - p_dead.set_assigned_location(icu_id); - p_dead.set_assigned_location(work_id); - p_dead.set_assigned_location(home_id); - p_severe.set_assigned_location(hospital_id); - p_severe.set_assigned_location(icu_id); - p_severe.set_assigned_location(home_id); + p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id); + p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id); + p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id); + p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id); + p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id); + p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id); // Add trip to see if a dead person can move outside of cemetery by scheduled mio::abm::TripList& trip_list = world.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id); - mio::abm::Trip trip2(p_dead.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); - mio::abm::Trip trip3(p_severe.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); + mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id); + mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); + mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); trip_list.add_trip(trip1); trip_list.add_trip(trip2); trip_list.add_trip(trip3); // Check the dead person got burried and the severely infected person starts in Hospital world.evolve(t, dt); - EXPECT_EQ(world.get_location(p_dead.get_person_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_dead.get_id()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(world.get_location(p_severe.get_person_id()).get_type(), mio::abm::LocationType::Hospital); + EXPECT_EQ(world.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Hospital); // Check the dead person is still in Cemetery and the severely infected person dies and got burried world.evolve(t + dt, dt); - EXPECT_EQ(world.get_location(p_dead.get_person_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_dead.get_id()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t + dt), mio::abm::InfectionState::Dead); - EXPECT_EQ(world.get_location(p_severe.get_person_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(world.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); } } @@ -436,8 +436,8 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time); auto& person = world.get_person(pid); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); - person.set_assigned_location(home_id); - person.set_assigned_location(work_id); + person.set_assigned_location(mio::abm::LocationType::Home, home_id); + person.set_assigned_location(mio::abm::LocationType::Work, work_id); auto testing_criteria = mio::abm::TestingCriteria(); testing_criteria.add_infection_state(mio::abm::InfectionState::InfectedSymptoms); diff --git a/cpp/tests/test_json_serializer.cpp b/cpp/tests/test_json_serializer.cpp index a38eb38ddf..da18cb07a0 100644 --- a/cpp/tests/test_json_serializer.cpp +++ b/cpp/tests/test_json_serializer.cpp @@ -493,10 +493,10 @@ TEST(TestJsonSerializer, abmPerson) auto person = make_test_person(location); auto js = mio::serialize_json(person); Json::Value expected_json; - expected_json["Location"]["index"] = Json::UInt(location.get_index()); + expected_json["Location"]["index"] = Json::UInt(location.get_id()); expected_json["Location"]["type"] = Json::UInt(location.get_type()); expected_json["age"] = Json::UInt(2); - expected_json["id"] = Json::UInt(person.get_person_id()); + expected_json["id"] = Json::UInt(person.get_id()); ASSERT_EQ(js.value(), expected_json); // auto r = mio::deserialize_json(expected_json, mio::Tag()); @@ -506,20 +506,19 @@ TEST(TestJsonSerializer, abmPerson) TEST(TestJsonSerializer, abmTrip) { - auto world = mio::abm::World(num_age_groups); - auto home_id = world.add_location(mio::abm::LocationType::Home); - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto& home = world.get_location(home_id); - auto person = make_test_person(home); - mio::abm::Trip trip(person.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), work_id, home_id); + mio::abm::Location home{mio::abm::LocationType::Home, 0}; + mio::abm::Location work{mio::abm::LocationType::Work, 1}; + auto person = make_test_person(home); + // add a trip from home (0) to work (1) + mio::abm::Trip trip(person.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), 1, 0); auto js = mio::serialize_json(trip, true); Json::Value expected_json; - expected_json["person_id"] = Json::UInt(person.get_person_id()); + expected_json["person_id"] = Json::UInt(person.get_id()); expected_json["time"] = Json::Int(mio::abm::hours(8).seconds()); - expected_json["destination_index"] = Json::UInt(work_id.index); - expected_json["destination_type"] = Json::UInt(work_id.type); - expected_json["origin_index"] = Json::UInt(home_id.index); - expected_json["origin_type"] = Json::UInt(home_id.type); + expected_json["destination_index"] = Json::UInt(1); // work + expected_json["destination_type"] = Json::UInt(mio::abm::LocationType::Work); + expected_json["origin_index"] = Json::UInt(0); // home + expected_json["origin_type"] = Json::UInt(mio::abm::LocationType::Home); ASSERT_EQ(js.value(), expected_json); auto r = mio::deserialize_json(expected_json, mio::Tag()); @@ -542,16 +541,16 @@ TEST(TestJsonSerializer, abmWorld) expected_json["num_agegroups"] = Json::UInt(num_age_groups); expected_json["trips"][0]["person_id"] = Json::UInt(person); expected_json["trips"][0]["time"] = Json::Int(mio::abm::hours(8).seconds()); - expected_json["trips"][0]["destination_index"] = Json::UInt(work_id.index); - expected_json["trips"][0]["destination_type"] = Json::UInt(work_id.type); - expected_json["trips"][0]["origin_index"] = Json::UInt(home_id.index); - expected_json["trips"][0]["origin_type"] = Json::UInt(home_id.type); + expected_json["trips"][0]["destination_index"] = Json::UInt(1); // work_id + expected_json["trips"][0]["destination_type"] = Json::UInt(mio::abm::LocationType::Work); + expected_json["trips"][0]["origin_index"] = Json::UInt(0); // home_id + expected_json["trips"][0]["origin_type"] = Json::UInt(mio::abm::LocationType::Home); expected_json["trips"][1]["person_id"] = Json::UInt(person); expected_json["trips"][1]["time"] = Json::Int(mio::abm::hours(11).seconds()); - expected_json["trips"][1]["destination_index"] = Json::UInt(work_id.index); - expected_json["trips"][1]["destination_type"] = Json::UInt(work_id.type); - expected_json["trips"][1]["origin_index"] = Json::UInt(home_id.index); - expected_json["trips"][1]["origin_type"] = Json::UInt(home_id.type); + expected_json["trips"][1]["destination_index"] = Json::UInt(1); // work_id + expected_json["trips"][1]["destination_type"] = Json::UInt(mio::abm::LocationType::Work); + expected_json["trips"][1]["origin_index"] = Json::UInt(0); // home_id + expected_json["trips"][1]["origin_type"] = Json::UInt(mio::abm::LocationType::Home); expected_json["locations"][0]["index"] = Json::UInt(0); expected_json["locations"][0]["type"] = Json::UInt(mio::abm::LocationType::Cemetery); expected_json["locations"][1]["index"] = Json::UInt(1); From 8fe0c9a9557872325c8a5a6cead6fc0e55c01f8e Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:25:12 +0200 Subject: [PATCH 40/54] move location type to location --- cpp/models/abm/person.cpp | 4 +- cpp/models/abm/person.h | 2 +- cpp/models/abm/testing_strategy.cpp | 26 +++-- cpp/models/abm/testing_strategy.h | 6 +- cpp/models/abm/world.cpp | 2 +- cpp/simulations/abm_braunschweig.cpp | 4 +- cpp/tests/test_abm_household.cpp | 16 +-- cpp/tests/test_abm_infection.cpp | 2 +- cpp/tests/test_abm_lockdown_rules.cpp | 48 ++++----- cpp/tests/test_abm_masks.cpp | 10 +- cpp/tests/test_abm_migration_rules.cpp | 42 ++++---- cpp/tests/test_abm_person.cpp | 52 ++++------ cpp/tests/test_abm_simulation.cpp | 75 +++++++------- cpp/tests/test_abm_testing_strategy.cpp | 4 +- cpp/tests/test_abm_world.cpp | 16 +-- cpp/tests/test_json_serializer.cpp | 126 ++++++++++++------------ 16 files changed, 204 insertions(+), 231 deletions(-) diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 48504e7479..ce4bcc96f6 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -31,9 +31,9 @@ namespace mio namespace abm { -Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location, AgeGroup age, +Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, AgeGroup age, PersonId person_id) - : m_location(location) + : m_location(location_id) , m_location_type(location_type) , m_assigned_locations((uint32_t)LocationType::Count, LocationId::invalid_id()) , m_quarantine_start(TimePoint(-(std::numeric_limits::max() / 2))) diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index aa33a4fcf1..478998e141 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -53,7 +53,7 @@ class Person * @param[in] age The AgeGroup of the Person. * @param[in] person_id Index of the Person. */ - explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location, AgeGroup age, + explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, AgeGroup age, PersonId person_id = PersonId::invalid_id()); explicit Person(const Person& other, PersonId id); diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index 6a84da7714..20053ae0c3 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -21,7 +21,6 @@ #include "abm/testing_strategy.h" #include "abm/location_id.h" #include "memilio/utils/random_number_generator.h" -#include #include namespace mio @@ -116,8 +115,7 @@ bool TestingScheme::run_scheme(PersonalRandomNumberGenerator& rng, Person& perso return true; } -TestingStrategy::TestingStrategy(const std::unordered_map, - std::vector, hash>& location_to_schemes_map) +TestingStrategy::TestingStrategy(const std::vector& location_to_schemes_map) : m_location_to_schemes_map(location_to_schemes_map.begin(), location_to_schemes_map.end()) { } @@ -125,18 +123,17 @@ TestingStrategy::TestingStrategy(const std::unordered_map(1, scheme)); + m_location_to_schemes_map.push_back({loc_type, loc_id, std::vector(1, scheme)}); } else { //add scheme to existing vector if the scheme doesn't exist yet - auto& schemes = iter_schemes->second; + auto& schemes = iter_schemes->schemes; if (std::find(schemes.begin(), schemes.end(), scheme) == schemes.end()) { schemes.push_back(scheme); } @@ -146,14 +143,13 @@ void TestingStrategy::add_testing_scheme(const LocationType& loc_type, const Loc void TestingStrategy::remove_testing_scheme(const LocationType& loc_type, const LocationId& loc_id, const TestingScheme& scheme) { - auto key = std::tie(loc_type, loc_id); auto iter_schemes = - std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [&key](const auto& p) { - return std::get<0>(p) == std::get<0>(key) && std::get<1>(p) == std::get<1>(key); + std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [&](const auto& p) { + return p.type == loc_type && p.id == loc_id; }); if (iter_schemes != m_location_to_schemes_map.end()) { //remove the scheme from the list - auto& schemes_vector = iter_schemes->second; + auto& schemes_vector = iter_schemes->schemes; auto last = std::remove(schemes_vector.begin(), schemes_vector.end(), scheme); schemes_vector.erase(last, schemes_vector.end()); //delete the list of schemes for this location if no schemes left @@ -185,12 +181,12 @@ bool TestingStrategy::run_strategy(PersonalRandomNumberGenerator& rng, Person& p for (auto key : {std::make_pair(location.get_type(), location.get_id()), std::make_pair(location.get_type(), LocationId::invalid_id())}) { auto iter_schemes = - std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [key](auto& p) { - return p.first == key; + std::find_if(m_location_to_schemes_map.begin(), m_location_to_schemes_map.end(), [&](const auto& p) { + return p.type == key.first && p.id == key.second; }); if (iter_schemes != m_location_to_schemes_map.end()) { //apply all testing schemes that are found - auto& schemes = iter_schemes->second; + auto& schemes = iter_schemes->schemes; if (!std::all_of(schemes.begin(), schemes.end(), [&rng, &person, t](TestingScheme& ts) { return !ts.is_active() || ts.run_scheme(rng, person, t); })) { diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 5002442ba1..3ae3bf5b4b 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -179,8 +179,7 @@ class TestingStrategy * @param[in] testing_schemes Vector of TestingSchemes that are checked for testing. */ TestingStrategy() = default; - explicit TestingStrategy(const std::unordered_map, std::vector, - hash>& location_to_schemes_map); + explicit TestingStrategy(const std::vector& location_to_schemes_map); /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at a certain Location. @@ -238,8 +237,7 @@ class TestingStrategy bool run_strategy(PersonalRandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t); private: - std::vector>> - m_location_to_schemes_map; ///< Set of schemes that are checked for testing. + std::vector m_location_to_schemes_map; ///< Set of schemes that are checked for testing. }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 60bc57ae50..b7c08e1505 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -36,7 +36,7 @@ namespace abm LocationId World::add_location(LocationType type, uint32_t num_cells) { LocationId id{static_cast(m_locations.size())}; - m_locations.emplace_back(id, parameters.get_num_groups(), num_cells); + m_locations.emplace_back(type, id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; // mark caches for rebuild diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index 04b36fe604..7d6630f931 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -959,8 +959,8 @@ void write_log_to_file_trip_data(const T& history) auto agent_id = std::get<0>(movement_data[movement_data_index][trip_index]); int start_index = movement_data_index - 1; - using Type = std::tuple; + using Type = std::tuple; while (!std::binary_search(std::begin(movement_data[start_index]), std::end(movement_data[start_index]), movement_data[movement_data_index][trip_index], [](const Type& v1, const Type& v2) { diff --git a/cpp/tests/test_abm_household.cpp b/cpp/tests/test_abm_household.cpp index 4ecadb2413..4b3ef64c91 100644 --- a/cpp/tests/test_abm_household.cpp +++ b/cpp/tests/test_abm_household.cpp @@ -48,8 +48,8 @@ TEST(TestHouseholds, test_add_household_to_world) EXPECT_EQ(persons[3].get_age(), age_group_5_to_14); // Test location - EXPECT_EQ(persons[0].get_location().get_index(), persons[1].get_location().get_index()); - EXPECT_EQ(persons[2].get_location().get_index(), persons[3].get_location().get_index()); + EXPECT_EQ(persons[0].get_location(), persons[1].get_location()); + EXPECT_EQ(persons[2].get_location(), persons[3].get_location()); } TEST(TestHouseholds, test_add_household_group_to_world) @@ -96,11 +96,11 @@ TEST(TestHouseholds, test_add_household_group_to_world) EXPECT_EQ(number_of_age35to59_year_olds, 70); // Test location for some people - EXPECT_EQ(persons[0].get_location().get_index(), persons[1].get_location().get_index()); - EXPECT_EQ(persons[1].get_location().get_index(), persons[5].get_location().get_index()); - EXPECT_EQ(persons[5].get_location().get_index(), persons[10].get_location().get_index()); + EXPECT_EQ(persons[0].get_location(), persons[1].get_location()); + EXPECT_EQ(persons[1].get_location(), persons[5].get_location()); + EXPECT_EQ(persons[5].get_location(), persons[10].get_location()); - EXPECT_EQ(persons[60].get_location().get_index(), persons[61].get_location().get_index()); - EXPECT_EQ(persons[61].get_location().get_index(), persons[62].get_location().get_index()); - EXPECT_EQ(persons[62].get_location().get_index(), persons[63].get_location().get_index()); + EXPECT_EQ(persons[60].get_location(), persons[61].get_location()); + EXPECT_EQ(persons[61].get_location(), persons[62].get_location()); + EXPECT_EQ(persons[62].get_location(), persons[63].get_location()); } diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index 6180b2fb3d..58d8425e2a 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -167,7 +167,7 @@ TEST(TestInfection, getPersonalProtectiveFactor) auto rng = mio::RandomNumberGenerator(); auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = mio::abm::Person(rng, location.get_id(), age_group_15_to_34); + auto person = mio::abm::Person(rng, location.get_type(), location.get_id(), age_group_15_to_34); person.add_new_vaccination(mio::abm::ExposureType::GenericVaccine, mio::abm::TimePoint(0)); auto latest_protection = person.get_latest_protection(); diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index 90038f53b6..b0ecbf2af3 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -47,12 +47,12 @@ TEST(TestLockdownRules, school_closure) .WillOnce(testing::Return(0.2)) .WillRepeatedly(testing::Return(1.0)); - auto p1 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); - p1.set_assigned_location(home.get_id()); - p1.set_assigned_location(school.get_id()); - auto p2 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); - p2.set_assigned_location(home.get_id()); - p2.set_assigned_location(school.get_id()); + auto p1 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); + p1.set_assigned_location(home.get_type(), home.get_id()); + p1.set_assigned_location(school.get_type(), school.get_id()); + auto p2 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); + p2.set_assigned_location(home.get_type(), home.get_id()); + p2.set_assigned_location(school.get_type(), school.get_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; @@ -88,9 +88,9 @@ TEST(TestLockdownRules, school_opening) .WillOnce(testing::Return(0.6)) .WillOnce(testing::Return(0.6)) .WillRepeatedly(testing::Return(1.0)); - auto p = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); - p.set_assigned_location(home.get_id()); - p.set_assigned_location(school.get_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); + p.set_assigned_location(home.get_type(), home.get_id()); + p.set_assigned_location(school.get_type(), school.get_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; @@ -137,12 +137,12 @@ TEST(TestLockdownRules, home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto person1 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); - auto person2 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); - person1.set_assigned_location(home.get_id()); - person1.set_assigned_location(work.get_id()); - person2.set_assigned_location(home.get_id()); - person2.set_assigned_location(work.get_id()); + auto person1 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); + auto person2 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); + person1.set_assigned_location(home.get_type(), home.get_id()); + person1.set_assigned_location(work.get_type(), work.get_id()); + person2.set_assigned_location(home.get_type(), home.get_id()); + person2.set_assigned_location(work.get_type(), work.get_id()); auto p1_rng = mio::abm::PersonalRandomNumberGenerator(rng, person1); ASSERT_EQ(mio::abm::go_to_work(p1_rng, person1, t_morning, dt, params), mio::abm::LocationType::Work); @@ -170,9 +170,9 @@ TEST(TestLockdownRules, no_home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto p = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); - p.set_assigned_location(home.get_id()); - p.set_assigned_location(work.get_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); + p.set_assigned_location(home.get_type(), home.get_id()); + p.set_assigned_location(work.get_type(), work.get_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; @@ -198,9 +198,9 @@ TEST(TestLockdownRules, social_event_closure) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location event(mio::abm::LocationType::SocialEvent, 0, num_age_groups); - auto p = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); - p.set_assigned_location(home.get_id()); - p.set_assigned_location(event.get_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); + p.set_assigned_location(home.get_type(), home.get_id()); + p.set_assigned_location(event.get_type(), event.get_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); mio::abm::close_social_events(t, 1, params); @@ -219,9 +219,9 @@ TEST(TestLockdownRules, social_events_opening) mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location event(mio::abm::LocationType::SocialEvent, 0, num_age_groups); - auto p = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); - p.set_assigned_location(event.get_id()); - p.set_assigned_location(home.get_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); + p.set_assigned_location(event.get_type(), event.get_id()); + p.set_assigned_location(home.get_type(), home.get_id()); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); mio::abm::close_social_events(t_closing, 1, params); diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index 5b457823dd..5266d34f7a 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -65,10 +65,12 @@ TEST(TestMasks, maskProtection) //setup location with some chance of exposure auto t = mio::abm::TimePoint(0); mio::abm::Location infection_location(mio::abm::LocationType::School, 0, num_age_groups); - auto susc_person1 = mio::abm::Person(rng, infection_location.get_id(), age_group_15_to_34); - auto susc_person2 = mio::abm::Person(rng, infection_location.get_id(), age_group_15_to_34); - auto infected1 = make_test_person(infection_location, age_group_15_to_34, - mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior + auto susc_person1 = + mio::abm::Person(rng, infection_location.get_type(), infection_location.get_id(), age_group_15_to_34); + auto susc_person2 = + mio::abm::Person(rng, infection_location.get_type(), infection_location.get_id(), age_group_15_to_34); + auto infected1 = make_test_person(infection_location, age_group_15_to_34, + mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior //cache precomputed results auto dt = mio::abm::days(1); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 3c0cbd6323..287ba73d95 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -29,7 +29,7 @@ TEST(TestMigrationRules, random_migration) int t = 0, dt = 1; auto rng = mio::RandomNumberGenerator(); auto default_type = mio::abm::LocationType::Cemetery; - auto person = mio::abm::Person(rng, {0, default_type}, age_group_15_to_34); + auto person = mio::abm::Person(rng, default_type, 0, age_group_15_to_34); auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, person); auto params = mio::abm::Parameters(num_age_groups); @@ -77,8 +77,8 @@ TEST(TestMigrationRules, 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(rng, home.get_id(), age_group_5_to_14); - auto p_adult = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + auto p_child = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_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); @@ -120,9 +120,9 @@ TEST(TestMigrationRules, 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(rng, home.get_id(), age_group_5_to_14); + auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_6); - auto p_child_goes_to_school_at_8 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + auto p_child_goes_to_school_at_8 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_8 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); @@ -180,9 +180,9 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times_with_smaller_t .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(rng, home.get_id(), age_group_5_to_14); + auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_6); - auto p_child_goes_to_school_at_8_30 = mio::abm::Person(rng, home.get_id(), age_group_5_to_14); + auto p_child_goes_to_school_at_8_30 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); auto rng_child_goes_to_school_at_8_30 = mio::abm::PersonalRandomNumberGenerator(rng, p_child_goes_to_school_at_8_30); @@ -217,7 +217,7 @@ TEST(TestMigrationRules, school_return) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location school(mio::abm::LocationType::School, 0, num_age_groups); - auto p_child = mio::abm::Person(rng, school.get_id(), age_group_5_to_14); + auto p_child = mio::abm::Person(rng, school.get_type(), school.get_id(), age_group_5_to_14); auto rng_child = mio::abm::PersonalRandomNumberGenerator(rng, p_child); auto t = mio::abm::TimePoint(0) + mio::abm::hours(15); @@ -244,9 +244,9 @@ TEST(TestMigrationRules, worker_goes_to_work) .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); @@ -284,9 +284,9 @@ TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); @@ -325,9 +325,9 @@ TEST(TestMigrationRules, workers_go_to_work_in_different_times) .WillRepeatedly(testing::Return(1.0)); - auto p_adult_goes_to_work_at_6 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + auto p_adult_goes_to_work_at_6 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); auto rng_adult_goes_to_work_at_6 = mio::abm::PersonalRandomNumberGenerator(rng, p_adult_goes_to_work_at_6); - auto p_adult_goes_to_work_at_8 = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + auto p_adult_goes_to_work_at_8 = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); auto rng_adult_goes_to_work_at_8 = mio::abm::PersonalRandomNumberGenerator(rng, p_adult_goes_to_work_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); @@ -361,7 +361,7 @@ TEST(TestMigrationRules, work_return) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); - auto p_adult = mio::abm::Person(rng, work.get_id(), age_group_35_to_59); + auto p_adult = mio::abm::Person(rng, work.get_type(), work.get_id(), age_group_35_to_59); auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); auto dt = mio::abm::hours(1); @@ -429,7 +429,7 @@ TEST(TestMigrationRules, go_shopping) auto p_hosp = make_test_person(hospital, age_group_0_to_4, mio::abm::InfectionState::InfectedSymptoms, t_weekday); auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(rng, p_hosp); - auto p_home = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); + auto p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); ASSERT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), @@ -469,10 +469,10 @@ TEST(TestMigrationRules, go_event) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); - auto p_work = mio::abm::Person(rng, work.get_id(), age_group_35_to_59); + auto p_work = mio::abm::Person(rng, work.get_type(), work.get_id(), age_group_35_to_59); auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - auto p_home = mio::abm::Person(rng, home.get_id(), age_group_60_to_79); + auto p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); auto t_weekday = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(20); @@ -504,11 +504,11 @@ TEST(TestMigrationRules, 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, 0, num_age_groups); - auto p = mio::abm::Person(rng, home.get_id(), age_group_15_to_34); + mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 1, num_age_groups); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); - mio::abm::migrate(p, social_event); + EXPECT_TRUE(mio::abm::migrate(p, social_event)); interact_testing(rng_p, p, social_event, {p}, t, dt, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 5c2f48b1b2..7acbaec222 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/location_id.h" #include "abm/model_functions.h" #include "abm/location_type.h" #include "abm/migration_rules.h" @@ -32,34 +33,20 @@ TEST(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(rng, location.get_id(), age_group_60_to_79); + auto person = mio::abm::Person(rng, location.get_type(), location.get_id(), age_group_60_to_79); EXPECT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(person.get_location(), location.get_id()); EXPECT_EQ(person.get_id(), mio::abm::PersonId::invalid_id()); } -TEST(TestPerson, copyPerson) -{ - auto rng = mio::RandomNumberGenerator(); - auto location = mio::abm::Location(mio::abm::LocationType::Work, 0, num_age_groups); - auto t = mio::abm::TimePoint(0); - auto person = mio::abm::Person(rng, location.get_id(), age_group_60_to_79); - auto copied_location = location.copy(); - auto copied_person = person.copy_person(copied_location.get_id()); - - EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); - EXPECT_EQ(copied_person.get_location(), copied_location.get_id()); - EXPECT_EQ(copied_person.get_id(), mio::abm::PersonId::invalid_id()); -} - TEST(TestPerson, migrate) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 0, 6, 1); - mio::abm::Location loc2(mio::abm::LocationType::School, 0, num_age_groups); + mio::abm::Location loc2(mio::abm::LocationType::School, 1, num_age_groups); mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 0, 6, 2); auto person = make_test_person(home, age_group_0_to_4, mio::abm::InfectionState::Recovered); @@ -68,14 +55,14 @@ TEST(TestPerson, migrate) EXPECT_EQ(person.get_location(), loc1.get_id()); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Unknown); - mio::abm::migrate(person, loc2, mio::abm::TransportMode::Walking, {0}); + EXPECT_TRUE(mio::abm::migrate(person, loc2, mio::abm::TransportMode::Walking, {0})); EXPECT_EQ(person.get_location(), loc2.get_id()); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Walking); - mio::abm::migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1}); + EXPECT_TRUE(mio::abm::migrate(person, loc3, mio::abm::TransportMode::Bike, {0, 1})); - EXPECT_EQ(person.get_cells().size(), 2); + ASSERT_EQ(person.get_cells().size(), 2); EXPECT_EQ(person.get_cells()[0], 0u); EXPECT_EQ(person.get_cells()[1], 1u); EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Bike); @@ -85,12 +72,12 @@ TEST(TestPerson, setGetAssignedLocation) { auto rng = mio::RandomNumberGenerator(); mio::abm::Location location(mio::abm::LocationType::Work, 2, num_age_groups); - auto person = mio::abm::Person(rng, location.get_id(), age_group_35_to_59); - person.set_assigned_location(location.get_id()); - EXPECT_EQ((int)person.get_assigned_location(mio::abm::LocationType::Work), 2); + auto person = mio::abm::Person(rng, location.get_type(), location.get_id(), age_group_35_to_59); + person.set_assigned_location(location.get_type(), location.get_id()); + EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(2)); - person.set_assigned_location({4, mio::abm::LocationType::Work}); - EXPECT_EQ((int)person.get_assigned_location(mio::abm::LocationType::Work), 4); + person.set_assigned_location(mio::abm::LocationType::Work, mio::abm::LocationId(4)); + EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(4)); } TEST(TestPerson, quarantine) @@ -146,7 +133,7 @@ TEST(TestPerson, get_tested) mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); auto infected = make_test_person(loc, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); auto rng_infected = mio::abm::PersonalRandomNumberGenerator(rng, infected); - auto susceptible = mio::abm::Person(rng, loc.get_id(), age_group_15_to_34); + auto susceptible = mio::abm::Person(rng, loc.get_type(), loc.get_id(), age_group_15_to_34); auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); auto pcr_test = mio::abm::PCRTest(); @@ -191,12 +178,14 @@ TEST(TestPerson, get_tested) TEST(TestPerson, getCells) { mio::abm::Location home(mio::abm::LocationType::Home, 0, 6, 1); - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 2); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 1, 6, 7); auto person = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); - mio::abm::migrate(person, location, mio::abm::TransportMode::Unknown, {0, 1}); + EXPECT_TRUE(mio::abm::migrate(person, location, mio::abm::TransportMode::Unknown, {3, 5})); - EXPECT_EQ(person.get_cells().size(), 2); // TODO: is this a meaningfull test? + ASSERT_EQ(person.get_cells().size(), 2); + EXPECT_EQ(person.get_cells()[0], 3); + EXPECT_EQ(person.get_cells()[1], 5); } TEST(TestPerson, interact) @@ -207,7 +196,7 @@ TEST(TestPerson, interact) auto infection_parameters = mio::abm::Parameters(num_age_groups); mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::TimePoint t(0); - auto person = mio::abm::Person(rng, loc.get_id(), age_group_15_to_34); + auto person = mio::abm::Person(rng, loc.get_type(), loc.get_id(), age_group_15_to_34); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); auto dt = mio::abm::seconds(8640); //0.1 days interact_testing(rng_person, person, loc, {person}, t, dt, infection_parameters); @@ -292,7 +281,7 @@ TEST(TestPerson, getLatestProtection) { auto rng = mio::RandomNumberGenerator(); auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); - auto person = mio::abm::Person(rng, location.get_id(), age_group_15_to_34); + auto person = mio::abm::Person(rng, location.get_type(), location.get_id(), age_group_15_to_34); auto prng = mio::abm::PersonalRandomNumberGenerator(rng, person); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); @@ -313,8 +302,7 @@ TEST(TestPerson, getLatestProtection) TEST(Person, rng) { auto rng = mio::RandomNumberGenerator(); - mio::abm::Location loc(mio::abm::LocationType::Home, 0); - auto p = mio::abm::Person(rng, loc.get_id(), age_group_35_to_59, mio::abm::PersonId(13)); + auto p = mio::abm::Person(rng, mio::abm::LocationType::Home, 0, age_group_35_to_59, mio::abm::PersonId(13)); EXPECT_EQ(p.get_rng_counter(), mio::Counter(0)); diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index 5863d64116..22034ad7b9 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -27,20 +27,15 @@ TEST(TestSimulation, advance_random) auto world = mio::abm::World(num_age_groups); auto location1 = world.add_location(mio::abm::LocationType::School); auto location2 = world.add_location(mio::abm::LocationType::School); - auto pid1 = world.add_person(location1, age_group_5_to_14); - auto pid2 = world.add_person(location1, age_group_5_to_14); - auto pid3 = world.add_person(location2, age_group_5_to_14); - auto pid4 = world.add_person(location2, age_group_5_to_14); + auto p1 = world.add_person(location1, age_group_5_to_14); + auto p2 = world.add_person(location1, age_group_5_to_14); + auto p3 = world.add_person(location2, age_group_5_to_14); + auto p4 = world.add_person(location2, age_group_5_to_14); - auto& p1 = world.get_person(pid1); - auto& p2 = world.get_person(pid2); - auto& p3 = world.get_person(pid3); - auto& p4 = world.get_person(pid4); - - p1.set_assigned_location(location1); - p2.set_assigned_location(location1); - p3.set_assigned_location(location2); - p4.set_assigned_location(location2); + world.assign_location(p1, location1); + world.assign_location(p2, location1); + world.assign_location(p3, location2); + world.assign_location(p4, location2); auto sim = mio::abm::Simulation(mio::abm::TimePoint(0), std::move(world)); @@ -82,39 +77,35 @@ TEST(TestSimulation, advanceWithCommonHistory) auto basics_id = world.add_location(mio::abm::LocationType::BasicsShop); auto public_id = world.add_location(mio::abm::LocationType::PublicTransport); - auto pid1 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Exposed); - auto pid2 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::Exposed); - auto pid3 = add_test_person(world, home_id, age_group_35_to_59, mio::abm::InfectionState::Dead); - - auto& person1 = world.get_person(pid1); - auto& person2 = world.get_person(pid2); - auto& person3 = world.get_person(pid3); - - person1.set_assigned_location(home_id); - person2.set_assigned_location(home_id); - person3.set_assigned_location(home_id); - person1.set_assigned_location(school_id); - person2.set_assigned_location(work_id); - person2.set_assigned_location(icu_id); - person2.set_assigned_location(hospital_id); - person1.set_assigned_location(social_id); - person2.set_assigned_location(social_id); - person3.set_assigned_location(social_id); - person1.set_assigned_location(basics_id); - person2.set_assigned_location(basics_id); - person3.set_assigned_location(basics_id); - person2.set_assigned_location(public_id); + auto person1 = add_test_person(world, home_id, age_group_5_to_14, mio::abm::InfectionState::Exposed); + auto person2 = add_test_person(world, home_id, age_group_15_to_34, mio::abm::InfectionState::Exposed); + auto person3 = add_test_person(world, home_id, age_group_35_to_59, mio::abm::InfectionState::Dead); + + world.assign_location(person1, home_id); + world.assign_location(person2, home_id); + world.assign_location(person3, home_id); + world.assign_location(person1, school_id); + world.assign_location(person2, work_id); + world.assign_location(person2, icu_id); + world.assign_location(person2, hospital_id); + world.assign_location(person1, social_id); + world.assign_location(person2, social_id); + world.assign_location(person3, social_id); + world.assign_location(person1, basics_id); + world.assign_location(person2, basics_id); + world.assign_location(person3, basics_id); + world.assign_location(person2, public_id); mio::abm::TripList& trip_list = world.get_trip_list(); // We add trips for person two to test the history and if it is working correctly - mio::abm::Trip trip1(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id); - mio::abm::Trip trip2(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id); - mio::abm::Trip trip3(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(4), hospital_id); - mio::abm::Trip trip4(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(5), social_id); - mio::abm::Trip trip5(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(6), basics_id); - mio::abm::Trip trip6(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(7), public_id); - mio::abm::Trip trip7(person2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), home_id); + mio::abm::Trip trip1(person2, mio::abm::TimePoint(0) + mio::abm::hours(2), work_id); + mio::abm::Trip trip2(person2, mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id); + mio::abm::Trip trip3(person2, mio::abm::TimePoint(0) + mio::abm::hours(4), hospital_id); + mio::abm::Trip trip4(person2, mio::abm::TimePoint(0) + mio::abm::hours(5), social_id); + mio::abm::Trip trip5(person2, mio::abm::TimePoint(0) + mio::abm::hours(6), basics_id); + mio::abm::Trip trip6(person2, mio::abm::TimePoint(0) + mio::abm::hours(7), public_id); + mio::abm::Trip trip7(person2, mio::abm::TimePoint(0) + mio::abm::hours(8), home_id); trip_list.add_trip(trip1); trip_list.add_trip(trip2); diff --git a/cpp/tests/test_abm_testing_strategy.cpp b/cpp/tests/test_abm_testing_strategy.cpp index b44c350961..cfb8b98c3b 100644 --- a/cpp/tests/test_abm_testing_strategy.cpp +++ b/cpp/tests/test_abm_testing_strategy.cpp @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "abm/person.h" +#include "abm/testing_strategy.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" @@ -141,7 +141,7 @@ TEST(TestTestingScheme, initAndRunTestingStrategy) .WillOnce(testing::Return(0.5)); mio::abm::TestingStrategy test_strategy = - mio::abm::TestingStrategy(std::unordered_map>()); + mio::abm::TestingStrategy(std::vector()); test_strategy.add_testing_scheme(mio::abm::LocationType::Work, testing_scheme1); test_strategy.add_testing_scheme(mio::abm::LocationType::Work, testing_scheme2); ASSERT_EQ(test_strategy.run_strategy(rng_person1, person1, loc_work, start_date), diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 93d3c151a2..e7ebad2ce2 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -107,17 +107,17 @@ TEST(TestWorld, findLocation) auto& person = world.get_person(person_id); person.set_assigned_location(mio::abm::LocationType::Home, home_id); - person.set_assigned_location(mio::abm::LocationType::School, work_id); - person.set_assigned_location(mio::abm::LocationType::Work, school_id); + person.set_assigned_location(mio::abm::LocationType::Work, work_id); + person.set_assigned_location(mio::abm::LocationType::School, school_id); - ASSERT_EQ(world.find_location(mio::abm::LocationType::Work, person_id), work_id); - ASSERT_EQ(world.find_location(mio::abm::LocationType::School, person_id), school_id); - ASSERT_EQ(world.find_location(mio::abm::LocationType::Home, person_id), home_id); + EXPECT_EQ(world.find_location(mio::abm::LocationType::Work, person_id), work_id); + EXPECT_EQ(world.find_location(mio::abm::LocationType::School, person_id), school_id); + EXPECT_EQ(world.find_location(mio::abm::LocationType::Home, person_id), home_id); auto&& world_test = std::as_const(world); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Work, person_id), work_id); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::School, person_id), school_id); - ASSERT_EQ(world_test.find_location(mio::abm::LocationType::Home, person_id), home_id); + EXPECT_EQ(world_test.find_location(mio::abm::LocationType::Work, person_id), work_id); + EXPECT_EQ(world_test.find_location(mio::abm::LocationType::School, person_id), school_id); + EXPECT_EQ(world_test.find_location(mio::abm::LocationType::Home, person_id), home_id); } TEST(TestWorld, evolveStateTransition) diff --git a/cpp/tests/test_json_serializer.cpp b/cpp/tests/test_json_serializer.cpp index da18cb07a0..9ef34343c3 100644 --- a/cpp/tests/test_json_serializer.cpp +++ b/cpp/tests/test_json_serializer.cpp @@ -487,22 +487,22 @@ TEST(TestJsonSerializer, abmLocation) EXPECT_EQ(r.value(), location); } -TEST(TestJsonSerializer, abmPerson) -{ - auto location = mio::abm::Location(mio::abm::LocationType::School, 0, 6); - auto person = make_test_person(location); - auto js = mio::serialize_json(person); - Json::Value expected_json; - expected_json["Location"]["index"] = Json::UInt(location.get_id()); - expected_json["Location"]["type"] = Json::UInt(location.get_type()); - expected_json["age"] = Json::UInt(2); - expected_json["id"] = Json::UInt(person.get_id()); - ASSERT_EQ(js.value(), expected_json); - - // auto r = mio::deserialize_json(expected_json, mio::Tag()); - // ASSERT_THAT(print_wrap(r), IsSuccess()); - // EXPECT_EQ(r.value(), person); -} +// TEST(TestJsonSerializer, abmPerson) // FIXME: (de)serialize is only partially implemented +// { +// auto location = mio::abm::Location(mio::abm::LocationType::School, 0, 6); +// auto person = make_test_person(location); +// auto js = mio::serialize_json(person); +// Json::Value expected_json; +// expected_json["Location"]["index"] = Json::UInt(location.get_id()); +// expected_json["Location"]["type"] = Json::UInt(location.get_type()); +// expected_json["age"] = Json::UInt(2); +// expected_json["id"] = Json::UInt(person.get_id()); +// ASSERT_EQ(js.value(), expected_json); + +// // auto r = mio::deserialize_json(expected_json, mio::Tag()); +// // ASSERT_THAT(print_wrap(r), IsSuccess()); +// // EXPECT_EQ(r.value(), person); +// } TEST(TestJsonSerializer, abmTrip) { @@ -513,12 +513,10 @@ TEST(TestJsonSerializer, abmTrip) mio::abm::Trip trip(person.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), 1, 0); auto js = mio::serialize_json(trip, true); Json::Value expected_json; - expected_json["person_id"] = Json::UInt(person.get_id()); - expected_json["time"] = Json::Int(mio::abm::hours(8).seconds()); - expected_json["destination_index"] = Json::UInt(1); // work - expected_json["destination_type"] = Json::UInt(mio::abm::LocationType::Work); - expected_json["origin_index"] = Json::UInt(0); // home - expected_json["origin_type"] = Json::UInt(mio::abm::LocationType::Home); + expected_json["person_id"] = Json::UInt(person.get_id()); + expected_json["time"] = Json::Int(mio::abm::hours(8).seconds()); + expected_json["destination"] = Json::UInt(1); // work + expected_json["origin"] = Json::UInt(0); // home ASSERT_EQ(js.value(), expected_json); auto r = mio::deserialize_json(expected_json, mio::Tag()); @@ -526,45 +524,45 @@ TEST(TestJsonSerializer, abmTrip) EXPECT_EQ(r.value(), trip); } -TEST(TestJsonSerializer, abmWorld) -{ - auto world = mio::abm::World(num_age_groups); - auto home_id = world.add_location(mio::abm::LocationType::Home); - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto person = world.add_person(home_id, age_group_15_to_34); - mio::abm::Trip trip1(person, mio::abm::TimePoint(0) + mio::abm::hours(8), work_id, home_id); - mio::abm::Trip trip2(person, mio::abm::TimePoint(0) + mio::abm::hours(11), work_id, home_id); - world.get_trip_list().add_trip(trip1, false); - world.get_trip_list().add_trip(trip2, true); - auto js = mio::serialize_json(world); - Json::Value expected_json; - expected_json["num_agegroups"] = Json::UInt(num_age_groups); - expected_json["trips"][0]["person_id"] = Json::UInt(person); - expected_json["trips"][0]["time"] = Json::Int(mio::abm::hours(8).seconds()); - expected_json["trips"][0]["destination_index"] = Json::UInt(1); // work_id - expected_json["trips"][0]["destination_type"] = Json::UInt(mio::abm::LocationType::Work); - expected_json["trips"][0]["origin_index"] = Json::UInt(0); // home_id - expected_json["trips"][0]["origin_type"] = Json::UInt(mio::abm::LocationType::Home); - expected_json["trips"][1]["person_id"] = Json::UInt(person); - expected_json["trips"][1]["time"] = Json::Int(mio::abm::hours(11).seconds()); - expected_json["trips"][1]["destination_index"] = Json::UInt(1); // work_id - expected_json["trips"][1]["destination_type"] = Json::UInt(mio::abm::LocationType::Work); - expected_json["trips"][1]["origin_index"] = Json::UInt(0); // home_id - expected_json["trips"][1]["origin_type"] = Json::UInt(mio::abm::LocationType::Home); - expected_json["locations"][0]["index"] = Json::UInt(0); - expected_json["locations"][0]["type"] = Json::UInt(mio::abm::LocationType::Cemetery); - expected_json["locations"][1]["index"] = Json::UInt(1); - expected_json["locations"][1]["type"] = Json::UInt(mio::abm::LocationType::Home); - expected_json["locations"][2]["index"] = Json::UInt(2); - expected_json["locations"][2]["type"] = Json::UInt(mio::abm::LocationType::Work); - expected_json["persons"][0]["Location"]["index"] = Json::UInt(1); - expected_json["persons"][0]["Location"]["type"] = Json::UInt(mio::abm::LocationType::Home); - expected_json["persons"][0]["age"] = Json::UInt(2); - expected_json["persons"][0]["id"] = Json::UInt(person); - expected_json["use_migration_rules"] = Json::Value(true); - ASSERT_EQ(js.value(), expected_json); - - // auto r = mio::deserialize_json(expected_json, mio::Tag()); - // ASSERT_THAT(print_wrap(r), IsSuccess()); - // EXPECT_EQ(r.value(), world); -} +// TEST(TestJsonSerializer, abmWorld) // FIXME: (de)serialize is only partially implemented +// { +// auto world = mio::abm::World(num_age_groups); +// auto home_id = world.add_location(mio::abm::LocationType::Home); +// auto work_id = world.add_location(mio::abm::LocationType::Work); +// auto person = world.add_person(home_id, age_group_15_to_34); +// mio::abm::Trip trip1(person, mio::abm::TimePoint(0) + mio::abm::hours(8), work_id, home_id); +// mio::abm::Trip trip2(person, mio::abm::TimePoint(0) + mio::abm::hours(11), work_id, home_id); +// world.get_trip_list().add_trip(trip1, false); +// world.get_trip_list().add_trip(trip2, true); +// auto js = mio::serialize_json(world); +// Json::Value expected_json; +// expected_json["num_agegroups"] = Json::UInt(num_age_groups); +// expected_json["trips"][0]["person_id"] = Json::UInt(person); +// expected_json["trips"][0]["time"] = Json::Int(mio::abm::hours(8).seconds()); +// expected_json["trips"][0]["destination_index"] = Json::UInt(1); // work_id +// expected_json["trips"][0]["destination_type"] = Json::UInt(mio::abm::LocationType::Work); +// expected_json["trips"][0]["origin_index"] = Json::UInt(0); // home_id +// expected_json["trips"][0]["origin_type"] = Json::UInt(mio::abm::LocationType::Home); +// expected_json["trips"][1]["person_id"] = Json::UInt(person); +// expected_json["trips"][1]["time"] = Json::Int(mio::abm::hours(11).seconds()); +// expected_json["trips"][1]["destination_index"] = Json::UInt(1); // work_id +// expected_json["trips"][1]["destination_type"] = Json::UInt(mio::abm::LocationType::Work); +// expected_json["trips"][1]["origin_index"] = Json::UInt(0); // home_id +// expected_json["trips"][1]["origin_type"] = Json::UInt(mio::abm::LocationType::Home); +// expected_json["locations"][0]["index"] = Json::UInt(0); +// expected_json["locations"][0]["type"] = Json::UInt(mio::abm::LocationType::Cemetery); +// expected_json["locations"][1]["index"] = Json::UInt(1); +// expected_json["locations"][1]["type"] = Json::UInt(mio::abm::LocationType::Home); +// expected_json["locations"][2]["index"] = Json::UInt(2); +// expected_json["locations"][2]["type"] = Json::UInt(mio::abm::LocationType::Work); +// expected_json["persons"][0]["Location"]["index"] = Json::UInt(1); +// expected_json["persons"][0]["Location"]["type"] = Json::UInt(mio::abm::LocationType::Home); +// expected_json["persons"][0]["age"] = Json::UInt(2); +// expected_json["persons"][0]["id"] = Json::UInt(person); +// expected_json["use_migration_rules"] = Json::Value(true); +// ASSERT_EQ(js.value(), expected_json); + +// // auto r = mio::deserialize_json(expected_json, mio::Tag()); +// // ASSERT_THAT(print_wrap(r), IsSuccess()); +// // EXPECT_EQ(r.value(), world); +// } From 2f3b4f301651ad797633170fdec4f4a4ffa324ee Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:01:15 +0200 Subject: [PATCH 41/54] update py-simulation and fix CI --- cpp/examples/abm_history_object.cpp | 2 +- cpp/models/abm/location_id.h | 2 +- cpp/models/abm/person_id.h | 2 +- cpp/models/abm/world.cpp | 2 +- cpp/models/abm/world.h | 4 +-- .../memilio/simulation/abm.cpp | 27 +++++++---------- .../memilio/simulation_test/test_abm.py | 29 ++++++++----------- 7 files changed, 29 insertions(+), 39 deletions(-) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 1590502b05..81706ddf7c 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -157,7 +157,7 @@ int main() world.assign_location(pid, hospital); world.assign_location(pid, icu); //assign work/school to people depending on their age - if (person.get_age() == age_group_0_to_4) { + if (person.get_age() == age_group_5_to_14) { world.assign_location(pid, school); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { diff --git a/cpp/models/abm/location_id.h b/cpp/models/abm/location_id.h index 4597f36bf0..e85b565b1a 100644 --- a/cpp/models/abm/location_id.h +++ b/cpp/models/abm/location_id.h @@ -30,7 +30,7 @@ namespace abm { /// Unique identifier for a Location within a World. -struct LocationId : mio::TypeSafe, public OperatorComparison { +struct LocationId : public mio::TypeSafe, public OperatorComparison { /// @brief Create an ID. LocationId(uint32_t id) : mio::TypeSafe(id) diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index 7c298191ee..5649447ebd 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -30,7 +30,7 @@ namespace abm { /// Unique identifier for a Person within a World. -struct PersonId : mio::TypeSafe, public OperatorComparison { +struct PersonId : public mio::TypeSafe, public OperatorComparison { /// @brief Create an ID. PersonId(uint32_t id) : mio::TypeSafe(id) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index b7c08e1505..21829f617f 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -271,7 +271,7 @@ auto World::get_persons() -> Range> LocationId World::find_location(LocationType type, const PersonId person) const { auto location_id = get_person(person).get_assigned_location(type); - assert(location_id != LocationId::invalid_id() && "No location."); + assert(location_id != LocationId::invalid_id() && "The person has no assigned location of that type."); return location_id; } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 1e8e7d178c..94b9ed645f 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -407,14 +407,14 @@ class World const Location& get_location(LocationId id) const { assert(id != LocationId::invalid_id()); - assert(id < LocationId(m_locations.size())); + assert(id < LocationId((uint32_t)m_locations.size())); return m_locations[id.get()]; } Location& get_location(LocationId id) { assert(id != LocationId::invalid_id()); - assert(id < LocationId(m_locations.size())); + assert(id < LocationId((uint32_t)m_locations.size())); return m_locations[id.get()]; } /** @} */ diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 348b652fe0..7cb0f4c919 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -31,6 +31,7 @@ #include "pybind11/cast.h" #include "pybind11/pybind11.h" #include "pybind11/operators.h" +#include #include namespace py = pybind11; @@ -129,20 +130,16 @@ PYBIND11_MODULE(_simulation_abm, m) .def(py::self -= mio::abm::TimeSpan{}); pymio::bind_class(m, "LocationId") - .def(py::init([](uint32_t idx, mio::abm::LocationType type) { - return mio::abm::LocationId{idx, type}; - })) - .def_readwrite("index", &mio::abm::LocationId::index) - .def_readwrite("type", &mio::abm::LocationId::type) - .def(py::self == py::self) - .def(py::self != py::self); + .def(py::init(), py::arg("id")) + .def("index", &mio::abm::LocationId::get); - py::class_(m, "PersonId").def(py::init([](uint32_t id) { - return mio::abm::PersonId{id}; - })); + pymio::bind_class(m, "PersonId") + .def(py::init(), py::arg("id")) + .def("index", &mio::abm::PersonId::get); pymio::bind_class(m, "Person") - .def("set_assigned_location", py::overload_cast(&mio::abm::Person::set_assigned_location)) + .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) .def_property_readonly("is_in_quarantine", &mio::abm::Person::is_in_quarantine); @@ -170,11 +167,11 @@ PYBIND11_MODULE(_simulation_abm, m) .def_readwrite("time", &mio::abm::Vaccination::time); pymio::bind_class(m, "TestingStrategy") - .def(py::init>&>()); + .def(py::init&>()); pymio::bind_class(m, "Location") .def_property_readonly("type", &mio::abm::Location::get_type) - .def_property_readonly("index", &mio::abm::Location::get_index) + .def_property_readonly("id", &mio::abm::Location::get_id) .def_property("infection_parameters", py::overload_cast<>(&mio::abm::Location::get_infection_parameters, py::const_), [](mio::abm::Location& self, mio::abm::LocalInfectionParameters params) { @@ -209,9 +206,7 @@ PYBIND11_MODULE(_simulation_abm, m) static_cast( &mio::abm::World::add_person), py::arg("location_id"), py::arg("age_group")) - .def("get_person", - static_cast(&mio::abm::World::get_person), - py::arg("id"), py::return_value_policy::reference_internal) + .def("assign_location", &mio::abm::World::assign_location, py::arg("person_id"), py::arg("location_id")) .def_property_readonly( "locations", static_cast< diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index 4eca1cb8aa..980d59e106 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -46,7 +46,7 @@ def test_locations(self): social_event_id = world.add_location(abm.LocationType.SocialEvent) self.assertEqual(len(world.locations), 3) - home = world.locations[home_id.index] + home = world.locations[home_id.index()] self.assertEqual(home.type, abm.LocationType.Home) testing_ages = [mio.AgeGroup(0)] @@ -73,13 +73,13 @@ def test_persons(self): p1_id = world.add_person(home_id, mio.AgeGroup(2)) p2_id = world.add_person(social_event_id, mio.AgeGroup(5)) - p1 = world.get_person(p1_id) - p2 = world.get_person(p2_id) + p1 = world.persons[p1_id.index()] + p2 = world.persons[p2_id.index()] # check persons self.assertEqual(len(world.persons), 2) self.assertEqual(p1.age, mio.AgeGroup(2)) - self.assertEqual(p1.location.index, 1) + self.assertEqual(p1.location.index(), 1) self.assertEqual(world.persons[0], p1) self.assertEqual(world.persons[1], p2) @@ -88,21 +88,16 @@ def test_simulation(self): sim = abm.Simulation(t0, num_age_groups) world = sim.world - # add some locations and persons - for type in abm.LocationType.values(): - world.add_location(type) - home_id = abm.LocationId(0, abm.LocationType.Home) - social_event_id = abm.LocationId(0, abm.LocationType.SocialEvent) - work_id = abm.LocationId(0, abm.LocationType.Work) + # add some locations and persons + home_id = world.add_location(abm.LocationType.Home) + social_event_id = world.add_location(abm.LocationType.SocialEvent) + work_id = world.add_location( abm.LocationType.Work) p1_id = world.add_person(home_id, mio.AgeGroup(0)) p2_id = world.add_person(home_id, mio.AgeGroup(2)) - p1 = world.get_person(p1_id) - p2 = world.get_person(p2_id) - for type in abm.LocationType.values(): - p1.set_assigned_location(abm.LocationId(0, type)) - p2.set_assigned_location(abm.LocationId(0, type)) - - social_event = world.locations[social_event_id.index] + + for loc_id in [home_id, social_event_id, work_id]: + world.assign_location(p1_id, loc_id) + world.assign_location(p2_id, loc_id) world.parameters.InfectedSymptomsToSevere[abm.VirusVariant.Wildtype, mio.AgeGroup( 0)] = 0.0 From 7ccacbfb3180ad74a222b53e11a5b022a9b84fb8 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:09:59 +0200 Subject: [PATCH 42/54] delete whitespace --- pycode/memilio-simulation/memilio/simulation_test/test_abm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index 980d59e106..e1ca3c6b51 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -91,7 +91,7 @@ def test_simulation(self): # add some locations and persons home_id = world.add_location(abm.LocationType.Home) social_event_id = world.add_location(abm.LocationType.SocialEvent) - work_id = world.add_location( abm.LocationType.Work) + work_id = world.add_location(abm.LocationType.Work) p1_id = world.add_person(home_id, mio.AgeGroup(0)) p2_id = world.add_person(home_id, mio.AgeGroup(2)) From f806c5ac6b2c0b99de485a15c9edd89dd9ef3379 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:15:18 +0200 Subject: [PATCH 43/54] make another conversion explicit --- cpp/models/abm/world.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 21829f617f..5610b35c05 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -27,6 +27,7 @@ #include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" #include "memilio/utils/stl_util.h" +#include namespace mio { @@ -55,7 +56,7 @@ PersonId World::add_person(const LocationId id, AgeGroup age) PersonId World::add_person(Person&& person) { assert(person.get_location() != LocationId::invalid_id()); - assert(person.get_location() < (LocationId)m_locations.size()); + assert(person.get_location() < LocationId((uint32_t)m_locations.size())); assert(person.get_age() < (AgeGroup)parameters.get_num_groups()); PersonId new_id{static_cast(m_persons.size())}; From 7a0636331960eb88265cb0451fc50ba276f42a57 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:26:14 +0200 Subject: [PATCH 44/54] delete whitespace --- pycode/memilio-simulation/memilio/simulation_test/test_abm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index e1ca3c6b51..cbc87b613e 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -88,13 +88,13 @@ def test_simulation(self): sim = abm.Simulation(t0, num_age_groups) world = sim.world - # add some locations and persons + # add some locations and persons home_id = world.add_location(abm.LocationType.Home) social_event_id = world.add_location(abm.LocationType.SocialEvent) work_id = world.add_location(abm.LocationType.Work) p1_id = world.add_person(home_id, mio.AgeGroup(0)) p2_id = world.add_person(home_id, mio.AgeGroup(2)) - + for loc_id in [home_id, social_event_id, work_id]: world.assign_location(p1_id, loc_id) world.assign_location(p2_id, loc_id) From 852d68e9fd6ebc347431b0938ea9eff55395dec6 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:38:05 +0200 Subject: [PATCH 45/54] remove unrelated code --- cpp/examples/cli.cpp | 77 -------------------------------------------- 1 file changed, 77 deletions(-) diff --git a/cpp/examples/cli.cpp b/cpp/examples/cli.cpp index 44665b3105..7dcf0375d7 100644 --- a/cpp/examples/cli.cpp +++ b/cpp/examples/cli.cpp @@ -1,15 +1,5 @@ #include "memilio/io/cli.h" -#include "memilio/io/io.h" -#include "memilio/io/json_serializer.h" -#include -#include -#include -#include -#include -#include -#include -#include #include struct Name { @@ -66,75 +56,8 @@ struct Greeting { } }; -using HourlyContactMatrix = std::array; - -mio::IOResult read_hourly_contact_matrix(const std::string& csv_file) -{ - std::ifstream file(csv_file); - if (!file.good()) { - return mio::failure(mio::StatusCode::FileNotFound, "Could not open " + csv_file + "."); - } - - std::string reader; - - std::vector matrix_entries; - int current_hour = 0; - int t, row, col; - double val; - - HourlyContactMatrix hcm; - - // skip first line - std::getline(file, reader); - // possible EOF here - // read csv - while (std::getline(file, reader)) { - int status = sscanf(reader.c_str(), "%i,%i,%i,%lf\n", &t, &row, &col, &val); - - if (status != 4) { - return mio::failure(mio::StatusCode::InvalidFileFormat, - "Unexpected format while reading " + csv_file + ". Line reads \"" + reader + "\""); - } - - printf("%i,%i,%i,%lf\n", t, row, col, val); - - if (t > current_hour) { - size_t n = std::round(std::sqrt(matrix_entries.size())); - hcm[current_hour] = Eigen::MatrixXd(n, n); - for (size_t i = 0; i < matrix_entries.size(); i++) { - hcm[current_hour].data()[i] = matrix_entries[i]; - } - matrix_entries.clear(); - ++current_hour; - } - - matrix_entries.push_back(val); - } - - size_t n = std::round(std::sqrt(matrix_entries.size())); - hcm[current_hour] = Eigen::MatrixXd(n, n); - for (size_t i = 0; i < matrix_entries.size(); i++) { - hcm[current_hour].data()[i] = matrix_entries[i]; - } - - return mio::success(hcm); -} - int main(int argc, char** argv) { - std::string filename = "/home/schm_r6/Documents/24h_networks_csv/office_5_5.csv"; - - auto res = read_hourly_contact_matrix(filename); - - if (!res) { - std::cout << res.error().formatted_message(); - return res.error().code().value(); - } - - std::cout << mio::serialize_json(res.value()).value() << "\n"; - - return 0; - if (argc == 1) { // Print this if no arguments were given std::cout << "This is a small example on how to use the command line interface. " "Use \"-h\" to show the help dialogue.\n"; From 90babe1d1fd01d6255646306fba6c8c9ae63e9e0 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:56:47 +0200 Subject: [PATCH 46/54] make yet another conversion explicit --- cpp/tests/test_abm_person.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 7acbaec222..092bc437d2 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -88,7 +88,7 @@ TEST(TestPerson, quarantine) auto infection_parameters = mio::abm::Parameters(num_age_groups); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); + mio::abm::Location work(mio::abm::LocationType::Work, 1, num_age_groups); //setup rng mock so the person has a state transition to Recovered ScopedMockDistribution>>> mock_uniform_dist; @@ -184,8 +184,8 @@ TEST(TestPerson, getCells) EXPECT_TRUE(mio::abm::migrate(person, location, mio::abm::TransportMode::Unknown, {3, 5})); ASSERT_EQ(person.get_cells().size(), 2); - EXPECT_EQ(person.get_cells()[0], 3); - EXPECT_EQ(person.get_cells()[1], 5); + EXPECT_EQ(person.get_cells()[0], 3u); + EXPECT_EQ(person.get_cells()[1], 5u); } TEST(TestPerson, interact) From 0c783ebb4afba3681a77325845aa612da4e1b172 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 10 Jul 2024 08:00:42 +0200 Subject: [PATCH 47/54] make yet yet another conversion explicit --- cpp/tests/test_abm_world.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index e7ebad2ce2..cf4895a3e0 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -38,8 +38,8 @@ TEST(TestWorld, addLocation) auto work_id = world.add_location(mio::abm::LocationType::Work); auto home_id = world.add_location(mio::abm::LocationType::Home); - ASSERT_EQ(school_id1.get(), 1); - ASSERT_EQ(school_id2.get(), 2); + ASSERT_EQ(school_id1.get(), 1u); + ASSERT_EQ(school_id2.get(), 2u); auto& school1 = world.get_location(school_id1); auto& school2 = world.get_location(school_id2); From e1842dc1e10b66e9c67c790012d43232b5c5a8e8 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:53:26 +0200 Subject: [PATCH 48/54] fix some id issues in abm tests --- cpp/models/abm/model_functions.cpp | 8 +- cpp/tests/test_abm_lockdown_rules.cpp | 10 +-- cpp/tests/test_abm_migration_rules.cpp | 110 +++++++++++-------------- cpp/tests/test_abm_person.cpp | 6 +- 4 files changed, 65 insertions(+), 69 deletions(-) diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index 776955a71d..ae97a2b51f 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -25,6 +25,7 @@ #include "abm/infection.h" #include "abm/virus_variant.h" #include "memilio/epidemiology/age_group.h" +#include "memilio/utils/logging.h" namespace mio { @@ -101,7 +102,10 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExposureRates& local_contact_exposure, const Person& person, const Location& location, const TimePoint t, const TimeSpan dt) { - mio::log_debug("Person with id {} is not at Location with id {}", person.get_id().get(), location.get_id().get()); + if (person.get_location() != location.get_id()) { + mio::log_debug("In add_exposure_contribution: Person {} is not at Location {}", person.get_id().get(), + location.get_id().get()); + } if (person.is_infected(t)) { auto& infection = person.get_infection(); @@ -136,6 +140,8 @@ bool migrate(Person& person, const Location& destination, const TransportMode mo return true; } else { + mio::log_debug("In migrate: Person {} already is at Location {}", person.get_id().get(), + destination.get_id().get()); return false; } } diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index b0ecbf2af3..fff125d1e4 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -31,7 +31,7 @@ TEST(TestLockdownRules, school_closure) auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(6); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location school(mio::abm::LocationType::School, 0, num_age_groups); + mio::abm::Location school(mio::abm::LocationType::School, 1, num_age_groups); //setup rng mock so one person is home schooled and the other goes to school ScopedMockDistribution>>> mock_uniform_dist; @@ -78,7 +78,7 @@ TEST(TestLockdownRules, school_opening) auto t_morning = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(7); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location school(mio::abm::LocationType::School, 0, num_age_groups); + mio::abm::Location school(mio::abm::LocationType::School, 1, num_age_groups); //setup rng mock so the person is homeschooled in case of lockdown ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -114,7 +114,7 @@ TEST(TestLockdownRules, home_office) auto dt = mio::abm::hours(1); mio::abm::Location home(mio::abm::LocationType::Home, 0); - mio::abm::Location work(mio::abm::LocationType::Work, 0); + mio::abm::Location work(mio::abm::LocationType::Work, 1); mio::abm::Parameters params(num_age_groups); // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) @@ -197,7 +197,7 @@ TEST(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, 0, num_age_groups); + mio::abm::Location event(mio::abm::LocationType::SocialEvent, 1, num_age_groups); auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); p.set_assigned_location(home.get_type(), home.get_id()); p.set_assigned_location(event.get_type(), event.get_id()); @@ -218,7 +218,7 @@ TEST(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, 0, num_age_groups); + mio::abm::Location event(mio::abm::LocationType::SocialEvent, 1, num_age_groups); auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_5_to_14); p.set_assigned_location(event.get_type(), event.get_id()); p.set_assigned_location(home.get_type(), home.get_id()); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 287ba73d95..c34e850406 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -18,7 +18,6 @@ * limitations under the License. */ #include "abm/location_type.h" -#include "abm/model_functions.h" #include "abm/migration_rules.h" #include "abm/person.h" #include "abm_helpers.h" @@ -94,9 +93,9 @@ TEST(TestMigrationRules, student_goes_to_school) params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; - ASSERT_EQ(mio::abm::go_to_school(child_rng, p_child, t_morning, dt, params), mio::abm::LocationType::School); - ASSERT_EQ(mio::abm::go_to_school(adult_rng, p_adult, t_morning, dt, params), mio::abm::LocationType::Home); - ASSERT_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_morning, dt, params), mio::abm::LocationType::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(child_rng, p_child, t_weekend, dt, params), mio::abm::LocationType::Home); } TEST(TestMigrationRules, students_go_to_school_in_different_times) @@ -138,21 +137,17 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times) params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; - ASSERT_EQ( + 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); - ASSERT_EQ( + 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); - ASSERT_EQ( + 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); - ASSERT_EQ( + 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); } @@ -198,17 +193,16 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times_with_smaller_t params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; - ASSERT_EQ( + 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); - ASSERT_EQ( + 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); - ASSERT_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, + 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); - ASSERT_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, + 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); } @@ -223,7 +217,7 @@ TEST(TestMigrationRules, school_return) auto t = mio::abm::TimePoint(0) + mio::abm::hours(15); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_school(rng_child, p_child, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_school(rng_child, p_child, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } @@ -262,9 +256,9 @@ TEST(TestMigrationRules, worker_goes_to_work) params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; - ASSERT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, params), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, params), mio::abm::LocationType::Home); - ASSERT_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::LocationType::Home); + 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_night, dt, params), mio::abm::LocationType::Home); } TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) @@ -302,9 +296,9 @@ TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; - ASSERT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, params), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, params), mio::abm::LocationType::Home); - ASSERT_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::LocationType::Home); + 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_night, dt, params), mio::abm::LocationType::Home); } TEST(TestMigrationRules, workers_go_to_work_in_different_times) @@ -343,17 +337,17 @@ TEST(TestMigrationRules, workers_go_to_work_in_different_times) params.get()[age_group_15_to_34] = true; params.get()[age_group_35_to_59] = true; - ASSERT_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), + 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); - ASSERT_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), + 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); - ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_6, p_adult_goes_to_work_at_6, t_night, dt, params), + 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); - ASSERT_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), + 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); - ASSERT_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), + 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); - ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_8, p_adult_goes_to_work_at_8, t_night, dt, params), + 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); } @@ -365,7 +359,7 @@ TEST(TestMigrationRules, work_return) auto rng_adult = mio::abm::PersonalRandomNumberGenerator(rng, p_adult); auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } @@ -383,18 +377,18 @@ TEST(TestMigrationRules, quarantine) auto p_inf1 = make_test_person(work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); auto rng_inf1 = mio::abm::PersonalRandomNumberGenerator(rng, p_inf1); p_inf1.get_tested(rng_inf1, t, test_params); - ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf1, p_inf1, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf1, p_inf1, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); //detected infected person quarantines at home auto p_inf2 = make_test_person(work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); auto rng_inf2 = mio::abm::PersonalRandomNumberGenerator(rng, p_inf2); - ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf2, p_inf2, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf2, p_inf2, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); //undetected infected person does not quaratine auto p_inf3 = make_test_person(hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); auto rng_inf3 = mio::abm::PersonalRandomNumberGenerator(rng, p_inf3); p_inf1.get_tested(rng_inf3, t, test_params); - ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf3, p_inf3, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf3, p_inf3, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Hospital); //detected infected person does not leave hospital to quarantine } @@ -407,12 +401,12 @@ TEST(TestMigrationRules, hospital) auto p_inf = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); auto rng_inf = mio::abm::PersonalRandomNumberGenerator(rng, p_inf); - ASSERT_EQ(mio::abm::go_to_hospital(rng_inf, p_inf, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_hospital(rng_inf, p_inf, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Hospital); auto p_car = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); auto rng_car = mio::abm::PersonalRandomNumberGenerator(rng, p_car); - ASSERT_EQ(mio::abm::go_to_hospital(rng_car, p_car, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_hospital(rng_car, p_car, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } @@ -432,17 +426,17 @@ TEST(TestMigrationRules, go_shopping) auto p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); - ASSERT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Hospital); - ASSERT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_sunday, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_sunday, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_night, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_night, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - ASSERT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_weekday, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_weekday, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::BasicsShop); } @@ -453,15 +447,13 @@ TEST(TestMigrationRules, shop_return) auto t = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(9); auto dt = mio::abm::hours(1); - mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0, num_age_groups); - auto p = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); + auto p = make_test_person(shop, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); - mio::abm::migrate(p, shop); - interact_testing(rng_p, p, shop, {p}, t, dt, params); //person only returns home after some time passed + p.add_time_at_location(dt); - ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); } @@ -471,7 +463,7 @@ TEST(TestMigrationRules, go_event) mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); auto p_work = mio::abm::Person(rng, work.get_type(), work.get_id(), age_group_35_to_59); auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); - mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); + mio::abm::Location home(mio::abm::LocationType::Home, 1, num_age_groups); auto p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); @@ -480,19 +472,19 @@ TEST(TestMigrationRules, go_event) auto t_night = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(1); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_event(rng_work, p_work, t_weekday, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_event(rng_work, p_work, t_weekday, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); - ASSERT_EQ(mio::abm::go_to_event(rng_home, p_home, t_night, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_event(rng_home, p_home, t_night, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - ASSERT_EQ(mio::abm::go_to_event(rng_home, p_home, t_weekday, dt, mio::abm::Parameters(num_age_groups)), + 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_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - ASSERT_EQ(mio::abm::go_to_event(rng_home, p_home, t_saturday, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_event(rng_home, p_home, t_saturday, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::SocialEvent); } @@ -508,11 +500,9 @@ TEST(TestMigrationRules, event_return) auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); - EXPECT_TRUE(mio::abm::migrate(p, social_event)); - interact_testing(rng_p, p, social_event, {p}, t, dt, params); + p.add_time_at_location(dt); - ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, mio::abm::Parameters(num_age_groups)), - mio::abm::LocationType::Home); + EXPECT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, params), mio::abm::LocationType::Home); } TEST(TestMigrationRules, icu) @@ -524,13 +514,13 @@ TEST(TestMigrationRules, icu) auto p_hosp = make_test_person(hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedCritical, t); auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(rng, p_hosp); - ASSERT_EQ(mio::abm::go_to_icu(rng_hosp, p_hosp, t, dt, mio::abm::Parameters(num_age_groups)), + 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::Location work(mio::abm::LocationType::Work, 0, num_age_groups); + mio::abm::Location work(mio::abm::LocationType::Work, 1, num_age_groups); auto p_work = make_test_person(work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); - ASSERT_EQ(mio::abm::go_to_icu(rng_work, p_work, t, dt, mio::abm::Parameters(num_age_groups)), + EXPECT_EQ(mio::abm::go_to_icu(rng_work, p_work, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); } @@ -544,9 +534,9 @@ TEST(TestMigrationRules, recover) auto rng_rec = mio::abm::PersonalRandomNumberGenerator(rng, p_rec); auto p_inf = make_test_person(hospital, age_group_60_to_79, mio::abm::InfectionState::InfectedSevere, t); auto rng_inf = mio::abm::PersonalRandomNumberGenerator(rng, p_inf); - ASSERT_EQ(mio::abm::return_home_when_recovered(rng_rec, p_rec, t, dt, {num_age_groups}), + EXPECT_EQ(mio::abm::return_home_when_recovered(rng_rec, p_rec, t, dt, {num_age_groups}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::return_home_when_recovered(rng_inf, p_inf, t, dt, {num_age_groups}), + EXPECT_EQ(mio::abm::return_home_when_recovered(rng_inf, p_inf, t, dt, {num_age_groups}), mio::abm::LocationType::Hospital); } @@ -560,5 +550,5 @@ TEST(TestMigrationRules, dead) auto p_dead = make_test_person(icu, age_group_60_to_79, mio::abm::InfectionState::Dead, t); auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, p_dead); - ASSERT_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::LocationType::Cemetery); } diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 092bc437d2..78e6074b4c 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -45,9 +45,9 @@ TEST(TestPerson, migrate) auto rng = mio::RandomNumberGenerator(); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 0, 6, 1); - mio::abm::Location loc2(mio::abm::LocationType::School, 1, num_age_groups); - mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 0, 6, 2); + mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 1, 6, 1); + mio::abm::Location loc2(mio::abm::LocationType::School, 2, num_age_groups); + mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 3, 6, 2); auto person = make_test_person(home, age_group_0_to_4, mio::abm::InfectionState::Recovered); mio::abm::migrate(person, loc1, mio::abm::TransportMode::Unknown, {0}); From cc4e9009323aa4521ab4750b8445e8caeb046a16 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:21:46 +0200 Subject: [PATCH 49/54] set correct location in TestMigrationRules.event_return --- cpp/tests/test_abm_migration_rules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index c34e850406..592897359d 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -497,7 +497,7 @@ TEST(TestMigrationRules, event_return) 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); - auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); + auto p = mio::abm::Person(rng, social_event.get_type(), social_event.get_id(), age_group_15_to_34); auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_time_at_location(dt); From 78e192342a70d5bc1ab72ed742ff7127bd0197c3 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 11 Jul 2024 14:23:13 +0200 Subject: [PATCH 50/54] Add or clean up comments, make minor improvements --- cpp/benchmarks/abm.cpp | 2 +- cpp/memilio/utils/random_number_generator.cpp | 2 +- cpp/models/abm/CMakeLists.txt | 1 + cpp/models/abm/location.h | 4 +- cpp/models/abm/location_id.h | 3 +- cpp/models/abm/model_functions.h | 10 ++--- cpp/models/abm/movement_data.h | 1 - cpp/models/abm/person.cpp | 4 -- cpp/models/abm/person.h | 16 ++++--- cpp/models/abm/person_id.h | 2 +- cpp/models/abm/personal_rng.h | 8 ++-- cpp/models/abm/testing_strategy.cpp | 2 +- cpp/models/abm/testing_strategy.h | 27 ++++++------ cpp/models/abm/world.cpp | 7 ++-- cpp/models/abm/world.h | 42 +++++++++---------- cpp/tests/abm_helpers.h | 6 +-- cpp/tests/test_abm_testing_strategy.cpp | 2 +- cpp/tests/test_abm_world.cpp | 2 +- .../memilio/simulation/abm.cpp | 2 +- 19 files changed, 71 insertions(+), 72 deletions(-) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 309aad32d0..27c84a65d7 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -114,7 +114,7 @@ mio::abm::Simulation make_simulation(size_t num_persons, std::initializer_list seeds) { diff --git a/cpp/memilio/utils/random_number_generator.cpp b/cpp/memilio/utils/random_number_generator.cpp index 1d97031845..6e71eae776 100644 --- a/cpp/memilio/utils/random_number_generator.cpp +++ b/cpp/memilio/utils/random_number_generator.cpp @@ -21,7 +21,7 @@ namespace mio { - + RandomNumberGenerator& thread_local_rng() { static thread_local auto rng = RandomNumberGenerator(); diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index d99d992fda..d83f510dd9 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(abm location.cpp location.h + location_id.h household.cpp household.h simulation.cpp diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 850dea052a..21c7cc8d6f 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -197,7 +197,7 @@ class Location */ void set_capacity(uint32_t persons, uint32_t volume, uint32_t cell_idx = 0) { - assert(cell_idx < m_cells.size()); + assert(cell_idx < m_cells.size() && "Given cell index is too large."); m_cells[cell_idx].m_capacity.persons = persons; m_cells[cell_idx].m_capacity.volume = volume; } @@ -209,7 +209,7 @@ class Location */ CellCapacity get_capacity(uint32_t cell_idx = 0) const { - assert(cell_idx < m_cells.size()); + assert(cell_idx < m_cells.size() && "Given cell index is too large."); return m_cells[cell_idx].m_capacity; } diff --git a/cpp/models/abm/location_id.h b/cpp/models/abm/location_id.h index e85b565b1a..1914e36e40 100644 --- a/cpp/models/abm/location_id.h +++ b/cpp/models/abm/location_id.h @@ -30,7 +30,8 @@ namespace abm { /// Unique identifier for a Location within a World. -struct LocationId : public mio::TypeSafe, public OperatorComparison { +struct MEMILIO_ENABLE_EBO LocationId : public mio::TypeSafe, + public OperatorComparison { /// @brief Create an ID. LocationId(uint32_t id) : mio::TypeSafe(id) diff --git a/cpp/models/abm/model_functions.h b/cpp/models/abm/model_functions.h index 6e4310c11a..e0bc00a3a2 100644 --- a/cpp/models/abm/model_functions.h +++ b/cpp/models/abm/model_functions.h @@ -33,7 +33,7 @@ namespace abm /** * @brief Compute the number of daily transmissions for contact transmission of a virus in a cell. - * @param[in] rates The local exposure rates. + * @param[in] rates The local exposure rates. * @param[in] cell_index Cell index of the Cell. * @param[in] virus VirusVariant of interest. * @param[in] age_receiver AgeGroup of the receiving Person. @@ -46,16 +46,16 @@ ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, co /** * @brief Compute the number of daily transmissions for aerosol transmission of a virus in a cell. - * @param[in] rates The local exposure rates. + * @param[in] rates The local exposure rates. * @param[in] cell_index Cell index of the Cell. * @param[in] virus VirusVariant of interest. - * @param[in] global_params The parameter set of the World. + * @param[in] global_params The parameter set of the World. * @return Average amount of Infection%s with the virus per day. */ ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, const Parameters& global_params); -/** +/** * @brief Add the contribution of a person to the local exposure rates. * @param[in, out] local_air_exposure Exposure rates by aerosols for the local population. * @param[in, out] local_contact_exposure Exposure by rates contacts for the local population. @@ -67,7 +67,7 @@ ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellI void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExposureRates& local_contact_exposure, const Person& person, const Location& location, const TimePoint t, const TimeSpan dt); -/** +/** * @brief Let a Person interact with the population at its current Location, possibly getting infected. * @param[in, out] rng PersonalRandomNumberGenerator for this Person. * @param[in, out] person The person to interact with the local population. diff --git a/cpp/models/abm/movement_data.h b/cpp/models/abm/movement_data.h index da25a1d831..de8a590a34 100644 --- a/cpp/models/abm/movement_data.h +++ b/cpp/models/abm/movement_data.h @@ -21,7 +21,6 @@ #ifndef ABM_MOVEMENT_DATA_H #define ABM_MOVEMENT_DATA_H -#include "abm/time.h" #include namespace mio diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index ce4bcc96f6..6e5938cff3 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -111,10 +111,6 @@ Infection& Person::get_infection() void Person::set_assigned_location(LocationType type, LocationId id) { - /* TODO: This is not safe if the location is not the same as added in the world, e.g. the index is wrong. We need to check this. - * For now only use it like this: auto home_id = world.add_location(mio::abm::LocationType::Home); - * person.set_assigned_location(home); - */ m_assigned_locations[static_cast(type)] = id; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 478998e141..9736bda675 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -73,7 +73,7 @@ class Person Infection& get_infection(); const Infection& get_infection() const; - /** + /** * @brief Get all Vaccination%s of the Person. * @return A vector with all Vaccination%s. * @{ @@ -163,7 +163,11 @@ class Person } /** - * @brief Set an assigned Location of the Person. + * @brief Set an assigned Location of the Person. + * + * Important: Setting incorrect values will cause issues during simulation. It is preferable to use + * World::assign_location with a valid LocationId, obtained e.g. through World::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] id The LocationId of the Location. @@ -348,7 +352,7 @@ class Person /** * @brief Add a new #Vaccination - * @param[in] v ExposureType (i. e. vaccine) the person takes. + * @param[in] v ExposureType (i. e. vaccine) the person takes. * @param[in] t TimePoint of the Vaccination. */ void add_new_vaccination(ExposureType v, TimePoint t) @@ -384,12 +388,12 @@ class Person } /** - * @brief Get the latest #Infection or #Vaccination and its initial TimePoint of the Person. + * @brief Get the latest #Infection or #Vaccination and its initial TimePoint of the Person. */ std::pair get_latest_protection() const; /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -423,7 +427,7 @@ class Person private: LocationId m_location; ///< Current Location of the Person. LocationType m_location_type; ///< Type of the current Location. - 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 Vaccination%s the Person has received. std::vector m_infections; ///< Vector with all Infection%s the Person had. diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index 5649447ebd..20184b7ff9 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -30,7 +30,7 @@ namespace abm { /// Unique identifier for a Person within a World. -struct PersonId : public mio::TypeSafe, public OperatorComparison { +struct MEMILIO_ENABLE_EBO PersonId : public mio::TypeSafe, public OperatorComparison { /// @brief Create an ID. PersonId(uint32_t id) : mio::TypeSafe(id) diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h index 2a946612fd..654e8dea5a 100644 --- a/cpp/models/abm/personal_rng.h +++ b/cpp/models/abm/personal_rng.h @@ -39,9 +39,9 @@ class Person; * for all persons share the same key. * The counter is taken from the person. * PersonalRandomNumberGenerator is cheap to construct and transparent - * for the compiler to optimize, so we don't store the RNG persistently, only the + * for the compiler to optimize, so we don't store the RNG persistently, only the * counter, so we don't need to store the key in each person. This increases - * consistency (if the key is changed after the person is created) and + * consistency (if the key is changed after the person is created) and * reduces the memory required per person. * @see mio::RandomNumberGeneratorBase */ @@ -52,7 +52,7 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase key, PersonId id, mio::Counter& counter); @@ -60,7 +60,7 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase& location_to_schemes_map) +TestingStrategy::TestingStrategy(const std::vector& location_to_schemes_map) : m_location_to_schemes_map(location_to_schemes_map.begin(), location_to_schemes_map.end()) { } diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 3ae3bf5b4b..ee0c4a3ba0 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -159,16 +159,12 @@ class TestingScheme */ class TestingStrategy { - struct hash { - std::size_t operator()(const std::pair& key) const - { - return std::hash{}(static_cast(key.first)) ^ - std::hash{}(static_cast(key.second)); - } - }; - public: - struct Entry { + /** + * @brief List of testing schemes for a given LocationType and LocationId. + * A LocalStrategy with id of value LocationId::invalid_id() is used for all Locations with LocationType type. + */ + struct LocalStrategy { LocationType type; LocationId id; std::vector schemes; @@ -179,10 +175,12 @@ class TestingStrategy * @param[in] testing_schemes Vector of TestingSchemes that are checked for testing. */ TestingStrategy() = default; - explicit TestingStrategy(const std::vector& location_to_schemes_map); + explicit TestingStrategy(const std::vector& location_to_schemes_map); /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at a certain Location. + * A TestingScheme with loc_id of value LocationId::invalid_id() is used for all Locations of the given type. + * @param[in] loc_type LocationType key for TestingScheme to be remove. * @param[in] loc_id LocationId key for TestingScheme to be added. * @param[in] scheme TestingScheme to be added. */ @@ -190,8 +188,7 @@ class TestingStrategy /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at a certain LocationType. - * A TestingScheme applies to all Location of the same type is store in - * LocationId{INVALID_LOCATION_INDEX, location_type} of m_location_to_schemes_map. + * A TestingScheme with loc_id of value LocationId::invalid_id() is used for all Locations of the given type. * @param[in] loc_type LocationId key for TestingScheme to be added. * @param[in] scheme TestingScheme to be added. */ @@ -202,6 +199,7 @@ class TestingStrategy /** * @brief Remove a TestingScheme from the set of schemes that are checked for testing at a certain Location. + * @param[in] loc_type LocationType key for TestingScheme to be remove. * @param[in] loc_id LocationId key for TestingScheme to be remove. * @param[in] scheme TestingScheme to be removed. */ @@ -209,8 +207,7 @@ class TestingStrategy /** * @brief Remove a TestingScheme from the set of schemes that are checked for testing at a certain Location. - * A TestingScheme applies to all Location of the same type is store in - * LocationId{INVALID_LOCATION_INDEX, location_type} of m_location_to_schemes_map. + * A TestingScheme with loc_id of value LocationId::invalid_id() is used for all Locations of the given type. * @param[in] loc_type LocationType key for TestingScheme to be remove. * @param[in] scheme TestingScheme to be removed. */ @@ -237,7 +234,7 @@ class TestingStrategy bool run_strategy(PersonalRandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t); private: - std::vector m_location_to_schemes_map; ///< Set of schemes that are checked for testing. + std::vector m_location_to_schemes_map; ///< Set of schemes that are checked for testing. }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 5610b35c05..e3654b69b2 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -55,9 +55,10 @@ PersonId World::add_person(const LocationId id, AgeGroup age) PersonId World::add_person(Person&& person) { - assert(person.get_location() != LocationId::invalid_id()); - assert(person.get_location() < LocationId((uint32_t)m_locations.size())); - assert(person.get_age() < (AgeGroup)parameters.get_num_groups()); + assert(person.get_location() != LocationId::invalid_id() && "Added Person's location must be valid."); + assert(person.get_location() < LocationId((uint32_t)m_locations.size()) && + "Added Person's location is not in World."); + assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); PersonId new_id{static_cast(m_persons.size())}; m_persons.emplace_back(person, new_id); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 94b9ed645f..979a58093e 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -105,7 +105,7 @@ class World World& operator=(World&&) = default; /** - * serialize this. + * serialize this. * @see mio::serialize */ template @@ -151,21 +151,21 @@ class World size, locations, trip_list, persons, use_migration_rules); } - /** + /** * @brief Prepare the World for the next Simulation step. * @param[in] t Current time. * @param[in] dt Length of the time step. */ void begin_step(TimePoint t, TimeSpan dt); - /** + /** * @brief Evolve the world one time step. * @param[in] t Current time. * @param[in] dt Length of the time step. */ void evolve(TimePoint t, TimeSpan dt); - /** + /** * @brief Add a Location to the World. * @param[in] type Type of Location to add. * @param[in] num_cells [Default: 1] Number of Cell%s that the Location is divided into. @@ -173,7 +173,7 @@ class World */ LocationId add_location(LocationType type, uint32_t num_cells = 1); - /** + /** * @brief Add a Person to the World. * @param[in] id Index and type of the initial Location of the Person. * @param[in] age AgeGroup of the person. @@ -215,14 +215,14 @@ class World get_person(assignee).set_assigned_location(get_location(location).get_type(), location); } - /** + /** * @brief Get the number of Persons in one #InfectionState at all Location%s. * @param[in] t Specified #TimePoint. * @param[in] s Specified #InfectionState. */ size_t get_subpopulation_combined(TimePoint t, InfectionState s) const; - /** + /** * @brief Get the number of Persons in one #InfectionState at all Location%s of a type. * @param[in] t Specified #TimePoint. * @param[in] s Specified #InfectionState. @@ -238,10 +238,10 @@ class World const TripList& get_trip_list() const; - /** + /** * @brief Decide if migration rules (like go to school/work) are used or not; * The migration rules regarding hospitalization/ICU/quarantine are always used. - * @param[in] param If true uses the migration rules for migration to school/work etc., else only the rules + * @param[in] param If true uses the migration rules for migration to school/work etc., else only the rules * regarding hospitalization/ICU/quarantine. */ void use_migration_rules(bool param); @@ -270,7 +270,7 @@ class World }); } - /** + /** * @brief Get the TestingStrategy. * @return Reference to the list of TestingScheme%s that are checked for testing. */ @@ -278,14 +278,14 @@ class World const TestingStrategy& get_testing_strategy() const; - /** + /** * @brief The simulation parameters of the world. */ Parameters parameters; /** * Get the RandomNumberGenerator used by this world for random events. - * Persons use their own generators with the same key as the global one. + * Persons use their own generators with the same key as the global one. * @return The random number generator. */ RandomNumberGenerator& get_rng() @@ -294,7 +294,7 @@ class World } /** - * @brief Add a TestingScheme to the set of schemes that are checked for testing at all Locations that have + * @brief Add a TestingScheme to the set of schemes that are checked for testing at all Locations that have * the LocationType. * @param[in] loc_type LocationId key for TestingScheme to be added. * @param[in] scheme TestingScheme to be added. @@ -302,7 +302,7 @@ class World void add_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme); /** - * @brief Remove a TestingScheme from the set of schemes that are checked for testing at all Locations that have + * @brief Remove a TestingScheme from the set of schemes that are checked for testing at all Locations that have * the LocationType. * @param[in] loc_type LocationId key for TestingScheme to be added. * @param[in] scheme TestingScheme to be added. @@ -317,13 +317,13 @@ class World */ Person& get_person(PersonId id) { - assert(id.get() < m_persons.size()); + assert(id.get() < m_persons.size() && "Given PersonId is not in this World."); return m_persons[id.get()]; } const Person& get_person(PersonId id) const { - assert(id.get() < m_persons.size()); + assert(id.get() < m_persons.size() && "Given PersonId is not in this World."); return m_persons[id.get()]; } /** @} */ @@ -406,15 +406,15 @@ class World */ const Location& get_location(LocationId id) const { - assert(id != LocationId::invalid_id()); - assert(id < LocationId((uint32_t)m_locations.size())); + assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); + assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this World."); return m_locations[id.get()]; } Location& get_location(LocationId id) { - assert(id != LocationId::invalid_id()); - assert(id < LocationId((uint32_t)m_locations.size())); + assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); + assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this World."); return m_locations[id.get()]; } /** @} */ @@ -456,7 +456,7 @@ class World /// @brief Shape the air and contact exposure cache according to the current Location%s. void build_exposure_caches(); - /** + /** * @brief Store all air/contact exposures for the current simulation step. * @param[in] t Current TimePoint of the simulation. * @param[in] dt The duration of the simulation step. diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index d98490e226..84bb208f8f 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -91,7 +91,7 @@ struct ScopedMockDistribution { /** * @brief Create a Person without a World object. Intended for simple use in tests. -*/ + */ mio::abm::Person make_test_person(mio::abm::Location& location, 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), @@ -99,13 +99,13 @@ mio::abm::Person make_test_person(mio::abm::Location& location, mio::AgeGroup ag /** * @brief Add a Person to the World. Intended for simple use in tests. -*/ + */ mio::abm::PersonId add_test_person(mio::abm::World& world, mio::abm::LocationId loc_id, 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)); -/// @brief mio::abm::interact, but it computes the correct exposures for you +/// @brief Calls mio::abm::interact, but it computes the correct exposures for you. void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, const mio::abm::Location& location, const std::vector& local_population, const mio::abm::TimePoint t, const mio::abm::TimeSpan dt, diff --git a/cpp/tests/test_abm_testing_strategy.cpp b/cpp/tests/test_abm_testing_strategy.cpp index cfb8b98c3b..3c53c7c228 100644 --- a/cpp/tests/test_abm_testing_strategy.cpp +++ b/cpp/tests/test_abm_testing_strategy.cpp @@ -141,7 +141,7 @@ TEST(TestTestingScheme, initAndRunTestingStrategy) .WillOnce(testing::Return(0.5)); mio::abm::TestingStrategy test_strategy = - mio::abm::TestingStrategy(std::vector()); + mio::abm::TestingStrategy(std::vector{}); test_strategy.add_testing_scheme(mio::abm::LocationType::Work, testing_scheme1); test_strategy.add_testing_scheme(mio::abm::LocationType::Work, testing_scheme2); ASSERT_EQ(test_strategy.run_strategy(rng_person1, person1, loc_work, start_date), diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index cf4895a3e0..99a3eed6fc 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -170,7 +170,7 @@ TEST(TestWorld, evolveStateTransition) EXPECT_EQ(p3.get_infection_state(t + dt), mio::abm::InfectionState::InfectedSymptoms); } -TEST(TestWorld, evolveMigration) // TODO: this is not a unit test, this is a unit of a test! +TEST(TestWorld, evolveMigration) { using testing::Return; diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 7cb0f4c919..d875243a1a 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -167,7 +167,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def_readwrite("time", &mio::abm::Vaccination::time); pymio::bind_class(m, "TestingStrategy") - .def(py::init&>()); + .def(py::init&>()); pymio::bind_class(m, "Location") .def_property_readonly("type", &mio::abm::Location::get_type) From 5bf395a33695238eef894659aaf292ce1203efb3 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:07:56 +0200 Subject: [PATCH 51/54] slightly extend TestPerson.migrate --- cpp/tests/test_abm_person.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 78e6074b4c..6cea44e1d4 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -22,6 +22,7 @@ #include "abm/location_type.h" #include "abm/migration_rules.h" #include "abm/person.h" +#include "abm/time.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" @@ -50,22 +51,30 @@ TEST(TestPerson, migrate) mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 3, 6, 2); auto person = make_test_person(home, age_group_0_to_4, mio::abm::InfectionState::Recovered); - mio::abm::migrate(person, loc1, mio::abm::TransportMode::Unknown, {0}); + // check that a person does not move to its current location + person.add_time_at_location(mio::abm::hours(1)); + EXPECT_FALSE(mio::abm::migrate(person, home)); + EXPECT_EQ(person.get_time_at_location(), mio::abm::hours(1)); + EXPECT_EQ(person.get_location(), home.get_id()); + // move the person around a bit + EXPECT_TRUE(mio::abm::migrate(person, loc1, 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::migrate(person, loc2, 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); EXPECT_TRUE(mio::abm::migrate(person, loc3, 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); ASSERT_EQ(person.get_cells().size(), 2); EXPECT_EQ(person.get_cells()[0], 0u); EXPECT_EQ(person.get_cells()[1], 1u); - EXPECT_EQ(person.get_last_transport_mode(), mio::abm::TransportMode::Bike); } TEST(TestPerson, setGetAssignedLocation) @@ -312,4 +321,4 @@ TEST(Person, rng) p_rng(); EXPECT_EQ(p.get_rng_counter(), mio::Counter(1)); EXPECT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{1})); -} \ No newline at end of file +} From 215ebff94636dc23f01e3003ca039c11cd6e622e Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:59:14 +0200 Subject: [PATCH 52/54] review: update documentation --- cpp/models/abm/location.h | 4 ++-- cpp/models/abm/person.h | 1 + cpp/models/abm/world.h | 27 +++++++++++++++++++-------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 21c7cc8d6f..921cca01a0 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -106,7 +106,7 @@ class Location /** * @brief Construct a copy of a Location with a new ID. - * @param[in] other A Location. + * @param[in] other The Location to copy from. * @param[in] id The ID for the new Location. */ explicit Location(const Location& other, LocationId id) @@ -282,7 +282,7 @@ class Location private: LocationType m_type; ///< Type of the Location. - LocationId m_id; ///< Id of the Location. Set by the World owning it. + LocationId m_id; ///< Unique identifier for the Location in the World owning it. LocalInfectionParameters m_parameters; ///< Infection parameters for the Location. std::vector m_cells{}; ///< A vector of all Cell%s that the Location is divided in. MaskType m_required_mask; ///< Least secure type of Mask that is needed to enter the Location. diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 9736bda675..7810eede7f 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -170,6 +170,7 @@ class Person * * 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. * @param[in] id The LocationId of the Location. */ void set_assigned_location(LocationType type, LocationId id); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 979a58093e..853dff4960 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -169,19 +169,23 @@ class World * @brief Add a Location to the World. * @param[in] type Type of Location to add. * @param[in] num_cells [Default: 1] Number of Cell%s that the Location is divided into. - * @return Index and type of the newly created Location. + * @return ID of the newly created Location. */ LocationId add_location(LocationType type, uint32_t num_cells = 1); /** * @brief Add a Person to the World. - * @param[in] id Index and type of the initial Location of the Person. + * @param[in] id The LocationId of the initial Location of the Person. * @param[in] age AgeGroup of the person. - * @return Reference to the newly created Person. + * @return ID of the newly created Person. */ PersonId add_person(const LocationId id, AgeGroup age); - // adds a copy of person to the world + /** + * @brief Adds a copy of a given Person to the World. + * @param[in] person The Person to copy from. + * @return ID of the newly created Person. + */ PersonId add_person(Person&& person); /** @@ -205,14 +209,21 @@ class World /** * @brief Find an assigned Location of a Person. * @param[in] type The #LocationType that specifies the assigned Location. - * @param[in] person The Person. - * @return Reference to the assigned Location. + * @param[in] person PersonId of the Person. + * @return ID of the Location of LocationType type assigend to person. */ LocationId find_location(LocationType type, const PersonId person) const; - void assign_location(PersonId assignee, LocationId location) + /** + * @brief Assign a Location to a Person. + * A Person can have at most one assigned Location of a certain LocationType. + * Assigning another Location of an already assigned LocationType will replace the prior assignment. + * @param[in] person The PersonId of the person this location will be assigned to. + * @param[in] location The LocationId of the Location. + */ + void assign_location(PersonId person, LocationId location) { - get_person(assignee).set_assigned_location(get_location(location).get_type(), location); + get_person(person).set_assigned_location(get_location(location).get_type(), location); } /** From a761592b761df4288edacee0737079732786f118 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:52:53 +0200 Subject: [PATCH 53/54] replace static_cast by the shorter py::overload_cast --- .../memilio/simulation/abm.cpp | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index d875243a1a..f0bddf54b1 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -202,23 +202,13 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_class(m, "World") .def(py::init()) .def("add_location", &mio::abm::World::add_location, py::arg("location_type"), py::arg("num_cells") = 1) - .def("add_person", - static_cast( - &mio::abm::World::add_person), + .def("add_person", py::overload_cast(&mio::abm::World::add_person), py::arg("location_id"), py::arg("age_group")) .def("assign_location", &mio::abm::World::assign_location, py::arg("person_id"), py::arg("location_id")) - .def_property_readonly( - "locations", - static_cast< - mio::Range> ( - mio::abm::World::*)() const>(&mio::abm::World::get_locations), - py::keep_alive<1, 0>{}) //keep this world alive while contents are referenced in ranges - .def_property_readonly( - "persons", - static_cast< - mio::Range> ( - mio::abm::World::*)() const>(&mio::abm::World::get_persons), - py::keep_alive<1, 0>{}) + .def_property_readonly("locations", py::overload_cast<>(&mio::abm::World::get_locations, py::const_), + py::keep_alive<1, 0>{}) //keep this world alive while contents are referenced in ranges + .def_property_readonly("persons", py::overload_cast<>(&mio::abm::World::get_locations, py::const_), + py::keep_alive<1, 0>{}) .def_property( "trip_list", py::overload_cast<>(&mio::abm::World::get_trip_list), [](mio::abm::World& self, const mio::abm::TripList& list) { From e3801245128bf2eaf9b8f1850d54ddc4b31d8f79 Mon Sep 17 00:00:00 2001 From: reneSchm <49305466+reneSchm@users.noreply.github.com> Date: Fri, 19 Jul 2024 08:19:45 +0200 Subject: [PATCH 54/54] fix last commit --- pycode/memilio-simulation/memilio/simulation/abm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index f0bddf54b1..0cbd9f0f8a 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -207,7 +207,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def("assign_location", &mio::abm::World::assign_location, py::arg("person_id"), py::arg("location_id")) .def_property_readonly("locations", py::overload_cast<>(&mio::abm::World::get_locations, py::const_), py::keep_alive<1, 0>{}) //keep this world alive while contents are referenced in ranges - .def_property_readonly("persons", py::overload_cast<>(&mio::abm::World::get_locations, py::const_), + .def_property_readonly("persons", py::overload_cast<>(&mio::abm::World::get_persons, py::const_), py::keep_alive<1, 0>{}) .def_property( "trip_list", py::overload_cast<>(&mio::abm::World::get_trip_list),