From f272d9c59f687b8376107c8c794b4f2866f101cb Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 7 Jun 2024 10:02:15 +0200 Subject: [PATCH 001/111] add ABM simulation node and edge --- cpp/models/abm/graph/mobility.cpp | 6 +++ cpp/models/abm/graph/mobility.h | 71 +++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 cpp/models/abm/graph/mobility.cpp create mode 100644 cpp/models/abm/graph/mobility.h diff --git a/cpp/models/abm/graph/mobility.cpp b/cpp/models/abm/graph/mobility.cpp new file mode 100644 index 0000000000..f5bc41eebc --- /dev/null +++ b/cpp/models/abm/graph/mobility.cpp @@ -0,0 +1,6 @@ +#include "abm/graph/mobility.h" + +namespace mio +{ + +} // namespace mio \ No newline at end of file diff --git a/cpp/models/abm/graph/mobility.h b/cpp/models/abm/graph/mobility.h new file mode 100644 index 0000000000..3b82d30011 --- /dev/null +++ b/cpp/models/abm/graph/mobility.h @@ -0,0 +1,71 @@ +#ifndef ABM_GRAPH_MOBILITY_H +#define ABM_GRAPH_MOBILITY_H + +#include "abm/simulation.h" +#include "abm/time.h" +#include + +namespace mio +{ +/** +* @brief ABM simulation in one node of the abm graph model +*/ +class ABMSimulationNode +{ + +public: + using Sim = mio::abm::Simulation; + + template ::value, void>> + ABMSimulationNode(Args&&... args) + : m_simulation(std::forward(args)...) + { + } + + /** + *@brief get abm simulation in this node. + */ + Sim& get_simulation() + { + return m_simulation; + } + const Sim& get_simulation() const + { + return m_simulation; + } + + /** + * @brief advances the simulation in this node by t+dt and logs information in History object(s) + * @tparam History history object type(s) + * @param[in] t Current time point + * @param[in] dt Time span that shoulb be advanced + * @param[in, out] history History object(s) storing simulation information + */ + template + void evolve(mio::abm::TimePoint t, mio::abm::TimeSpan dt, History&... history) + { + m_simulation.advance(t + dt, history...); + } + +private: + Sim m_simulation; +}; + +class ABMMobilityEdge +{ + +public: + ABMMobilityEdge() + { + } + + void apply_migration(ABMSimulationNode& node_from, ABMSimulationNode& node_to); +}; + +void ABMMobilityEdge::apply_migration(ABMSimulationNode& node_from, ABMSimulationNode& node_to) +{ +} + +} // namespace mio + +#endif // ABM_GRAPH_MOBILITY_H \ No newline at end of file From 1f29e1c9d98c30ab3ed49a655999b7834c8b1911 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 11 Jul 2024 16:01:47 +0200 Subject: [PATCH 002/111] graph abm implementation --- cpp/CMakeLists.txt | 1 + cpp/memilio/mobility/graph_simulation.h | 85 ++++--- cpp/models/abm/graph/mobility.cpp | 6 - cpp/models/abm/graph/mobility.h | 71 ------ cpp/models/abm/location.h | 10 + cpp/models/abm/person.cpp | 2 +- cpp/models/abm/person.h | 4 +- cpp/models/abm/world.cpp | 89 ++++--- cpp/models/abm/world.h | 23 ++ cpp/models/graph_abm/graph_abm_mobility.cpp | 31 +++ cpp/models/graph_abm/graph_abm_mobility.h | 264 ++++++++++++++++++++ cpp/models/graph_abm/mobility_rules.cpp | 40 +++ cpp/models/graph_abm/mobility_rules.h | 43 ++++ cpp/thirdparty/CMakeLists.txt | 35 +-- 14 files changed, 528 insertions(+), 176 deletions(-) delete mode 100644 cpp/models/abm/graph/mobility.cpp delete mode 100644 cpp/models/abm/graph/mobility.h create mode 100644 cpp/models/graph_abm/graph_abm_mobility.cpp create mode 100644 cpp/models/graph_abm/graph_abm_mobility.h create mode 100644 cpp/models/graph_abm/mobility_rules.cpp create mode 100644 cpp/models/graph_abm/mobility_rules.h diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 405b7cd17d..31f23b169c 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -130,6 +130,7 @@ if(MEMILIO_BUILD_MODELS) add_subdirectory(models/ode_sir) add_subdirectory(models/sde_sir) add_subdirectory(models/sde_sirs) + add_subdirectory(models/graph_abm) endif() if(MEMILIO_BUILD_EXAMPLES) diff --git a/cpp/memilio/mobility/graph_simulation.h b/cpp/memilio/mobility/graph_simulation.h index 003ae08793..4931662282 100644 --- a/cpp/memilio/mobility/graph_simulation.h +++ b/cpp/memilio/mobility/graph_simulation.h @@ -29,16 +29,14 @@ namespace mio /** * @brief abstract simulation on a graph with alternating node and edge actions */ -template > +template class GraphSimulationBase { public: - using node_function = std::function; - + using node_function = node_f; using edge_function = edge_f; - GraphSimulationBase(double t0, double dt, const Graph& g, const node_function& node_func, + GraphSimulationBase(Timepoint t0, Timespan dt, const Graph& g, const node_function& node_func, const edge_function&& edge_func) : m_t(t0) , m_dt(dt) @@ -48,7 +46,7 @@ class GraphSimulationBase { } - GraphSimulationBase(double t0, double dt, Graph&& g, const node_function& node_func, + GraphSimulationBase(Timepoint t0, Timespan dt, Graph&& g, const node_function& node_func, const edge_function&& edge_func) : m_t(t0) , m_dt(dt) @@ -58,28 +56,7 @@ class GraphSimulationBase { } - void advance(double t_max = 1.0) - { - auto dt = m_dt; - while (m_t < t_max) { - if (m_t + dt > t_max) { - dt = t_max - m_t; - } - - for (auto& n : m_graph.nodes()) { - m_node_func(m_t, dt, n.property); - } - - m_t += dt; - - for (auto& e : m_graph.edges()) { - m_edge_func(m_t, dt, e.property, m_graph.nodes()[e.start_node_idx].property, - m_graph.nodes()[e.end_node_idx].property); - } - } - } - - double get_t() const + Timepoint get_t() const { return m_t; } @@ -100,28 +77,56 @@ class GraphSimulationBase } protected: - double m_t; - double m_dt; + Timepoint m_t; + Timespan m_dt; Graph m_graph; node_function m_node_func; edge_function m_edge_func; }; -template -class GraphSimulation : public GraphSimulationBase +template , + class node_f = std::function> +class GraphSimulation : public GraphSimulationBase { - using GraphSimulationBase::GraphSimulationBase; + using Base = GraphSimulationBase; + using Base::GraphSimulationBase; + +public: + void advance(Timepoint t_max = 1.0) + { + auto dt = Base::m_dt; + while (Base::m_t < t_max) { + if (Base::m_t + dt > t_max) { + dt = t_max - Base::m_t; + } + + for (auto& n : Base::m_graph.nodes()) { + Base::m_node_func(Base::m_t, dt, n.property); + } + + Base::m_t += dt; + + for (auto& e : Base::m_graph.edges()) { + Base::m_edge_func(Base::m_t, dt, e.property, Base::m_graph.nodes()[e.start_node_idx].property, + Base::m_graph.nodes()[e.end_node_idx].property); + } + } + } }; template class GraphSimulationStochastic - : public GraphSimulationBase> + typename Graph::NodeProperty&, typename Graph::NodeProperty&)>, + std::function> { - using Base = - GraphSimulationBase>; + using Base = GraphSimulationBase, + std::function>; using node_function = typename Base::node_function; using edge_function = typename Base::edge_function; @@ -247,8 +252,8 @@ class GraphSimulationStochastic RandomNumberGenerator m_rng; }; -template -auto make_graph_sim(FP t0, FP dt, Graph&& g, NodeF&& node_func, EdgeF&& edge_func) +template +auto make_graph_sim(Timepoint t0, Timespan dt, Graph&& g, NodeF&& node_func, EdgeF&& edge_func) { return GraphSimulation>(t0, dt, std::forward(g), std::forward(node_func), std::forward(edge_func)); diff --git a/cpp/models/abm/graph/mobility.cpp b/cpp/models/abm/graph/mobility.cpp deleted file mode 100644 index f5bc41eebc..0000000000 --- a/cpp/models/abm/graph/mobility.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "abm/graph/mobility.h" - -namespace mio -{ - -} // namespace mio \ No newline at end of file diff --git a/cpp/models/abm/graph/mobility.h b/cpp/models/abm/graph/mobility.h deleted file mode 100644 index 3b82d30011..0000000000 --- a/cpp/models/abm/graph/mobility.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef ABM_GRAPH_MOBILITY_H -#define ABM_GRAPH_MOBILITY_H - -#include "abm/simulation.h" -#include "abm/time.h" -#include - -namespace mio -{ -/** -* @brief ABM simulation in one node of the abm graph model -*/ -class ABMSimulationNode -{ - -public: - using Sim = mio::abm::Simulation; - - template ::value, void>> - ABMSimulationNode(Args&&... args) - : m_simulation(std::forward(args)...) - { - } - - /** - *@brief get abm simulation in this node. - */ - Sim& get_simulation() - { - return m_simulation; - } - const Sim& get_simulation() const - { - return m_simulation; - } - - /** - * @brief advances the simulation in this node by t+dt and logs information in History object(s) - * @tparam History history object type(s) - * @param[in] t Current time point - * @param[in] dt Time span that shoulb be advanced - * @param[in, out] history History object(s) storing simulation information - */ - template - void evolve(mio::abm::TimePoint t, mio::abm::TimeSpan dt, History&... history) - { - m_simulation.advance(t + dt, history...); - } - -private: - Sim m_simulation; -}; - -class ABMMobilityEdge -{ - -public: - ABMMobilityEdge() - { - } - - void apply_migration(ABMSimulationNode& node_from, ABMSimulationNode& node_to); -}; - -void ABMMobilityEdge::apply_migration(ABMSimulationNode& node_from, ABMSimulationNode& node_to) -{ -} - -} // namespace mio - -#endif // ABM_GRAPH_MOBILITY_H \ No newline at end of file diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 1c8f2d027c..f78bd09a23 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -395,6 +395,15 @@ class Location m_geographical_location = location; } + /** + * @brief Get the world id the location is in. Is only relevant for graph ABM. + * @return World id of the location + */ + int get_world_id() const + { + return m_world_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. @@ -406,6 +415,7 @@ class Location 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. GeographicalLocation m_geographical_location; ///< Geographical location (longitude and latitude) of the Location. + int m_world_id = 0; ///< World id the location is in. Only used for ABM graph model or hybrid graph model. }; } // namespace abm diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index e2dcd5f848..32a1509935 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -149,7 +149,7 @@ uint32_t Person::get_assigned_location_index(LocationType type) const bool Person::goes_to_work(TimePoint t, const Parameters& params) const { - return m_random_workgroup < params.get().get_matrix_at(t.days())[0]; + return (m_random_workgroup < params.get().get_matrix_at(t.days())[0] && !m_is_commuter); } TimeSpan Person::get_go_to_work_time(const Parameters& params) const diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index b80430a1b0..4076701d9b 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -329,7 +329,7 @@ class Person { return t < m_quarantine_start + params.get(); } - + /** * @brief Removes the quarantine status of the Person. */ @@ -526,6 +526,8 @@ class Person std::vector m_cells; ///< Vector with all Cell%s the Person visits at its current Location. mio::abm::TransportMode m_last_transport_mode; ///< TransportMode the Person used to get to its current Location. Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator + bool m_is_commuter = + false; ///< Whether the Person commutes i.e. has work in another graph node. Only used for ABM graph model or hybrid graph model. }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index e534b8cec0..82a060ded0 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -48,6 +48,7 @@ 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)); + m_activeness_statuses.push_back(true); auto& person = *m_persons.back(); person.set_assigned_location(m_cemetery_id); get_individualized_location(id).add_person(person); @@ -67,9 +68,11 @@ 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); + if (m_activeness_statuses[i]) { + auto&& person = m_persons[i]; + auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); + person->interact(personal_rng, t, dt, parameters); + } } } @@ -77,47 +80,51 @@ 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 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)) { - if (target_location != current_location && - 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); + if (m_activeness_statuses[i]) { + auto&& person = m_persons[i]; + 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)) { + if (target_location != current_location && + 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); + } + return true; } - return true; } + return false; + }; + + //run migration rules one after the other if the corresponding location type exists + //shortcutting of bool operators ensures the rules stop after the first rule is applied + if (m_use_migration_rules) { + (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || + (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || + (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || + (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || + (has_locations({LocationType::School, LocationType::Home}) && try_migration_rule(&go_to_school)) || + (has_locations({LocationType::Work, LocationType::Home}) && try_migration_rule(&go_to_work)) || + (has_locations({LocationType::BasicsShop, LocationType::Home}) && + try_migration_rule(&go_to_shop)) || + (has_locations({LocationType::SocialEvent, LocationType::Home}) && + try_migration_rule(&go_to_event)) || + (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); + } + else { + //no daily routine migration, just infection related + (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || + (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || + (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || + (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || + (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); } - return false; - }; - - //run migration rules one after the other if the corresponding location type exists - //shortcutting of bool operators ensures the rules stop after the first rule is applied - if (m_use_migration_rules) { - (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || - (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || - (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || - (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || - (has_locations({LocationType::School, LocationType::Home}) && try_migration_rule(&go_to_school)) || - (has_locations({LocationType::Work, LocationType::Home}) && try_migration_rule(&go_to_work)) || - (has_locations({LocationType::BasicsShop, LocationType::Home}) && try_migration_rule(&go_to_shop)) || - (has_locations({LocationType::SocialEvent, LocationType::Home}) && try_migration_rule(&go_to_event)) || - (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); - } - else { - //no daily routine migration, just infection related - (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || - (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || - (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || - (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || - (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); } } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 8aa65ac5de..8819aaa509 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -33,6 +33,7 @@ #include "memilio/utils/stl_util.h" #include +#include #include #include #include @@ -90,6 +91,8 @@ class World LocationId origin_id = {origin_loc.get_index(), origin_loc.get_type()}; m_persons.push_back( std::make_unique(person.copy_person(get_individualized_location(origin_id)))); + // The default value for a person that is added to a world is active + m_activeness_statuses.push_back(true); } } } @@ -287,6 +290,15 @@ class World return m_rng; } + /** + * Get the world id. Is only relevant for graph abm or hybrid model. + * @return The world id + */ + int get_id() const + { + return m_id; + } + /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at all Locations that have * the LocationType. @@ -303,6 +315,15 @@ class World */ void remove_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme); + /** + * @brief Flip activeness status of a person in the world. + * @param[in] person_id Person whose activeness status is fipped. + */ + void change_activeness(uint32_t person_id) + { + m_activeness_statuses[person_id] = !m_activeness_statuses[person_id]; + } + private: /** * @brief Person%s interact at their Location and may become infected. @@ -318,6 +339,7 @@ class World void migration(TimePoint t, TimeSpan dt); std::vector> m_persons; ///< Vector with pointers to every Person. + std::vector m_activeness_statuses; ///< Vector with activeness status for 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. @@ -330,6 +352,7 @@ class World m_migration_rules; ///< Rules that govern the migration between Location%s. LocationId m_cemetery_id; // Central cemetery for all dead persons. RandomNumberGenerator m_rng; ///< Global random number generator + int m_id; ///< World id. Is only used for abm graph model or hybrid model. }; } // namespace abm diff --git a/cpp/models/graph_abm/graph_abm_mobility.cpp b/cpp/models/graph_abm/graph_abm_mobility.cpp new file mode 100644 index 0000000000..7acf3c2b64 --- /dev/null +++ b/cpp/models/graph_abm/graph_abm_mobility.cpp @@ -0,0 +1,31 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "graph_abm/graph_abm_mobility.h" +#include "abm/simulation.h" +#include "abm/world.h" +#include "abm/person.h" +#include "abm/location_type.h" +#include "abm/parameters.h" + +namespace mio +{ + +} // namespace mio \ No newline at end of file diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h new file mode 100644 index 0000000000..c1d6433155 --- /dev/null +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -0,0 +1,264 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef MIO_ABM_GRAPH_MOBILITY_H +#define MIO_ABM_GRAPH_MOBILITY_H + +#include "abm/simulation.h" +#include "abm/time.h" +#include "abm/location_type.h" +#include "abm/parameters.h" +#include "abm/person.h" +#include "memilio/mobility/graph_simulation.h" +#include "memilio/mobility/graph.h" +#include +#include +#include +#include + +namespace mio +{ +/** +* @brief Represents the ABM simulation in one node of the abm graph model. +*/ +template +class ABMSimulationNode +{ + +public: + using Sim = abm::Simulation; + + template ::value, void>> + ABMSimulationNode(Args&&... args) + : m_simulation(std::forward(args)...) + { + } + + /** + *@brief get abm simulation in this node. + */ + Sim& get_simulation() + { + return m_simulation; + } + const Sim& get_simulation() const + { + return m_simulation; + } + + /** + * @brief advances the simulation in this node by t+dt and logs information in History object(s) + * @tparam History history object type(s) + * @param[in] t Current time point + * @param[in] dt Time span that shoulb be advanced + * @param[in, out] history History object(s) storing simulation information + */ + void evolve(mio::abm::TimePoint t, mio::abm::TimeSpan dt) + { + m_simulation.advance(t + dt, m_history); + } + +private: + Sim m_simulation; ///< ABM Simulation of the node + std::tuple m_history; +}; + +/** + * @brief Parameters influencing the mobility between two abm graph nodes. + */ +class ABMMobilityParameters +{ + +public: + using MobilityRulesVec = + std::vector; + + /** + * Constructor for initializing commuting persons + * @param commuting_persons Vector holding commuting persons' ids + */ + ABMMobilityParameters(const std::vector& commuting_persons) + : m_commuting_persons(commuting_persons) + { + } + + /** + * Equality comparison operators + */ + bool operator==(const ABMMobilityParameters& other) const + { + return m_commuting_persons == other.m_commuting_persons; + } + bool operator!=(const ABMMobilityParameters& other) const + { + return m_commuting_persons != other.m_commuting_persons; + } + + /** + * Get/Set the commuting persons vector. + * The vector represents the persons (by their id) that commute from one node to another + * according to mobility rules. + */ + const std::vector& get_commuting_persons() const + { + return m_commuting_persons; + } + + std::vector& get_commuting_persons() + { + return m_commuting_persons; + } + /** + * @param[in] commuting_persons Vector with commuting person ids. + */ + void set_commuting_persons(const std::vector& commuting_persons) + { + m_commuting_persons = commuting_persons; + } + + /** + * Get/Set the mobility rules. + * The rules are applied to the persons in m_commuting_persons every time exchange betwen two nodes is triggered. + */ + const MobilityRulesVec& get_mobility_rules() const + { + return m_mobility_rules; + } + MobilityRulesVec& get_mobility_rules() + { + return m_mobility_rules; + } + /** + * @param[in] mobility_rules Vector with rules for mobility between nodes. + */ + void set_mobility_rules(const MobilityRulesVec& mobility_rules) + { + m_mobility_rules = mobility_rules; + } + +private: + std::vector m_commuting_persons; ///< Person ids that are commuting via an edge + MobilityRulesVec m_mobility_rules; ///< Rules for moving persons from one node to another +}; + +/** + * Represents the mobility between two nodes. + */ +template +class ABMMobilityEdge +{ + +public: + /** + * Creates edge with mobility parameters + * @param params mobility parameters including people commuting via the edge and mobility rules + */ + ABMMobilityEdge(const ABMMobilityParameters& params) + : m_parameters(params) + { + } + + /** + * @brief Exchanges persons via the edge. + * Commuters are given by the ABMMobilityParameters and exchanged via mobility rules also given ABMMobilityParameters. + * @param[in] node_from Commuters home node + * @param[in] node_to Node commuters (temporarily) move to + * @param[in] t Echange time point + */ + void apply_mobility(ABMSimulationNode& node_from, ABMSimulationNode& node_to, + abm::TimePoint t) + { + // iterate over all persons that could commute via the edge + for (auto p : m_parameters.get_commuting_persons()) { + // as all nodes have all person it doesn't matter which node's persons we take here + auto& person = node_from.get_simulation().get_world().get_persons()[p]; + auto& params = node_from.get_simulation().get_world().parameters; + auto& current_location = person.get_location(); + for (auto& rule : m_parameters.get_mobility_rules()) { + auto target_type = rule(person, t, params); + abm::Location& target_location = + node_from.get_simulation().get_world().find_location(target_type, person); + assert((node_from.get_simulation().get_world().get_id() == target_location.get_world_id() || + node_to.get_simulation().get_world().get_id() == target_location.get_world_id()) && + "Wrong graph edge. Target location is no edge node."); + if (target_location != current_location && + target_location.get_number_persons() < target_location.get_capacity().persons) { + person.migrate_to(target_location); + // change activeness status for commuted person + node_to.get_simulation().get_world().change_activeness(p); + node_from.get_simulation().get_world().change_activeness(p); + // only one mobility rule per person can be applied + break; + } + } + } + } + +private: + ABMMobilityParameters m_parameters; ///< Mobility parameters +}; + +/** + * @brief Edge functor for abm graph simulation. + * @see ABMMobilityEdge::apply_mobility + * @param[in] t Time point the functor is applied. + * @param[in] edge ABMMobilityEdge for which the functor is applied. + * @param[in] node_from Edge start node. + * @param[in] node_to Edge end node. + */ +template +void apply_mobility(abm::TimePoint t, abm::TimeSpan /*dt*/, ABMMobilityEdge& edge, + ABMSimulationNode& node_from, ABMSimulationNode& node_to) +{ + edge.apply_mobility(node_from, node_to, t); +} + +/** + * @brief Node functor for abm graph simulation. + * @see ABMSimulationNode::evolve + * @param[in] t Time point the functor is applied. + * @param[in] dt Time interval the node is evolved. + * @param[in] node ABMSimulationNode to which the functor is applied. + */ +template +void evolve_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode& node) +{ + node.evolve(t, dt); +} + +/** + * @brief Creates an abm graph simulation. + * Every dt time step for each edge the persons given in ABMMobilityParameters move from one node to the other + * according to mobility rules also given by ABMMobilityParameters. + * @param[in] t0 Start time point of the simulation. + * @param[in] dt Step between mobility on edges. + * @param[in] graph Graph for simulation. + */ +template +GraphSimulation, ABMMobilityEdge>, abm::TimePoint, abm::TimeSpan> +make_abm_graph_sim(abm::TimePoint t0, abm::TimeSpan dt, + Graph, ABMMobilityEdge>&& graph) +{ + return make_graph_sim(t0, dt, std::move(graph), &evolve_model, &apply_mobility) +} + +} // namespace mio + +#endif // MIO_ABM_GRAPH_MOBILITY_H \ No newline at end of file diff --git a/cpp/models/graph_abm/mobility_rules.cpp b/cpp/models/graph_abm/mobility_rules.cpp new file mode 100644 index 0000000000..31c7ac4a98 --- /dev/null +++ b/cpp/models/graph_abm/mobility_rules.cpp @@ -0,0 +1,40 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "graph_abm/mobility_rules.h" +#include "abm/location.h" + +namespace mio +{ + +abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, const abm::Parameters& params) +{ + abm::LocationType current_loc = person.get_location().get_type(); + + if (current_loc == abm::LocationType::Home && t < params.get() && t.day_of_week() < 5 && + person.goes_to_work(t, params) && !person.is_in_quarantine(t, params)) { + return abm::LocationType::Home; + } + + // agents are sent home or to work every time this function is called i.e. if it is called too often they will be sent to work multiple times + return abm::LocationType::Home; +} + +} // namespace mio \ No newline at end of file diff --git a/cpp/models/graph_abm/mobility_rules.h b/cpp/models/graph_abm/mobility_rules.h new file mode 100644 index 0000000000..68a226ad8d --- /dev/null +++ b/cpp/models/graph_abm/mobility_rules.h @@ -0,0 +1,43 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef MIO_ABM_GRAPH_MOBILITY_RULES_H +#define MIO_ABM_GRAPH_MOBILITY_RULES_H + +#include "abm/location_type.h" +#include "abm/time.h" +#include "abm/parameters.h" +#include "abm/person.h" + +namespace mio +{ + +/** + * @brief Once a day commuters go to work in another node. + * @param[in] person Person the rule is applies to + * @param[in] t Current time point + * @param[in] params Parameters of person's Home world + * @return LocationType the person is going to + */ +abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, const abm::Parameters& params); + +} // namespace mio + +#endif //MIO_ABM_GRAPH_MOBILITY_RULES_H \ No newline at end of file diff --git a/cpp/thirdparty/CMakeLists.txt b/cpp/thirdparty/CMakeLists.txt index d75fd27f33..104760b28f 100644 --- a/cpp/thirdparty/CMakeLists.txt +++ b/cpp/thirdparty/CMakeLists.txt @@ -70,17 +70,17 @@ if(MEMILIO_USE_BUNDLED_BOOST) include(FetchContent) FetchContent_Declare(boost - GIT_REPOSITORY https://github.com/boostorg/boost.git - GIT_TAG boost-${MEMILIO_BOOST_VERSION}) + GIT_REPOSITORY https://github.com/boostorg/boost.git + GIT_TAG boost-${MEMILIO_BOOST_VERSION}) FetchContent_GetProperties(boost) if(NOT boost_POPULATED) FetchContent_Populate(boost) endif() - - add_custom_target(boost-bootstrap ALL - DEPENDS "${boost_SOURCE_DIR}/boost" - ) + + add_custom_target(boost-bootstrap ALL + DEPENDS "${boost_SOURCE_DIR}/boost" + ) if(MSVC) add_custom_command( @@ -89,23 +89,23 @@ if(MEMILIO_USE_BUNDLED_BOOST) COMMAND "b2.exe" headers WORKING_DIRECTORY ${boost_SOURCE_DIR} VERBATIM - ) + ) else() add_custom_command( OUTPUT "${boost_SOURCE_DIR}/boost" COMMAND ./bootstrap.sh - COMMAND ./b2 headers + COMMAND ./b2 headers WORKING_DIRECTORY ${boost_SOURCE_DIR} VERBATIM - ) + ) endif() - + add_library(boost INTERFACE) add_dependencies(boost boost-bootstrap) add_library(Boost::boost ALIAS boost) target_include_directories(boost SYSTEM INTERFACE $) - - if (NOT MSVC) + + if(NOT MSVC) target_compile_options(boost INTERFACE "-Wno-c++20-attribute-extensions") endif() @@ -125,16 +125,19 @@ if(MEMILIO_USE_BUNDLED_BOOST) ${boost_SOURCE_DIR}/libs/filesystem/src/utf8_codecvt_facet.cpp ${boost_SOURCE_DIR}/libs/filesystem/src/windows_file_codecvt.cpp ) + # Ensure that the boost atomic library is used instead of the standard atomic library, where some functionality is only available as of C++20. target_compile_definitions(boost_filesystem PUBLIC BOOST_FILESYSTEM_NO_CXX20_ATOMIC_REF) target_link_libraries(boost_filesystem PUBLIC boost_disable_autolink boost) set_property(TARGET boost_filesystem PROPERTY POSITION_INDEPENDENT_CODE ON) add_library(Boost::filesystem ALIAS boost_filesystem) - if(NOT MSVC) #on gcc and apple clang we need to define BOOST_NO_CXX98_FUNCTION_BASE because a deprecated function is sometimes used in boost + + if(NOT MSVC) # on gcc and apple clang we need to define BOOST_NO_CXX98_FUNCTION_BASE because a deprecated function is sometimes used in boost target_compile_definitions(boost_filesystem PUBLIC BOOST_NO_CXX98_FUNCTION_BASE) endif() + target_compile_options(boost_filesystem PUBLIC /wd4127 /wd4459) set(Boost_LIBRARIES Boost::boost Boost::filesystem) set(Boost_FOUND ON) @@ -196,15 +199,15 @@ else() to the directory containing the jsoncppConfig.cmake file to build with JsonCpp.") endif() -if (MEMILIO_ENABLE_MPI) +if(MEMILIO_ENABLE_MPI) find_package(MPI REQUIRED COMPONENTS CXX) endif() -if (MEMILIO_ENABLE_OPENMP) +if(MEMILIO_ENABLE_OPENMP) find_package(OpenMP REQUIRED COMPONENTS CXX) endif() -#Random123 library for random number generators +# Random123 library for random number generators message(STATUS "Downloading Random123 library") include(FetchContent) From 5e91137d6cde2e09f28f0e970a0710307bddffd8 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 11 Jul 2024 16:04:01 +0200 Subject: [PATCH 003/111] remove warning surpression for boost --- cpp/thirdparty/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/thirdparty/CMakeLists.txt b/cpp/thirdparty/CMakeLists.txt index 104760b28f..d45524af33 100644 --- a/cpp/thirdparty/CMakeLists.txt +++ b/cpp/thirdparty/CMakeLists.txt @@ -137,7 +137,6 @@ if(MEMILIO_USE_BUNDLED_BOOST) target_compile_definitions(boost_filesystem PUBLIC BOOST_NO_CXX98_FUNCTION_BASE) endif() - target_compile_options(boost_filesystem PUBLIC /wd4127 /wd4459) set(Boost_LIBRARIES Boost::boost Boost::filesystem) set(Boost_FOUND ON) From 4368cb512e89167b7cf23176f6e92a76cc1cbf48 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 12 Jul 2024 08:50:03 +0200 Subject: [PATCH 004/111] add CMakeList --- cpp/models/graph_abm/CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 cpp/models/graph_abm/CMakeLists.txt diff --git a/cpp/models/graph_abm/CMakeLists.txt b/cpp/models/graph_abm/CMakeLists.txt new file mode 100644 index 0000000000..780cdac99f --- /dev/null +++ b/cpp/models/graph_abm/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(graph_abm + graph_abm_mobility.cpp + graph_abm_mobility.h + mobility_rules.h + mobility_rules.cpp +) + +target_link_libraries(graph_abm PUBLIC memilio) +target_include_directories(graph_abm PUBLIC + $ + $ +) + +target_compile_options(graph_abm PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) From 81df1a80ea53948222df7f5bc9689a82b483e2e9 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 12 Jul 2024 09:03:25 +0200 Subject: [PATCH 005/111] CMake List Bug fix --- cpp/thirdparty/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/thirdparty/CMakeLists.txt b/cpp/thirdparty/CMakeLists.txt index 13ceab4ca7..d6b8eba801 100644 --- a/cpp/thirdparty/CMakeLists.txt +++ b/cpp/thirdparty/CMakeLists.txt @@ -84,8 +84,10 @@ if(MEMILIO_USE_BUNDLED_BOOST) include(FetchContent) FetchContent_Declare(boost - GIT_REPOSITORY https://github.com/boostorg/boost.git - GIT_TAG boost-${MEMILIO_BOOST_VERSION}) + + # don't use the URL from github, that download isn't complete and requires more setup (subrepositories, bootstrapping) + URL https://archives.boost.io/release/${MEMILIO_BOOST_VERSION}/source/boost_${MEMILIO_BOOST_VERSION_UNDERSC}.tar.gz + ) FetchContent_GetProperties(boost) if(NOT boost_POPULATED) From 5b2c64ab2c7fffee71f7ef6ee53a9d2c9236ead5 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 12 Jul 2024 09:06:44 +0200 Subject: [PATCH 006/111] fix include --- cpp/models/graph_abm/graph_abm_mobility.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index c1d6433155..eef17dcd7f 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -28,10 +28,6 @@ #include "abm/person.h" #include "memilio/mobility/graph_simulation.h" #include "memilio/mobility/graph.h" -#include -#include -#include -#include namespace mio { From 414c3f490d2db2f566a63ae077cc4c627886dec0 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 12 Jul 2024 09:11:20 +0200 Subject: [PATCH 007/111] bug fix --- cpp/models/graph_abm/graph_abm_mobility.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index eef17dcd7f..f79d9c2518 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -252,7 +252,7 @@ GraphSimulation, ABMMobilityEdge make_abm_graph_sim(abm::TimePoint t0, abm::TimeSpan dt, Graph, ABMMobilityEdge>&& graph) { - return make_graph_sim(t0, dt, std::move(graph), &evolve_model, &apply_mobility) + return make_graph_sim(t0, dt, std::move(graph), &evolve_model, &apply_mobility); } } // namespace mio From 4e2a73398fd6b0654aa0803f5c6ddc9b5cba8aaf Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 15 Jul 2024 13:43:39 +0200 Subject: [PATCH 008/111] test --- cpp/models/abm/location.h | 11 ++- cpp/models/abm/world.h | 15 ++- cpp/models/graph_abm/graph_abm_mobility.h | 46 ++++++--- cpp/models/graph_abm/mobility_rules.cpp | 5 +- cpp/tests/CMakeLists.txt | 21 ++-- cpp/tests/test_graph_abm.cpp | 115 ++++++++++++++++++++++ 6 files changed, 185 insertions(+), 28 deletions(-) create mode 100644 cpp/tests/test_graph_abm.cpp diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index f78bd09a23..19a3c4c182 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -396,7 +396,7 @@ class Location } /** - * @brief Get the world id the location is in. Is only relevant for graph ABM. + * @brief Get the world id the location is in. Is only relevant for graph ABM or hybrid model. * @return World id of the location */ int get_world_id() const @@ -404,6 +404,15 @@ class Location return m_world_id; } + /** + * @brief Set world id of the Location. Is only relevant for graph ABM or hybrid model. + * @param[in] world_id The world id of the location. + */ + void set_world_id(int world_id) + { + m_world_id = world_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/world.h b/cpp/models/abm/world.h index 8819aaa509..2a97a0a724 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -59,11 +59,12 @@ class World * @brief Create a World. * @param[in] num_agegroups The number of AgeGroup%s in the simulated World. Must be less than MAX_NUM_AGE_GROUPS. */ - World(size_t num_agegroups) + World(size_t num_agegroups, int id = 0) : parameters(num_agegroups) , m_trip_list() , m_use_migration_rules(true) , m_cemetery_id(add_location(LocationType::Cemetery)) + , m_id(id) { assert(num_agegroups < MAX_NUM_AGE_GROUPS && "MAX_NUM_AGE_GROUPS exceeded."); } @@ -72,12 +73,13 @@ class World * @brief Create a copied World. * @param[in] other The World that needs to be copied. */ - World(const World& other) + World(const World& other, int id = 0) : parameters(other.parameters) , m_persons() , m_locations() , m_trip_list(other.m_trip_list) , m_cemetery_id(add_location(LocationType::Cemetery)) + , m_id(id) { for (auto& origin_loc : other.get_locations()) { if (origin_loc.get_type() != LocationType::Cemetery) { @@ -299,6 +301,15 @@ class World return m_id; } + /** + * Get activeness status of all persons in the world. + * @return Activeness vector + */ + std::vector& get_activeness_statuses() + { + return m_activeness_statuses; + } + /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at all Locations that have * the LocationType. diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index f79d9c2518..61a14d2ce8 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -28,6 +28,8 @@ #include "abm/person.h" #include "memilio/mobility/graph_simulation.h" #include "memilio/mobility/graph.h" +#include +#include namespace mio { @@ -42,8 +44,9 @@ class ABMSimulationNode using Sim = abm::Simulation; template ::value, void>> - ABMSimulationNode(Args&&... args) - : m_simulation(std::forward(args)...) + ABMSimulationNode(std::tuple history, Args&&... args) + : m_history(history) + , m_simulation(std::forward(args)...) { } @@ -68,7 +71,7 @@ class ABMSimulationNode */ void evolve(mio::abm::TimePoint t, mio::abm::TimeSpan dt) { - m_simulation.advance(t + dt, m_history); + m_simulation.advance(t + dt, std::get<0>(m_history)); } private: @@ -83,15 +86,16 @@ class ABMMobilityParameters { public: - using MobilityRulesVec = - std::vector; + using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); /** * Constructor for initializing commuting persons * @param commuting_persons Vector holding commuting persons' ids */ - ABMMobilityParameters(const std::vector& commuting_persons) + ABMMobilityParameters(const std::vector& commuting_persons, + const std::vector& mobility_rules) : m_commuting_persons(commuting_persons) + , m_mobility_rules(mobility_rules) { } @@ -130,28 +134,29 @@ class ABMMobilityParameters } /** - * Get/Set the mobility rules. + * Get/ the mobility rules. * The rules are applied to the persons in m_commuting_persons every time exchange betwen two nodes is triggered. */ - const MobilityRulesVec& get_mobility_rules() const + const std::vector& get_mobility_rules() const { return m_mobility_rules; } - MobilityRulesVec& get_mobility_rules() + std::vector& get_mobility_rules() { return m_mobility_rules; } /** - * @param[in] mobility_rules Vector with rules for mobility between nodes. + * @brief Add mobility rule to member vector. + * @param[in] mobility_rule Rule to be added for mobility between nodes. */ - void set_mobility_rules(const MobilityRulesVec& mobility_rules) + void add_mobility_rule(const MobilityRuleType& mobility_rule) { - m_mobility_rules = mobility_rules; + m_mobility_rules.push_back(mobility_rule); } private: std::vector m_commuting_persons; ///< Person ids that are commuting via an edge - MobilityRulesVec m_mobility_rules; ///< Rules for moving persons from one node to another + std::vector m_mobility_rules; ///< Rules for moving persons from one node to another }; /** @@ -160,6 +165,7 @@ class ABMMobilityParameters template class ABMMobilityEdge { + using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); public: /** @@ -171,6 +177,20 @@ class ABMMobilityEdge { } + ABMMobilityEdge(const std::vector& commuting_persons, + const std::vector& mobility_rules = {}) + : m_parameters(commuting_persons, mobility_rules) + { + } + + /** + * @brief Get mobility paramters. + */ + const ABMMobilityParameters& get_parameters() const + { + return m_parameters; + } + /** * @brief Exchanges persons via the edge. * Commuters are given by the ABMMobilityParameters and exchanged via mobility rules also given ABMMobilityParameters. diff --git a/cpp/models/graph_abm/mobility_rules.cpp b/cpp/models/graph_abm/mobility_rules.cpp index 31c7ac4a98..797e6e08cd 100644 --- a/cpp/models/graph_abm/mobility_rules.cpp +++ b/cpp/models/graph_abm/mobility_rules.cpp @@ -28,8 +28,9 @@ abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, c { abm::LocationType current_loc = person.get_location().get_type(); - if (current_loc == abm::LocationType::Home && t < params.get() && t.day_of_week() < 5 && - person.goes_to_work(t, params) && !person.is_in_quarantine(t, params)) { + if (current_loc == abm::LocationType::Home && params.get()[person.get_age()] && + t < params.get() && t.day_of_week() < 5 && person.goes_to_work(t, params) && + !person.is_in_quarantine(t, params)) { return abm::LocationType::Home; } diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 4e6c389fe8..8d48f72c48 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -72,22 +72,23 @@ set(TESTSOURCES test_lct_secir.cpp test_lct_initializer_flows.cpp test_ad.cpp + test_graph_abm.cpp ) if(MEMILIO_HAS_JSONCPP) -set(TESTSOURCES ${TESTSOURCES} -test_json_serializer.cpp -test_epi_data_io.cpp -test_lct_parameters_io.cpp -test_ide_parameters_io.cpp -) + set(TESTSOURCES ${TESTSOURCES} + test_json_serializer.cpp + test_epi_data_io.cpp + test_lct_parameters_io.cpp + test_ide_parameters_io.cpp + ) endif() if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) -set(TESTSOURCES ${TESTSOURCES} -test_save_parameters.cpp -test_save_results.cpp -) + set(TESTSOURCES ${TESTSOURCES} + test_save_parameters.cpp + test_save_results.cpp + ) endif() add_executable(memilio-test ${TESTSOURCES}) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp new file mode 100644 index 0000000000..3c41c73c2d --- /dev/null +++ b/cpp/tests/test_graph_abm.cpp @@ -0,0 +1,115 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "abm/world.h" +#include "abm/location_type.h" +#include "abm/time.h" +#include "graph_abm/graph_abm_mobility.h" +#include "graph_abm/mobility_rules.h" +#include "memilio/epidemiology/age_group.h" +#include "memilio/io/history.h" +#include +#include +#include +#include + +TEST(TestGraphAbm, test_activessness) +{ + auto world = mio::abm::World(size_t(1)); + world.parameters.get().set_multiple({mio::AgeGroup(0)}, true); + auto work_id = world.add_location(mio::abm::LocationType::Work); + auto home_id = world.add_location(mio::abm::LocationType::Home); + auto& p1 = world.add_person(home_id, mio::AgeGroup(0)); + auto& p2 = world.add_person(home_id, mio::AgeGroup(0)); + p1.set_assigned_location(work_id); + p2.set_assigned_location(work_id); + p1.set_assigned_location(home_id); + p2.set_assigned_location(home_id); + + auto& home = world.get_individualized_location(home_id); + auto& work = world.get_individualized_location(work_id); + + EXPECT_EQ(p1.get_location(), home); + EXPECT_EQ(p2.get_location(), home); + EXPECT_EQ(world.get_activeness_statuses().size(), 2); + + world.change_activeness(p1.get_person_id()); + EXPECT_EQ(world.get_activeness_statuses()[p1.get_person_id()], false); + EXPECT_EQ(world.get_activeness_statuses()[p2.get_person_id()], true); + + auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); + auto dt = mio::abm::hours(3); + + world.evolve(t, dt); + + //inactive persons do not move + EXPECT_EQ(p1.get_location(), home); + EXPECT_EQ(p2.get_location(), work); +} + +struct MockHistory { + + template + void log(const T& t) + { + mio::unused(t); + } +}; + +TEST(TestGraphAbm, test_evolve_node) +{ + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::hours(2); + mio::ABMSimulationNode node(MockHistory{}, t, size_t(1)); + node.evolve(t, dt); + + EXPECT_EQ(node.get_simulation().get_time(), mio::abm::TimePoint(dt.seconds())); +} + +TEST(TestGraphAbm, test_apply_mobility) +{ + auto world_1 = mio::abm::World(size_t(1), 1); + auto work_id_1 = world_1.add_location(mio::abm::LocationType::Work); + world_1.get_individualized_location(work_id_1).set_world_id(world_1.get_id()); + auto work_id_2 = world_1.add_location(mio::abm::LocationType::Work); + world_1.get_individualized_location(work_id_2).set_world_id(2); + auto home_id = world_1.add_location(mio::abm::LocationType::Home); + world_1.get_individualized_location(home_id).set_world_id(world_1.get_id()); + auto& p1 = world_1.add_person(home_id, mio::AgeGroup(0)); + auto& p2 = world_1.add_person(home_id, mio::AgeGroup(0)); + p1.set_assigned_location(work_id_1); + p2.set_assigned_location(work_id_2); + p1.set_assigned_location(home_id); + p2.set_assigned_location(home_id); + auto world_2 = mio::abm::World(world_1, 2); + // Deactivate persons in world 2 + world_2.change_activeness(p1.get_person_id()); + world_2.change_activeness(p2.get_person_id()); + + auto t0 = mio::abm::TimePoint(0) + mio::abm::hours(6); + // mio::ABMSimulationNode node1(MockHistory{}, t0, std::move(world_1)); + // mio::ABMSimulationNode node2(MockHistory{}, t0, std::move(world_2)); + //const auto&& a = mio::apply_commuting; + auto a = mio::apply_commuting(p1, t0, world_1.parameters); + mio::unused(a); + //auto b = decltype(a); + + //mio::ABMMobilityEdge edge({p2.get_person_id()}, {*mio::apply_commuting}); +} \ No newline at end of file From 8f1d7cb425f6eb1abd6b5aa3f8e5b9d57613e8d5 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 15 Jul 2024 15:32:41 +0200 Subject: [PATCH 009/111] test apply_mobility --- cpp/models/abm/person.cpp | 2 +- cpp/models/abm/person.h | 2 -- cpp/models/graph_abm/CMakeLists.txt | 2 +- cpp/models/graph_abm/graph_abm_mobility.h | 3 +-- cpp/models/graph_abm/mobility_rules.cpp | 3 +-- cpp/tests/CMakeLists.txt | 2 +- cpp/tests/test_graph_abm.cpp | 27 ++++++++++++----------- 7 files changed, 19 insertions(+), 22 deletions(-) diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 32a1509935..e2dcd5f848 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -149,7 +149,7 @@ uint32_t Person::get_assigned_location_index(LocationType type) const bool Person::goes_to_work(TimePoint t, const Parameters& params) const { - return (m_random_workgroup < params.get().get_matrix_at(t.days())[0] && !m_is_commuter); + return m_random_workgroup < params.get().get_matrix_at(t.days())[0]; } TimeSpan Person::get_go_to_work_time(const Parameters& params) const diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 4076701d9b..c65664dc73 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -526,8 +526,6 @@ class Person std::vector m_cells; ///< Vector with all Cell%s the Person visits at its current Location. mio::abm::TransportMode m_last_transport_mode; ///< TransportMode the Person used to get to its current Location. Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator - bool m_is_commuter = - false; ///< Whether the Person commutes i.e. has work in another graph node. Only used for ABM graph model or hybrid graph model. }; } // namespace abm diff --git a/cpp/models/graph_abm/CMakeLists.txt b/cpp/models/graph_abm/CMakeLists.txt index 780cdac99f..866bc85b29 100644 --- a/cpp/models/graph_abm/CMakeLists.txt +++ b/cpp/models/graph_abm/CMakeLists.txt @@ -1,8 +1,8 @@ add_library(graph_abm graph_abm_mobility.cpp graph_abm_mobility.h - mobility_rules.h mobility_rules.cpp + mobility_rules.h ) target_link_libraries(graph_abm PUBLIC memilio) diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 61a14d2ce8..2593b5b56d 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -45,8 +45,7 @@ class ABMSimulationNode template ::value, void>> ABMSimulationNode(std::tuple history, Args&&... args) - : m_history(history) - , m_simulation(std::forward(args)...) + : m_simulation(std::forward(args)...), m_history(history) { } diff --git a/cpp/models/graph_abm/mobility_rules.cpp b/cpp/models/graph_abm/mobility_rules.cpp index 797e6e08cd..0d7c7df3da 100644 --- a/cpp/models/graph_abm/mobility_rules.cpp +++ b/cpp/models/graph_abm/mobility_rules.cpp @@ -31,11 +31,10 @@ abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, c if (current_loc == abm::LocationType::Home && params.get()[person.get_age()] && t < params.get() && t.day_of_week() < 5 && person.goes_to_work(t, params) && !person.is_in_quarantine(t, params)) { - return abm::LocationType::Home; + return abm::LocationType::Work; } // agents are sent home or to work every time this function is called i.e. if it is called too often they will be sent to work multiple times return abm::LocationType::Home; } - } // namespace mio \ No newline at end of file diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 8d48f72c48..4620468e6d 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -93,7 +93,7 @@ endif() add_executable(memilio-test ${TESTSOURCES}) target_include_directories(memilio-test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(memilio-test PRIVATE memilio ode_secir ode_seir ode_secirvvs ode_seair ide_seir ide_secir lct_secir abm gtest_main AD::AD) +target_link_libraries(memilio-test PRIVATE memilio ode_secir ode_seir ode_secirvvs ode_seair ide_seir ide_secir lct_secir abm gtest_main AD::AD graph_abm) target_compile_options(memilio-test PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) # make unit tests find the test data files diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 3c41c73c2d..aa8e45ac97 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -24,11 +24,7 @@ #include "graph_abm/graph_abm_mobility.h" #include "graph_abm/mobility_rules.h" #include "memilio/epidemiology/age_group.h" -#include "memilio/io/history.h" -#include -#include #include -#include TEST(TestGraphAbm, test_activessness) { @@ -86,10 +82,12 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { auto world_1 = mio::abm::World(size_t(1), 1); + world_1.parameters.get().set_multiple({mio::AgeGroup(0)}, true); auto work_id_1 = world_1.add_location(mio::abm::LocationType::Work); world_1.get_individualized_location(work_id_1).set_world_id(world_1.get_id()); auto work_id_2 = world_1.add_location(mio::abm::LocationType::Work); - world_1.get_individualized_location(work_id_2).set_world_id(2); + auto& work_2 = world_1.get_individualized_location(work_id_2); + work_2.set_world_id(2); auto home_id = world_1.add_location(mio::abm::LocationType::Home); world_1.get_individualized_location(home_id).set_world_id(world_1.get_id()); auto& p1 = world_1.add_person(home_id, mio::AgeGroup(0)); @@ -104,12 +102,15 @@ TEST(TestGraphAbm, test_apply_mobility) world_2.change_activeness(p2.get_person_id()); auto t0 = mio::abm::TimePoint(0) + mio::abm::hours(6); - // mio::ABMSimulationNode node1(MockHistory{}, t0, std::move(world_1)); - // mio::ABMSimulationNode node2(MockHistory{}, t0, std::move(world_2)); - //const auto&& a = mio::apply_commuting; - auto a = mio::apply_commuting(p1, t0, world_1.parameters); - mio::unused(a); - //auto b = decltype(a); - - //mio::ABMMobilityEdge edge({p2.get_person_id()}, {*mio::apply_commuting}); + mio::ABMSimulationNode node1(MockHistory{}, t0, std::move(world_1)); + mio::ABMSimulationNode node2(MockHistory{}, t0, std::move(world_2)); + + mio::ABMMobilityEdge edge({p2.get_person_id()}, {&mio::apply_commuting}); + edge.apply_mobility(node1, node2, t0+mio::abm::hours(2)); + + EXPECT_EQ(work_2.get_number_persons(), 1); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], true); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], false); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], false); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], true); } \ No newline at end of file From 7400802d6515edb7c5f80511e7cabbdb9e31a271 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 16 Jul 2024 08:23:48 +0200 Subject: [PATCH 010/111] extend apply_mobility test --- cpp/models/graph_abm/graph_abm_mobility.h | 3 ++- cpp/tests/test_graph_abm.cpp | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 2593b5b56d..4191a6aee5 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -45,7 +45,8 @@ class ABMSimulationNode template ::value, void>> ABMSimulationNode(std::tuple history, Args&&... args) - : m_simulation(std::forward(args)...), m_history(history) + : m_simulation(std::forward(args)...) + , m_history(history) { } diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index aa8e45ac97..0a9a254a6f 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -81,12 +81,12 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { - auto world_1 = mio::abm::World(size_t(1), 1); + auto world_1 = mio::abm::World(size_t(1), 1); world_1.parameters.get().set_multiple({mio::AgeGroup(0)}, true); auto work_id_1 = world_1.add_location(mio::abm::LocationType::Work); world_1.get_individualized_location(work_id_1).set_world_id(world_1.get_id()); auto work_id_2 = world_1.add_location(mio::abm::LocationType::Work); - auto& work_2 = world_1.get_individualized_location(work_id_2); + auto& work_2 = world_1.get_individualized_location(work_id_2); work_2.set_world_id(2); auto home_id = world_1.add_location(mio::abm::LocationType::Home); world_1.get_individualized_location(home_id).set_world_id(world_1.get_id()); @@ -101,16 +101,25 @@ TEST(TestGraphAbm, test_apply_mobility) world_2.change_activeness(p1.get_person_id()); world_2.change_activeness(p2.get_person_id()); - auto t0 = mio::abm::TimePoint(0) + mio::abm::hours(6); - mio::ABMSimulationNode node1(MockHistory{}, t0, std::move(world_1)); - mio::ABMSimulationNode node2(MockHistory{}, t0, std::move(world_2)); + auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); + mio::ABMSimulationNode node1(MockHistory{}, t, std::move(world_1)); + mio::ABMSimulationNode node2(MockHistory{}, t, std::move(world_2)); mio::ABMMobilityEdge edge({p2.get_person_id()}, {&mio::apply_commuting}); - edge.apply_mobility(node1, node2, t0+mio::abm::hours(2)); + edge.apply_mobility(node1, node2, t); EXPECT_EQ(work_2.get_number_persons(), 1); EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], true); EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], false); EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], false); EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], true); + + //return home + t += mio::abm::hours(12); + edge.apply_mobility(node1, node2, t); + EXPECT_EQ(work_2.get_number_persons(), 0); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], true); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], true); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], false); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], false); } \ No newline at end of file From b31471551b7c5698c04929f0819b95f277cb219f Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 16 Jul 2024 16:01:16 +0200 Subject: [PATCH 011/111] error in abm_minimal example --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 81be666e75..0c063cf567 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -54,7 +54,7 @@ int main() // For more than 1 family households we need families. These are parents and children and randoms (which are distributed like the data we have for these households). auto child = mio::abm::HouseholdMember(num_age_groups); // A child is 50/50% 0-4 or 5-14. child.set_age_weight(age_group_0_to_4, 1); - child.set_age_weight(age_group_0_to_4, 1); + child.set_age_weight(age_group_5_to_14, 1); auto parent = mio::abm::HouseholdMember(num_age_groups); // A parent is 50/50% 15-34 or 35-59. parent.set_age_weight(age_group_15_to_34, 1); From dbbd06864c718984847031e09c110069f26b5dab Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 17 Jul 2024 15:13:39 +0200 Subject: [PATCH 012/111] test for apply mobility --- cpp/models/abm/location_type.h | 5 ++-- cpp/models/abm/person.cpp | 12 ++++++-- cpp/models/abm/person.h | 20 +++++++++++++ cpp/models/abm/world.cpp | 15 +++++++++- cpp/models/abm/world.h | 34 +++++++++++++++++++++++ cpp/models/graph_abm/graph_abm_mobility.h | 7 +++-- cpp/tests/test_graph_abm.cpp | 29 ++++++++++--------- 7 files changed, 102 insertions(+), 20 deletions(-) diff --git a/cpp/models/abm/location_type.h b/cpp/models/abm/location_type.h index 9b685a9a7a..80a2979e0e 100644 --- a/cpp/models/abm/location_type.h +++ b/cpp/models/abm/location_type.h @@ -58,15 +58,16 @@ static constexpr uint32_t INVALID_LOCATION_INDEX = std::numeric_limits struct LocationId { uint32_t index; LocationType type; + int world_id = 0; bool operator==(const LocationId& rhs) const { - return (index == rhs.index && type == rhs.type); + return (index == rhs.index && type == rhs.type && world_id == rhs.world_id); } bool operator!=(const LocationId& rhs) const { - return !(index == rhs.index && type == rhs.type); + return !(index == rhs.index && type == rhs.type && world_id == rhs.world_id); } bool operator<(const LocationId& rhs) const diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index e2dcd5f848..e202826576 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -45,6 +45,7 @@ Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age , m_person_id(person_id) , m_cells{0} , m_last_transport_mode(TransportMode::Unknown) + , m_assigned_location_world_ids((int)LocationType::Count) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); @@ -134,12 +135,14 @@ void Person::set_assigned_location(Location& location) * 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[(uint32_t)location.get_type()] = location.get_index(); + m_assigned_location_world_ids[(uint32_t)location.get_type()] = location.get_world_id(); } void Person::set_assigned_location(LocationId id) { - m_assigned_locations[(uint32_t)id.type] = id.index; + m_assigned_locations[(uint32_t)id.type] = id.index; + m_assigned_location_world_ids[(uint32_t)id.type] = id.world_id; } uint32_t Person::get_assigned_location_index(LocationType type) const @@ -147,6 +150,11 @@ uint32_t Person::get_assigned_location_index(LocationType type) const return m_assigned_locations[(uint32_t)type]; } +int Person::get_assigned_location_world_id(LocationType type) const +{ + return m_assigned_location_world_ids[(uint32_t)type]; +} + bool Person::goes_to_work(TimePoint t, const Parameters& params) const { return m_random_workgroup < params.get().get_matrix_at(t.days())[0]; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index c65664dc73..06f29d59e9 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -32,6 +32,7 @@ #include "memilio/utils/memory.h" #include "abm/movement_data.h" #include +#include namespace mio { @@ -281,6 +282,23 @@ class Person return m_assigned_locations; } + /** + * @brief Returns the world id of an assigned location of the Person. + * Assume that a Person has at most one assigned Location of a certain #LocationType. + * @param[in] type #LocationType of the assigned Location. + * @return The world id of the assigned Location. + */ + int get_assigned_location_world_id(LocationType type) const; + + /** + * @brief Get the assigned locations' world ids of the Person. + * @return A vector with the world ids of the assigned locations of the Person + */ + const std::vector& get_assigned_location_world_ids() const + { + return m_assigned_location_world_ids; + } + /** * @brief Draw if the Person goes to work or is in home office during lockdown at a specific TimePoint. * Every Person has a random number. Depending on this number and the time, the Person works from home in case of a @@ -526,6 +544,8 @@ class Person std::vector m_cells; ///< Vector with all Cell%s the Person visits at its current Location. mio::abm::TransportMode m_last_transport_mode; ///< TransportMode the Person used to get to its current Location. Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator + std::vector + m_assigned_location_world_ids; ///< Vector with world ids of the assigned locations. Only used in graph abm. }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 82a060ded0..0b3509fecd 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -37,8 +37,9 @@ 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()), type, m_id}; m_locations.emplace_back(std::make_unique(id, parameters.get_num_groups(), num_cells)); + m_locations[id.index]->set_world_id(m_id); m_has_locations[size_t(type)] = true; return id; } @@ -55,6 +56,18 @@ Person& World::add_person(const LocationId id, AgeGroup age) return person; } +Person& World::add_external_person(Location& loc, 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, loc, age, person_id)); + m_activeness_statuses.push_back(false); + auto& person = *m_persons.back(); + person.set_assigned_location(m_cemetery_id); + loc.add_person(person); + return person; +} + void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 2a97a0a724..4b47686599 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -33,6 +33,7 @@ #include "memilio/utils/stl_util.h" #include +#include #include #include #include @@ -183,6 +184,15 @@ class World */ Person& add_person(const LocationId id, AgeGroup age); + /** + * @brief Add an external Person i.e. a Person whoseHome location is in another World to the World. + * Only used for abm graph model. + * @param[in] loc Initial Location of the Person + * @param[in] age AgeGroup of the Person + * @return Reference to the newly created Person + */ + Person& add_external_person(Location& loc, AgeGroup age); + /** * @brief Get a range of all Location%s in the World. * @return A range of all Location%s. @@ -335,6 +345,30 @@ class World m_activeness_statuses[person_id] = !m_activeness_statuses[person_id]; } + /** + * @brief Copy the persons from another World to this World. + * @param[in] other The World the Person%s are copied from. + */ + void copy_persons_from_other_world(const World& other) + { + for (auto& person : other.get_persons()) { + auto new_person = Person(person.copy_person(person.get_location())); + //copy assigned locations + for (auto type = 0; type < person.get_assigned_locations().size(); ++type) { + auto index = person.get_assigned_location_index(LocationType(type)); + auto world_id = person.get_assigned_location_world_id(LocationType(type)); + new_person.set_assigned_location(LocationId{index, LocationType(type), world_id}); + } + m_persons.push_back(std::make_unique(new_person)); + if (person.get_location().get_world_id() == m_id) { + m_activeness_statuses.push_back(true); + } + else { + m_activeness_statuses.push_back(false); + } + } + } + private: /** * @brief Person%s interact at their Location and may become infected. diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 4191a6aee5..64dac753af 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -208,9 +208,12 @@ class ABMMobilityEdge auto& params = node_from.get_simulation().get_world().parameters; auto& current_location = person.get_location(); for (auto& rule : m_parameters.get_mobility_rules()) { - auto target_type = rule(person, t, params); + auto target_type = rule(person, t, params); + auto target_world_id = person.get_assigned_location_world_id(target_type); abm::Location& target_location = - node_from.get_simulation().get_world().find_location(target_type, person); + (target_world_id == node_from.get_simulation().get_world().get_id()) + ? node_from.get_simulation().get_world().find_location(target_type, person) + : node_to.get_simulation().get_world().find_location(target_type, person); assert((node_from.get_simulation().get_world().get_id() == target_location.get_world_id() || node_to.get_simulation().get_world().get_id() == target_location.get_world_id()) && "Wrong graph edge. Target location is no edge node."); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 0a9a254a6f..70cbe350ab 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -24,6 +24,7 @@ #include "graph_abm/graph_abm_mobility.h" #include "graph_abm/mobility_rules.h" #include "memilio/epidemiology/age_group.h" +#include #include TEST(TestGraphAbm, test_activessness) @@ -81,27 +82,29 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { - auto world_1 = mio::abm::World(size_t(1), 1); - world_1.parameters.get().set_multiple({mio::AgeGroup(0)}, true); + auto world_1 = mio::abm::World(size_t(1), 1); + auto world_2 = mio::abm::World(size_t(1), 2); + world_1.parameters.get()[mio::AgeGroup(0)] = true; + world_2.parameters.get()[mio::AgeGroup(0)] = true; auto work_id_1 = world_1.add_location(mio::abm::LocationType::Work); - world_1.get_individualized_location(work_id_1).set_world_id(world_1.get_id()); - auto work_id_2 = world_1.add_location(mio::abm::LocationType::Work); - auto& work_2 = world_1.get_individualized_location(work_id_2); - work_2.set_world_id(2); - auto home_id = world_1.add_location(mio::abm::LocationType::Home); - world_1.get_individualized_location(home_id).set_world_id(world_1.get_id()); + auto home_id = world_1.add_location(mio::abm::LocationType::Home); + auto work_id_2 = world_2.add_location(mio::abm::LocationType::Work); + + EXPECT_EQ(work_id_1.world_id, 1); + EXPECT_EQ(work_id_2.world_id, 2); + auto& p1 = world_1.add_person(home_id, mio::AgeGroup(0)); auto& p2 = world_1.add_person(home_id, mio::AgeGroup(0)); p1.set_assigned_location(work_id_1); p2.set_assigned_location(work_id_2); p1.set_assigned_location(home_id); p2.set_assigned_location(home_id); - auto world_2 = mio::abm::World(world_1, 2); - // Deactivate persons in world 2 - world_2.change_activeness(p1.get_person_id()); - world_2.change_activeness(p2.get_person_id()); - auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); + //copy persons to world 2 + world_2.copy_persons_from_other_world(world_1); + + auto& work_2 = world_2.get_individualized_location(work_id_2); + auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); mio::ABMSimulationNode node1(MockHistory{}, t, std::move(world_1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(world_2)); From 196275fb95fdd790aa46412a570e7aaa0316708f Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 18 Jul 2024 09:23:06 +0200 Subject: [PATCH 013/111] finx clang bug --- cpp/models/abm/world.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 4b47686599..cab8262d0d 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -354,7 +354,7 @@ class World for (auto& person : other.get_persons()) { auto new_person = Person(person.copy_person(person.get_location())); //copy assigned locations - for (auto type = 0; type < person.get_assigned_locations().size(); ++type) { + for (size_t type = size_t(0); type < person.get_assigned_locations().size(); ++type) { auto index = person.get_assigned_location_index(LocationType(type)); auto world_id = person.get_assigned_location_world_id(LocationType(type)); new_person.set_assigned_location(LocationId{index, LocationType(type), world_id}); From a5a4e88f411b8583fb40705689d5057e4838eefb Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 18 Jul 2024 10:13:52 +0200 Subject: [PATCH 014/111] abm minimal bug fix --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 0c063cf567..17fd3767bd 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -135,7 +135,7 @@ int main() person.set_assigned_location(hospital); person.set_assigned_location(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) { person.set_assigned_location(school); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { From 8cce3908fec118d570153ef1675d1c730268a603 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 18 Jul 2024 14:21:58 +0200 Subject: [PATCH 015/111] graph_abm example --- cpp/examples/CMakeLists.txt | 10 +- cpp/examples/graph_abm.cpp | 214 ++++++++++++++++++++++++++++++++++++ cpp/models/abm/person.cpp | 5 + cpp/models/abm/person.h | 6 + cpp/models/abm/world.h | 36 ++++-- 5 files changed, 258 insertions(+), 13 deletions(-) create mode 100644 cpp/examples/graph_abm.cpp diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 0d3b703ffe..897625f3f3 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -112,6 +112,10 @@ add_executable(history_example history.cpp) target_link_libraries(history_example PRIVATE memilio) target_compile_options(history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(graph_abm_example graph_abm.cpp) +target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) +target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + if(MEMILIO_HAS_JSONCPP) add_executable(ode_secir_read_graph_example ode_secir_read_graph.cpp) target_link_libraries(ode_secir_read_graph_example PRIVATE memilio ode_secir) @@ -144,7 +148,7 @@ if(MEMILIO_HAS_HDF5) endif() if(MEMILIO_HAS_JSONCPP) - add_executable(ide_initialization_example ide_initialization.cpp) - target_link_libraries(ide_initialization_example PRIVATE memilio ide_secir) - target_compile_options(ide_initialization_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(ide_initialization_example ide_initialization.cpp) + target_link_libraries(ide_initialization_example PRIVATE memilio ide_secir) + target_compile_options(ide_initialization_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp new file mode 100644 index 0000000000..5ded098d16 --- /dev/null +++ b/cpp/examples/graph_abm.cpp @@ -0,0 +1,214 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "abm/abm.h" +#include +#include +#include + +int main() +{ + // This is an example with three age groups representing children, adults and seniors. + size_t num_age_groups = 3; + const auto age_group_children = mio::AgeGroup(0); + const auto age_group_adults = mio::AgeGroup(1); + const auto age_group_seniors = mio::AgeGroup(2); + + auto world1 = mio::abm::World(num_age_groups, 0); + + //Set infection parameters + world1.parameters.get() = 4.; + world1.parameters.get() = 2.; + world1.parameters.get() = 4.; + world1.parameters.get() = 5.; + world1.parameters.get() = 6.; + world1.parameters.get() = 8.; + world1.parameters.get() = 7.; + world1.parameters.get() = 10.; + world1.parameters.get() = 11.; + + //Age group 0 goes to school and age group 1 goes to work + world1.parameters.get()[age_group_children] = true; + world1.parameters.get()[age_group_adults] = true; + + //Household members can be child, parent or senior + auto child = mio::abm::HouseholdMember(num_age_groups); + child.set_age_weight(age_group_children, 1); + auto parent = mio::abm::HouseholdMember(num_age_groups); + parent.set_age_weight(age_group_adults, 1); + auto adult = mio::abm::HouseholdMember(num_age_groups); + adult.set_age_weight(age_group_adults, 1); + adult.set_age_weight(age_group_seniors, 1); + + //Single-Person households + auto single_hh = mio::abm::Household(); + single_hh.add_members(adult, 1); + + //Two-Adult household + auto two_adult_hh = mio::abm::Household(); + two_adult_hh.add_members(adult, 2); + + //Single-Parent household + auto single_parent_hh = mio::abm::Household(); + single_parent_hh.add_members(child, 1); + single_parent_hh.add_members(parent, 1); + + //Family household + auto family_hh = mio::abm::Household(); + family_hh.add_members(child, 1); + family_hh.add_members(parent, 2); + + // Vector holding all persons for the graph simulation. This vector is copied to all worlds at the end. + std::vector persons; + + //Household groups for world 1 + auto single_hh_group_w1 = mio::abm::HouseholdGroup(); + single_hh_group_w1.add_households(single_hh, 5); + auto two_adult_hh_group_w1 = mio::abm::HouseholdGroup(); + two_adult_hh_group_w1.add_households(two_adult_hh, 3); + auto single_parent_hh_group_w1 = mio::abm::HouseholdGroup(); + single_parent_hh_group_w1.add_households(single_parent_hh, 5); + auto family_hh_group_w1 = mio::abm::HouseholdGroup(); + family_hh_group_w1.add_households(family_hh, 10); + add_household_group_to_world(world1, single_hh_group_w1); + add_household_group_to_world(world1, two_adult_hh_group_w1); + add_household_group_to_world(world1, single_hh_group_w1); + add_household_group_to_world(world1, family_hh_group_w1); + + //add persons from world 0 to vector + for (auto& person : world1.get_persons()) { + persons.push_back(person); + } + + auto world2 = mio::abm::World(num_age_groups, 1); + //Household groups for world 2 + auto single_hh_group_w2 = mio::abm::HouseholdGroup(); + single_hh_group_w2.add_households(single_hh, 6); + auto two_adult_hh_group_w2 = mio::abm::HouseholdGroup(); + two_adult_hh_group_w2.add_households(two_adult_hh, 2); + auto single_parent_hh_group_w2 = mio::abm::HouseholdGroup(); + single_parent_hh_group_w2.add_households(single_parent_hh, 10); + auto family_hh_group_w2 = mio::abm::HouseholdGroup(); + family_hh_group_w2.add_households(family_hh, 11); + add_household_group_to_world(world2, single_hh_group_w2); + add_household_group_to_world(world2, two_adult_hh_group_w2); + add_household_group_to_world(world2, single_hh_group_w2); + add_household_group_to_world(world2, family_hh_group_w2); + + //add persons from world 1 to vector + for (auto& person : world2.get_persons()) { + persons.push_back(person); + } + + //Create locations for both worlds + //world 0 + auto event_w1 = world1.add_location(mio::abm::LocationType::SocialEvent); + world1.get_individualized_location(event_w1).get_infection_parameters().set(10); + auto hospital_w1 = world1.add_location(mio::abm::LocationType::Hospital); + world1.get_individualized_location(hospital_w1).get_infection_parameters().set(10); + auto icu_w1 = world1.add_location(mio::abm::LocationType::ICU); + world1.get_individualized_location(icu_w1).get_infection_parameters().set(5); + auto shop_w1 = world1.add_location(mio::abm::LocationType::BasicsShop); + world1.get_individualized_location(shop_w1).get_infection_parameters().set(20); + auto school_w1 = world1.add_location(mio::abm::LocationType::School); + world1.get_individualized_location(school_w1).get_infection_parameters().set(20); + auto work_w1 = world1.add_location(mio::abm::LocationType::Work); + world1.get_individualized_location(work_w1).get_infection_parameters().set(10); + //World 1 + auto event_w2 = world2.add_location(mio::abm::LocationType::SocialEvent); + world2.get_individualized_location(event_w2).get_infection_parameters().set(10); + auto hospital_w2 = world2.add_location(mio::abm::LocationType::Hospital); + world2.get_individualized_location(hospital_w2).get_infection_parameters().set(10); + auto icu_w2 = world2.add_location(mio::abm::LocationType::ICU); + world2.get_individualized_location(icu_w2).get_infection_parameters().set(5); + auto shop_w2 = world2.add_location(mio::abm::LocationType::BasicsShop); + world2.get_individualized_location(shop_w2).get_infection_parameters().set(20); + auto school_w2 = world2.add_location(mio::abm::LocationType::School); + world2.get_individualized_location(school_w2).get_infection_parameters().set(20); + auto work_w2 = world2.add_location(mio::abm::LocationType::Work); + world2.get_individualized_location(work_w2).get_infection_parameters().set(10); + + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); + std::vector params_e1; + std::vector params_e2; + + //Assign infection states and locations + std::vector infection_distribution{0.5, 0.3, 0.05, 0.05, 0.05, 0.05, 0.0, 0.0}; + for (auto& person : 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(mio::thread_local_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) { + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + world1.parameters, start_date, infection_state)); + } + if (person.get_assigned_location_world_id(mio::abm::LocationType::Home) == world1.get_id()) { + person.set_assigned_location(event_w1); + person.set_assigned_location(shop_w1); + person.set_assigned_location(hospital_w1); + person.set_assigned_location(icu_w1); + if (person.get_age() == age_group_children) { + person.set_assigned_location(school_w1); + } + if (person.get_age() == age_group_adults) { + //10% of adults in world 0 work in world 1 + size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), + std::vector{90, 10}); + if (work_world == 1) { //person works in other world + person.set_assigned_location(work_w2); + //add person to edge parameters + params_e1.push_back(person.get_person_id()); + } + else { //person works in same world + person.set_assigned_location(work_w1); + } + } + } + else { + person.set_assigned_location(event_w2); + person.set_assigned_location(shop_w2); + person.set_assigned_location(hospital_w2); + person.set_assigned_location(icu_w2); + if (person.get_age() == age_group_children) { + person.set_assigned_location(school_w2); + } + if (person.get_age() == age_group_adults) { + //20% of adults in world 1 work in world 0 + size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), + std::vector{20, 80}); + if (work_world == 0) { //person works in other world + person.set_assigned_location(work_w1); + //add person to edge parameters + params_e2.push_back(person.get_person_id()); + } + else { //person works in same world + person.set_assigned_location(work_w2); + } + } + } + } + + //copy persons to both worlds + world1.set_persons(persons); + world2.set_persons(persons); + + return 0; +} \ No newline at end of file diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index e202826576..d558aabf47 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -222,6 +222,11 @@ uint32_t Person::get_person_id() return m_person_id; } +void Person::set_person_id(uint32_t id) +{ + m_person_id = id; +} + std::vector& Person::get_cells() { return m_cells; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 06f29d59e9..5e63c6ba37 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -371,6 +371,12 @@ class Person */ uint32_t get_person_id(); + /** + * @brief Set the PersonID of the Person. + * The PersonID should correspond to the index in m_persons in world. + */ + void set_person_id(uint32_t id); + /** * @brief Get index of Cell%s of the Person. * @return A vector of all Cell indices the Person visits at the current Location. diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index cab8262d0d..a140c10849 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -346,21 +346,37 @@ class World } /** - * @brief Copy the persons from another World to this World. + * @brief Copy the persons from another World to this World. + * If the persons are at a location in this world they are activated, otherwise they are deactivated. * @param[in] other The World the Person%s are copied from. */ void copy_persons_from_other_world(const World& other) { - for (auto& person : other.get_persons()) { - auto new_person = Person(person.copy_person(person.get_location())); - //copy assigned locations - for (size_t type = size_t(0); type < person.get_assigned_locations().size(); ++type) { - auto index = person.get_assigned_location_index(LocationType(type)); - auto world_id = person.get_assigned_location_world_id(LocationType(type)); - new_person.set_assigned_location(LocationId{index, LocationType(type), world_id}); + for (auto& p : other.get_persons()) { + p.set_person_id(static_cast(m_persons.size())); + m_persons.push_back(std::make_unique(p.copy_person(p.get_location()))); + if (p.get_location().get_world_id() == m_id) { + m_activeness_statuses.push_back(true); } - m_persons.push_back(std::make_unique(new_person)); - if (person.get_location().get_world_id() == m_id) { + else { + m_activeness_statuses.push_back(false); + } + } + } + + /** + * @brief Set the Person%s of the World. + * @param[in] persons The Person%s of the World. + */ + void set_persons(std::vector& persons) + { + //first clear old person vector and corresponding activeness vector + m_persons.clear(); + m_activeness_statuses.clear(); + for (auto& p : persons) { + p.set_person_id(static_cast(m_persons.size())); + m_persons.emplace_back(std::make_unique(p.copy_person(p.get_location()))); + if (p.get_location().get_world_id() == m_id) { m_activeness_statuses.push_back(true); } else { From 2fc9026d95b3ee2f76a15ea49dd506ada6e61688 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 19 Jul 2024 15:22:46 +0200 Subject: [PATCH 016/111] graph abm example --- cpp/examples/graph_abm.cpp | 53 ++++++++++++++++++- cpp/memilio/mobility/graph_simulation.h | 12 ++--- .../metapopulation_mobility_instant.h | 8 ++- cpp/models/abm/world.cpp | 3 +- cpp/models/abm/world.h | 6 +++ cpp/models/graph_abm/graph_abm_mobility.h | 7 ++- 6 files changed, 76 insertions(+), 13 deletions(-) diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 5ded098d16..f26d2fd23d 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -19,10 +19,47 @@ */ #include "abm/abm.h" +#include "abm/infection_state.h" +#include "abm/location_type.h" +#include "abm/time.h" +#include "graph_abm/graph_abm_mobility.h" +#include "graph_abm/mobility_rules.h" +#include "memilio/io/history.h" +#include "memilio/mobility/graph.h" #include #include +#include +#include +#include #include +//Logger +struct Logger : mio::LogAlways { + /** + * A vector of tuples with the Location information i.e. each tuple contains the following information: + * - The LocationId (including the world id) + * - The total number of Persons at the location + * - A map containing the number of Persons per InfectionState at the location + */ + using Type = std::vector>>; + static Type log(const mio::abm::Simulation& sim) + { + Type location_information{}; + std::map persons_per_infection_state; + auto t = sim.get_time(); + for (auto&& loc : sim.get_world().get_locations()) { + for (size_t i = 0; i < static_cast(mio::abm::InfectionState::Count); ++i) { + auto inf_state = mio::abm::InfectionState(i); + persons_per_infection_state.insert({inf_state, loc.get_subpopulation(t, inf_state)}); + } + location_information.push_back( + std::make_tuple(mio::abm::LocationId{loc.get_index(), loc.get_type(), loc.get_world_id()}, + loc.get_number_persons(), persons_per_infection_state)); + } + return location_information; + } +}; + int main() { // This is an example with three age groups representing children, adults and seniors. @@ -171,7 +208,7 @@ int main() if (person.get_age() == age_group_adults) { //10% of adults in world 0 work in world 1 size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), - std::vector{90, 10}); + std::vector{0.9, 0.1}); if (work_world == 1) { //person works in other world person.set_assigned_location(work_w2); //add person to edge parameters @@ -193,7 +230,7 @@ int main() if (person.get_age() == age_group_adults) { //20% of adults in world 1 work in world 0 size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), - std::vector{20, 80}); + std::vector{0.2, 0.8}); if (work_world == 0) { //person works in other world person.set_assigned_location(work_w1); //add person to edge parameters @@ -210,5 +247,17 @@ int main() world1.set_persons(persons); world2.set_persons(persons); + using HistoryType = mio::History; + mio::Graph, mio::ABMMobilityEdge> graph; + graph.add_node(world1.get_id(), HistoryType{}, start_date, std::move(world1)); + graph.add_node(world2.get_id(), HistoryType{}, start_date, std::move(world2)); + graph.add_edge(0, 1, params_e1, + std::vector::MobilityRuleType>{&mio::apply_commuting}); + graph.add_edge(1, 0, params_e2, + std::vector::MobilityRuleType>{&mio::apply_commuting}); + + auto sim = mio::make_abm_graph_sim(start_date, mio::abm::hours(12), std::move(graph)); + sim.advance(end_date); + return 0; } \ No newline at end of file diff --git a/cpp/memilio/mobility/graph_simulation.h b/cpp/memilio/mobility/graph_simulation.h index 4931662282..557dc1ffac 100644 --- a/cpp/memilio/mobility/graph_simulation.h +++ b/cpp/memilio/mobility/graph_simulation.h @@ -85,13 +85,13 @@ class GraphSimulationBase }; template , - class node_f = std::function> + class edge_f = void (*)(Timepoint, Timespan, typename Graph::EdgeProperty&, typename Graph::NodeProperty&, + typename Graph::NodeProperty&), + class node_f = void (*)(Timepoint, Timespan, typename Graph::NodeProperty&)> class GraphSimulation : public GraphSimulationBase { + using GraphSimulationBase::GraphSimulationBase; using Base = GraphSimulationBase; - using Base::GraphSimulationBase; public: void advance(Timepoint t_max = 1.0) @@ -255,8 +255,8 @@ class GraphSimulationStochastic template auto make_graph_sim(Timepoint t0, Timespan dt, Graph&& g, NodeF&& node_func, EdgeF&& edge_func) { - return GraphSimulation>(t0, dt, std::forward(g), std::forward(node_func), - std::forward(edge_func)); + return GraphSimulation, Timepoint, Timespan, EdgeF, NodeF>( + t0, dt, std::forward(g), std::forward(node_func), std::forward(edge_func)); } template diff --git a/cpp/memilio/mobility/metapopulation_mobility_instant.h b/cpp/memilio/mobility/metapopulation_mobility_instant.h index 2cea9c59ad..a242e8060e 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_instant.h +++ b/cpp/memilio/mobility/metapopulation_mobility_instant.h @@ -525,7 +525,9 @@ void apply_migration(FP t, FP dt, MigrationEdge& migrationEdge, SimulationNo * @{ */ template -GraphSimulation, MigrationEdge>> +GraphSimulation, MigrationEdge>, FP, FP, + void (*)(double, double, mio::MigrationEdge<>&, mio::SimulationNode&, mio::SimulationNode&), + void (*)(double, double, mio::SimulationNode&)> make_migration_sim(FP t0, FP dt, const Graph, MigrationEdge>& graph) { return make_graph_sim(t0, dt, graph, &evolve_model, @@ -534,7 +536,9 @@ make_migration_sim(FP t0, FP dt, const Graph, MigrationEdge< } template -GraphSimulation, MigrationEdge>> +GraphSimulation, MigrationEdge>, FP, FP, + void (*)(double, double, mio::MigrationEdge<>&, mio::SimulationNode&, mio::SimulationNode&), + void (*)(double, double, mio::SimulationNode&)> make_migration_sim(FP t0, FP dt, Graph, MigrationEdge>&& graph) { return make_graph_sim(t0, dt, std::move(graph), &evolve_model, diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 0b3509fecd..7a4446eab0 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -102,7 +102,8 @@ void World::migration(TimePoint t, TimeSpan dt) 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 (person->get_assigned_location_world_id(target_type) == m_id && + 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) { bool wears_mask = person->apply_mask_intervention(personal_rng, target_location); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index a140c10849..5ce8638cf5 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -370,6 +370,12 @@ class World */ void set_persons(std::vector& persons) { + //first remove all old persons from the locations + for (auto&& person : m_persons) { + if (person->get_location().get_world_id() == m_id) { + person->get_location().remove_person(*person); + } + } //first clear old person vector and corresponding activeness vector m_persons.clear(); m_activeness_statuses.clear(); diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 64dac753af..cf51617d64 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -165,9 +165,9 @@ class ABMMobilityParameters template class ABMMobilityEdge { - using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); public: + using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); /** * Creates edge with mobility parameters * @param params mobility parameters including people commuting via the edge and mobility rules @@ -271,7 +271,10 @@ void evolve_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode -GraphSimulation, ABMMobilityEdge>, abm::TimePoint, abm::TimeSpan> +GraphSimulation, ABMMobilityEdge>, abm::TimePoint, abm::TimeSpan, + void (*)(mio::abm::TimePoint, mio::abm::TimeSpan, mio::ABMMobilityEdge&, + mio::ABMSimulationNode&, mio::ABMSimulationNode&), + void (*)(mio::abm::TimePoint, mio::abm::TimeSpan, mio::ABMSimulationNode&)> make_abm_graph_sim(abm::TimePoint t0, abm::TimeSpan dt, Graph, ABMMobilityEdge>&& graph) { From 0ece6beda507f2ddd7b8a3d3ce2e16d22a372f0b Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 23 Jul 2024 08:32:45 +0200 Subject: [PATCH 017/111] fix merge conflicts --- cpp/examples/CMakeLists.txt | 7 +- cpp/models/abm/model_functions.cpp | 2 +- cpp/models/abm/person.cpp | 25 ++- cpp/models/abm/person.h | 23 ++- cpp/models/abm/world.cpp | 155 +++++++++--------- cpp/models/abm/world.h | 56 ++++--- cpp/models/graph_abm/mobility_rules.cpp | 2 +- cpp/tests/CMakeLists.txt | 3 +- cpp/tests/abm_helpers.cpp | 2 +- cpp/tests/test_abm_infection.cpp | 3 +- cpp/tests/test_abm_location.cpp | 8 +- cpp/tests/test_abm_lockdown_rules.cpp | 48 +++--- cpp/tests/test_abm_masks.cpp | 12 +- cpp/tests/test_abm_migration_rules.cpp | 47 +++--- cpp/tests/test_abm_person.cpp | 25 +-- cpp/tests/test_abm_world.cpp | 72 ++++---- .../memilio/simulation/abm.cpp | 4 +- 17 files changed, 263 insertions(+), 231 deletions(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 897625f3f3..e321c7d966 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -112,10 +112,9 @@ add_executable(history_example history.cpp) target_link_libraries(history_example PRIVATE memilio) target_compile_options(history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -add_executable(graph_abm_example graph_abm.cpp) -target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) -target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - +# add_executable(graph_abm_example graph_abm.cpp) +# target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) +# target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) if(MEMILIO_HAS_JSONCPP) add_executable(ode_secir_read_graph_example ode_secir_read_graph.cpp) target_link_libraries(ode_secir_read_graph_example PRIVATE memilio ode_secir) diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index ae97a2b51f..ecf7790f97 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -133,7 +133,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_type(), destination.get_id()); + person.set_location(destination.get_type(), destination.get_id(), destination.get_world_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 e4fe5de865..24b68c6d55 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -31,10 +31,11 @@ namespace mio namespace abm { -Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, AgeGroup age, - PersonId person_id) +Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, + int location_world_id, AgeGroup age, PersonId person_id) : m_location(location_id) , m_location_type(location_type) + , m_location_world_id(location_world_id) , m_assigned_locations((uint32_t)LocationType::Count, LocationId::invalid_id()) , m_quarantine_start(TimePoint(-(std::numeric_limits::max() / 2))) , m_age(age) @@ -93,11 +94,12 @@ LocationId Person::get_location() const return m_location; } -void Person::set_location(LocationType type, LocationId id) +void Person::set_location(LocationType type, LocationId id, int world_id) { - m_location = id; - m_location_type = type; - m_time_at_location = TimeSpan(0); + m_location = id; + m_location_type = type; + m_location_world_id = world_id; + m_time_at_location = TimeSpan(0); } const Infection& Person::get_infection() const @@ -110,10 +112,10 @@ Infection& Person::get_infection() return m_infections.back(); } -void Person::set_assigned_location(LocationType type, LocationId id) +void Person::set_assigned_location(LocationType type, LocationId id, int world_id) { m_assigned_locations[static_cast(type)] = id; - m_assigned_location_world_ids[static_cast(type)] = location.get_world_id(); + m_assigned_location_world_ids[static_cast(type)] = world_id; } LocationId Person::get_assigned_location(LocationType type) const @@ -123,7 +125,7 @@ LocationId Person::get_assigned_location(LocationType type) const int Person::get_assigned_location_world_id(LocationType type) const { - return m_assigned_locations[static_cast(type)]; + return m_assigned_location_world_ids[static_cast(type)]; } bool Person::goes_to_work(TimePoint t, const Parameters& params) const @@ -193,11 +195,6 @@ PersonId Person::get_id() const return m_person_id; } -void Person::set_person_id(uint32_t id) -{ - m_person_id = id; -} - std::vector& Person::get_cells() { return m_cells; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 86904a6e23..2cec136d55 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -53,8 +53,8 @@ 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_id, AgeGroup age, - PersonId person_id = PersonId::invalid_id()); + explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, + int location_world_id, AgeGroup age, PersonId person_id = PersonId::invalid_id()); explicit Person(const Person& other, PersonId id); @@ -129,11 +129,18 @@ class Person return m_location_type; } + int get_location_world_id() const + { + return m_location_world_id; + } + /** * @brief Change the location of the person. - * @param[in] id The new location. + * @param[in] type The LocationType of the new Location. + * @param[in] id The LocationId of the new Location. + * @param[in] world_id The world id of the new Location. */ - void set_location(LocationType type, LocationId id); + void set_location(LocationType type, LocationId id, int world_id); /** * @brief Get the time the Person has been at its current Location. @@ -172,8 +179,9 @@ class Person * Location of a certain #LocationType. * @param[in] type The LocationType of the Location. * @param[in] id The LocationId of the Location. + * @param[in] world_id The world id of the Location. */ - void set_assigned_location(LocationType type, LocationId id); + void set_assigned_location(LocationType type, LocationId id, int world_id); /** * @brief Returns the index of an assigned Location of the Person. @@ -282,10 +290,10 @@ class Person PersonId get_id() const; /** - * @brief Set the PersonID of the Person. + * @brief Set the PersonId of the Person. * The PersonID should correspond to the index in m_persons in world. */ - void set_person_id(uint32_t id); + void set_id(PersonId id); /** * @brief Get index of Cell%s of the Person. @@ -451,6 +459,7 @@ class Person private: LocationId m_location; ///< Current Location of the Person. LocationType m_location_type; ///< Type of the current Location. + int m_location_world_id; ///< World id of the current Location. Only used for Graph ABM. std::vector m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the 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 a2876896ce..e00776788f 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -39,7 +39,7 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) LocationId id{static_cast(m_locations.size())}; m_locations.emplace_back(type, id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; - m_locations[id.index]->set_world_id(m_id); + m_locations[id.get()].set_world_id(m_id); // mark caches for rebuild m_is_local_population_cache_valid = false; @@ -51,7 +51,7 @@ 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).get_type(), id, age)); + return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age)); } PersonId World::add_person(Person&& person) @@ -65,7 +65,7 @@ PersonId World::add_person(Person&& person) m_persons.emplace_back(person, new_id); m_activeness_statuses.push_back(true); auto& new_person = m_persons.back(); - new_person.set_assigned_location(LocationType::Cemetery, m_cemetery_id); + new_person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); if (m_is_local_population_cache_valid) { ++m_local_population_cache[new_person.get_location().get()]; @@ -73,17 +73,17 @@ PersonId World::add_person(Person&& person) return new_id; } -Person& World::add_external_person(Location& loc, 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, loc, age, person_id)); - m_activeness_statuses.push_back(false); - auto& person = *m_persons.back(); - person.set_assigned_location(m_cemetery_id); - loc.add_person(person); - return person; -} +// Person& World::add_external_person(Location& loc, 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, loc, age, person_id)); +// m_activeness_statuses.push_back(false); +// auto& person = *m_persons.back(); +// person.set_assigned_location(m_cemetery_id); +// loc.add_person(person); +// return person; +// } void World::evolve(TimePoint t, TimeSpan dt) { @@ -99,7 +99,9 @@ void World::interaction(TimePoint t, TimeSpan dt) const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { - interact(person_id, t, dt); + if (m_activeness_statuses[person_id]) { + interact(person_id, t, dt); + } } } @@ -108,74 +110,79 @@ void World::migration(TimePoint t, TimeSpan dt) const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) 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, 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(person_id, target_location.get_id()); + if (m_activeness_statuses[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); + if (person.get_assigned_location_world_id(target_type) == m_id) { + 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(person_id, target_location.get_id()); + } + return true; + } } - return true; } + return false; + }; + + //run migration rules one after the other if the corresponding location type exists + //shortcutting of bool operators ensures the rules stop after the first rule is applied + if (m_use_migration_rules) { + (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || + (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || + (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || + (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || + (has_locations({LocationType::School, LocationType::Home}) && try_migration_rule(&go_to_school)) || + (has_locations({LocationType::Work, LocationType::Home}) && try_migration_rule(&go_to_work)) || + (has_locations({LocationType::BasicsShop, LocationType::Home}) && + try_migration_rule(&go_to_shop)) || + (has_locations({LocationType::SocialEvent, LocationType::Home}) && + try_migration_rule(&go_to_event)) || + (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); + } + else { + //no daily routine migration, just infection related + (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || + (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || + (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || + (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || + (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); } - return false; - }; - - //run migration rules one after the other if the corresponding location type exists - //shortcutting of bool operators ensures the rules stop after the first rule is applied - if (m_use_migration_rules) { - (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || - (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || - (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || - (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || - (has_locations({LocationType::School, LocationType::Home}) && try_migration_rule(&go_to_school)) || - (has_locations({LocationType::Work, LocationType::Home}) && try_migration_rule(&go_to_work)) || - (has_locations({LocationType::BasicsShop, LocationType::Home}) && try_migration_rule(&go_to_shop)) || - (has_locations({LocationType::SocialEvent, LocationType::Home}) && try_migration_rule(&go_to_event)) || - (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); - } - else { - //no daily routine migration, just infection related - (has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) || - (has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) || - (has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) || - (has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) || - (has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine)); } } -} -// check if a person makes a trip -bool weekend = t.is_weekend(); -size_t num_trips = m_trip_list.num_trips(weekend); - -if (num_trips != 0) { - 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 = 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_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_id(), target_location.get_id(), trip.trip_mode); + // check if a person makes a trip + bool weekend = t.is_weekend(); + size_t num_trips = m_trip_list.num_trips(weekend); + + if (num_trips != 0) { + 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 = 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_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_id(), target_location.get_id(), trip.trip_mode); + } } + m_trip_list.increase_index(); } - m_trip_list.increase_index(); } -} -if (((t).days() < std::floor((t + dt).days()))) { - m_trip_list.reset_index(); -} + if (((t).days() < std::floor((t + dt).days()))) { + m_trip_list.reset_index(); + } } void World::build_compute_local_population_cache() const diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 3aff91ef0b..f8b749bc45 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -73,16 +73,17 @@ class World * @brief Create a World. * @param[in] params Initial simulation parameters. */ - World(const Parameters& params) + World(const Parameters& params, int world_id = 0) : parameters(params.get_num_groups()) , m_trip_list() , m_use_migration_rules(true) , m_cemetery_id(add_location(LocationType::Cemetery)) + , m_id(world_id) { parameters = params; } - World(const World& other) + World(const World& other, int world_id = 0) : parameters(other.parameters) , m_local_population_cache() , m_air_exposure_rates_cache() @@ -92,6 +93,7 @@ class World , m_exposure_caches_need_rebuild(true) , m_persons(other.m_persons) , m_locations(other.m_locations) + , m_activeness_statuses(other.m_activeness_statuses) , m_has_locations(other.m_has_locations) , m_testing_strategy(other.m_testing_strategy) , m_trip_list(other.m_trip_list) @@ -99,6 +101,7 @@ class World , m_migration_rules(other.m_migration_rules) , m_cemetery_id(other.m_cemetery_id) , m_rng(other.m_rng) + , m_id(world_id) { } World& operator=(const World&) = default; @@ -189,14 +192,14 @@ class World */ PersonId add_person(Person&& person); - /** - * @brief Add an external Person i.e. a Person whoseHome location is in another World to the World. - * Only used for abm graph model. - * @param[in] loc Initial Location of the Person - * @param[in] age AgeGroup of the Person - * @return Reference to the newly created Person - */ - Person& add_external_person(Location& loc, AgeGroup age); + // /** + // * @brief Add an external Person i.e. a Person whoseHome location is in another World to the World. + // * Only used for abm graph model. + // * @param[in] loc Initial Location of the Person + // * @param[in] age AgeGroup of the Person + // * @return Reference to the newly created Person + // */ + // Person& add_external_person(Location& loc, AgeGroup age); /** * @brief Get a range of all Location%s in the World. @@ -233,7 +236,7 @@ class World */ void assign_location(PersonId person, LocationId location) { - get_person(person).set_assigned_location(get_location(location).get_type(), location); + get_person(person).set_assigned_location(get_location(location).get_type(), location, m_id); } /** @@ -486,15 +489,20 @@ class World /** * @brief Copy the persons from another World to this World. - * If the persons are at a location in this world they are activated, otherwise they are deactivated. + * If the persons are at a location in this world they are activated, otherwise they are deactivated. + * If necessary the person ids are changed such that they correspond to the index in this world's m_persons vector. * @param[in] other The World the Person%s are copied from. */ void copy_persons_from_other_world(const World& other) { for (auto& p : other.get_persons()) { - p.set_person_id(static_cast(m_persons.size())); - m_persons.push_back(std::make_unique(p.copy_person(p.get_location()))); - if (p.get_location().get_world_id() == m_id) { + if (p.get_id() != static_cast(m_persons.size())) { + mio::log_debug("In world.copy_persons_from_other_world: PersonId does not correspond to index in " + "m_persons vector. Person is copied with adapted Id"); + } + PersonId new_id{static_cast(m_persons.size())}; + m_persons.emplace_back(p, new_id); + if (p.get_location_world_id() == m_id) { m_activeness_statuses.push_back(true); } else { @@ -509,19 +517,19 @@ class World */ void set_persons(std::vector& persons) { - //first remove all old persons from the locations - for (auto&& person : m_persons) { - if (person->get_location().get_world_id() == m_id) { - person->get_location().remove_person(*person); - } - } + m_is_local_population_cache_valid = false; + m_are_exposure_caches_valid = false; //first clear old person vector and corresponding activeness vector m_persons.clear(); m_activeness_statuses.clear(); for (auto& p : persons) { - p.set_person_id(static_cast(m_persons.size())); - m_persons.emplace_back(std::make_unique(p.copy_person(p.get_location()))); - if (p.get_location().get_world_id() == m_id) { + if (p.get_id() != static_cast(m_persons.size())) { + mio::log_debug("In world.copy_persons_from_other_world: PersonId does not correspond to index in " + "m_persons vector. Person is copied with adapted Id"); + } + PersonId new_id{static_cast(m_persons.size())}; + m_persons.emplace_back(p, new_id); + if (p.get_location_world_id() == m_id) { m_activeness_statuses.push_back(true); } else { diff --git a/cpp/models/graph_abm/mobility_rules.cpp b/cpp/models/graph_abm/mobility_rules.cpp index 0d7c7df3da..16b536fd93 100644 --- a/cpp/models/graph_abm/mobility_rules.cpp +++ b/cpp/models/graph_abm/mobility_rules.cpp @@ -26,7 +26,7 @@ namespace mio abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, const abm::Parameters& params) { - abm::LocationType current_loc = person.get_location().get_type(); + abm::LocationType current_loc = person.get_location_type(); if (current_loc == abm::LocationType::Home && params.get()[person.get_age()] && t < params.get() && t.day_of_week() < 5 && person.goes_to_work(t, params) && diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 4620468e6d..63994d2f3e 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -72,7 +72,8 @@ set(TESTSOURCES test_lct_secir.cpp test_lct_initializer_flows.cpp test_ad.cpp - test_graph_abm.cpp + + # test_graph_abm.cpp ) if(MEMILIO_HAS_JSONCPP) diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 85f9edf765..58298eb942 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_type(), location.get_id(), age); + mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_world_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 3ee2369000..1121e05b64 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -177,7 +177,8 @@ 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_type(), location.get_id(), age_group_15_to_34); + auto person = + mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_world_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 2e95746ca9..b0618cceb2 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -74,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(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_person(p1).set_assigned_location(mio::abm::LocationType::School, school_id, world.get_id()); + world.get_person(p2).set_assigned_location(mio::abm::LocationType::School, school_id, world.get_id()); + world.get_person(p1).set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + world.get_person(p2).set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); world.get_location(school_id).set_capacity(1, 66); diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index fff125d1e4..2c4836c9f9 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_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()); + auto p1 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + p1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + p1.set_assigned_location(school.get_type(), school.get_id(), school.get_world_id()); + auto p2 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + p2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + p2.set_assigned_location(school.get_type(), school.get_id(), school.get_world_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_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()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + p.set_assigned_location(school.get_type(), school.get_id(), school.get_world_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_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 person1 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + auto person2 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + person1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + person1.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); + person2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + person2.set_assigned_location(work.get_type(), work.get_id(), work.get_world_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_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()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + p.set_assigned_location(work.get_type(), work.get_id(), work.get_world_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, 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()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + p.set_assigned_location(event.get_type(), event.get_id(), event.get_world_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, 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()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + p.set_assigned_location(event.get_type(), event.get_id(), event.get_world_id()); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_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 5266d34f7a..0f707bfc5e 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -65,12 +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_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 + auto susc_person1 = mio::abm::Person(rng, infection_location.get_type(), infection_location.get_id(), + infection_location.get_world_id(), age_group_15_to_34); + auto susc_person2 = mio::abm::Person(rng, infection_location.get_type(), infection_location.get_id(), + infection_location.get_world_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 592897359d..48bdb22ea3 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -28,7 +28,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, default_type, 0, age_group_15_to_34); + auto person = mio::abm::Person(rng, default_type, 0, 0, age_group_15_to_34); auto p_rng = mio::abm::PersonalRandomNumberGenerator(rng, person); auto params = mio::abm::Parameters(num_age_groups); @@ -76,8 +76,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_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 p_child = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_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); @@ -119,9 +119,11 @@ 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_type(), 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(), home.get_world_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_type(), 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(), home.get_world_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); @@ -175,9 +177,11 @@ 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_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_type(), 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(), home.get_world_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_type(), home.get_id(), home.get_world_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); @@ -211,7 +215,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_type(), school.get_id(), age_group_5_to_14); + auto p_child = mio::abm::Person(rng, school.get_type(), school.get_id(), school.get_world_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); @@ -238,9 +242,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_type(), home.get_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_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); @@ -278,9 +282,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_type(), home.get_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_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); @@ -319,9 +323,11 @@ 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_type(), 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(), home.get_world_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_type(), 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(), home.get_world_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); @@ -355,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_type(), work.get_id(), age_group_35_to_59); + auto p_adult = mio::abm::Person(rng, work.get_type(), work.get_id(), work.get_world_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); @@ -423,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_type(), home.get_id(), age_group_60_to_79); + auto p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); EXPECT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), @@ -461,10 +467,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_type(), work.get_id(), age_group_35_to_59); + auto p_work = mio::abm::Person(rng, work.get_type(), work.get_id(), work.get_world_id(), age_group_35_to_59); auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); 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 p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_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); @@ -497,7 +503,8 @@ 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, social_event.get_type(), social_event.get_id(), age_group_15_to_34); + auto p = mio::abm::Person(rng, social_event.get_type(), social_event.get_id(), social_event.get_world_id(), + age_group_15_to_34); auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_time_at_location(dt); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 625c3080fb..6ddffca93e 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -33,8 +33,9 @@ TEST(TestPerson, init) auto rng = mio::RandomNumberGenerator(); 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_type(), location.get_id(), age_group_60_to_79); + auto t = mio::abm::TimePoint(0); + auto person = + mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_world_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()); @@ -81,11 +82,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_type(), location.get_id(), age_group_35_to_59); - person.set_assigned_location(location.get_type(), location.get_id()); + auto person = + mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_world_id(), age_group_35_to_59); + person.set_assigned_location(location.get_type(), location.get_id(), location.get_world_id()); EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(2)); - person.set_assigned_location(mio::abm::LocationType::Work, mio::abm::LocationId(4)); + person.set_assigned_location(mio::abm::LocationType::Work, mio::abm::LocationId(4), 0); EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(4)); } @@ -142,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.get_type(), loc.get_id(), age_group_15_to_34); + auto susceptible = mio::abm::Person(rng, loc.get_type(), loc.get_id(), loc.get_world_id(), age_group_15_to_34); auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); auto pcr_parameters = params.get()[mio::abm::TestType::PCR]; @@ -204,7 +206,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_type(), loc.get_id(), age_group_15_to_34); + auto person = mio::abm::Person(rng, loc.get_type(), loc.get_id(), loc.get_world_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); @@ -287,9 +289,10 @@ TEST(TestPerson, getMaskProtectiveFactor) 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_type(), location.get_id(), age_group_15_to_34); + 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_type(), location.get_id(), location.get_world_id(), age_group_15_to_34); auto prng = mio::abm::PersonalRandomNumberGenerator(rng, person); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); @@ -310,7 +313,7 @@ TEST(TestPerson, getLatestProtection) TEST(Person, rng) { auto rng = mio::RandomNumberGenerator(); - auto p = mio::abm::Person(rng, mio::abm::LocationType::Home, 0, age_group_35_to_59, mio::abm::PersonId(13)); + auto p = mio::abm::Person(rng, mio::abm::LocationType::Home, 0, 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_world.cpp b/cpp/tests/test_abm_world.cpp index 3ab8a7c3e7..51783964c0 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -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(mio::abm::LocationType::Home, home_id); - person.set_assigned_location(mio::abm::LocationType::Work, work_id); - person.set_assigned_location(mio::abm::LocationType::School, school_id); + person.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + person.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + person.set_assigned_location(mio::abm::LocationType::School, school_id, world.get_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); @@ -154,9 +154,9 @@ TEST(TestWorld, evolveStateTransition) auto& p2 = world.get_persons()[1]; auto& p3 = world.get_persons()[2]; - 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); + p1.set_assigned_location(mio::abm::LocationType::School, location1, world.get_id()); + p2.set_assigned_location(mio::abm::LocationType::School, location1, world.get_id()); + p3.set_assigned_location(mio::abm::LocationType::Work, location2, world.get_id()); //setup mock so p2 becomes infected ScopedMockDistribution>>> @@ -213,12 +213,12 @@ TEST(TestWorld, evolveMigration) auto& p1 = world.get_person(pid1); auto& p2 = world.get_person(pid2); - 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); + p1.set_assigned_location(mio::abm::LocationType::School, school_id, world.get_id()); + p2.set_assigned_location(mio::abm::LocationType::School, school_id, world.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); ScopedMockDistribution>>> mock_exponential_dist; @@ -280,19 +280,19 @@ TEST(TestWorld, evolveMigration) auto& p4 = world.get_person(pid4); auto& p5 = world.get_person(pid5); - 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); + p1.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, world.get_id()); + p2.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, world.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p3.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p4.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p3.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, world.get_id()); + p4.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, world.get_id()); + p5.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, world.get_id()); + p5.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + p5.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); mio::abm::TripList& data = world.get_trip_list(); mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id); @@ -387,12 +387,12 @@ TEST(TestWorld, evolveMigration) auto& p_dead = world.get_persons()[0]; auto& p_severe = world.get_persons()[1]; - 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); + p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, world.get_id()); + p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, world.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, world.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_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(); @@ -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(mio::abm::LocationType::Home, home_id); - person.set_assigned_location(mio::abm::LocationType::Work, work_id); + person.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + person.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); auto testing_criteria = mio::abm::TestingCriteria(); testing_criteria.add_infection_state(mio::abm::InfectionState::InfectedSymptoms); @@ -447,10 +447,10 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) const auto start_date = mio::abm::TimePoint(20); const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); const auto probability = 1.0; - const auto test_params_pcr = mio::abm::TestParameters{0.9, 0.99}; + const auto test_params_pcr = mio::abm::TestParameters{0.9, 0.99}; - auto testing_scheme = - mio::abm::TestingScheme(testing_criteria, testing_frequency, start_date, end_date, test_params_pcr, probability); + auto testing_scheme = mio::abm::TestingScheme(testing_criteria, testing_frequency, start_date, end_date, + test_params_pcr, probability); world.get_testing_strategy().add_testing_scheme(mio::abm::LocationType::Work, testing_scheme); ASSERT_EQ(world.get_testing_strategy().run_strategy(rng_person, person, work, current_time), diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 079b5c1a8d..8a9af03883 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -144,8 +144,8 @@ PYBIND11_MODULE(_simulation_abm, m) .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); From b773193555162f8e3582e877197b40fcdbf9ede7 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 23 Jul 2024 13:16:17 +0200 Subject: [PATCH 018/111] fix test --- cpp/models/abm/world.cpp | 4 +- cpp/models/abm/world.h | 15 +++- cpp/models/graph_abm/graph_abm_mobility.h | 40 +++++++--- cpp/tests/CMakeLists.txt | 3 +- cpp/tests/test_graph_abm.cpp | 91 ++++++++++++----------- 5 files changed, 93 insertions(+), 60 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index e00776788f..f41a10a489 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -198,7 +198,9 @@ 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().get()]; + if (m_persons[i].get_location_world_id() == m_id) { + ++m_local_population_cache[m_persons[i].get_location().get()]; + } } // implicit taskloop barrier } // implicit single barrier } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index f8b749bc45..d0c6e183aa 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -480,11 +480,11 @@ class World /** * @brief Flip activeness status of a person in the world. - * @param[in] person_id Person whose activeness status is fipped. + * @param[in] person_id PersonId of Person whose activeness status is fipped. */ - void change_activeness(uint32_t person_id) + void change_activeness(PersonId person_id) { - m_activeness_statuses[person_id] = !m_activeness_statuses[person_id]; + m_activeness_statuses[person_id.get()] = !m_activeness_statuses[person_id.get()]; } /** @@ -538,6 +538,15 @@ class World } } + /** + * @brief Invalidate local population and exposure rate cache. + */ + void invalidate_cache() + { + m_are_exposure_caches_valid = false; + m_is_local_population_cache_valid = false; + } + private: /** * @brief Person%s interact at their Location and may become infected. diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index cf51617d64..6e2229c1f0 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -26,6 +26,8 @@ #include "abm/location_type.h" #include "abm/parameters.h" #include "abm/person.h" +#include "abm/person_id.h" +#include "abm/model_functions.h" #include "memilio/mobility/graph_simulation.h" #include "memilio/mobility/graph.h" #include @@ -203,23 +205,37 @@ class ABMMobilityEdge { // iterate over all persons that could commute via the edge for (auto p : m_parameters.get_commuting_persons()) { + auto& person_n1 = node_from.get_simulation().get_world().get_person(mio::abm::PersonId(p)); + auto& person_n2 = node_to.get_simulation().get_world().get_person(mio::abm::PersonId(p)); + auto& params = node_from.get_simulation().get_world().parameters; // as all nodes have all person it doesn't matter which node's persons we take here - auto& person = node_from.get_simulation().get_world().get_persons()[p]; - auto& params = node_from.get_simulation().get_world().parameters; - auto& current_location = person.get_location(); + auto current_location_type = person_n1.get_location_type(); + auto current_id = person_n1.get_location(); + auto current_world_id = person_n1.get_location_world_id(); for (auto& rule : m_parameters.get_mobility_rules()) { - auto target_type = rule(person, t, params); - auto target_world_id = person.get_assigned_location_world_id(target_type); - abm::Location& target_location = - (target_world_id == node_from.get_simulation().get_world().get_id()) - ? node_from.get_simulation().get_world().find_location(target_type, person) - : node_to.get_simulation().get_world().find_location(target_type, person); + auto target_type = rule(person_n1, t, params); + auto target_world_id = person_n1.get_assigned_location_world_id(target_type); + auto target_id = person_n1.get_assigned_location(target_type); + auto& target_world = (target_world_id == node_from.get_simulation().get_world().get_id()) + ? node_from.get_simulation().get_world() + : node_to.get_simulation().get_world(); + auto& target_location = target_world.get_location(target_id); assert((node_from.get_simulation().get_world().get_id() == target_location.get_world_id() || node_to.get_simulation().get_world().get_id() == target_location.get_world_id()) && "Wrong graph edge. Target location is no edge node."); - if (target_location != current_location && - target_location.get_number_persons() < target_location.get_capacity().persons) { - person.migrate_to(target_location); + if (target_type == current_location_type && + (target_id != current_id || target_world_id != current_world_id)) { + mio::log_error("Person with index {} has two assigned locations of the same type.", + person_n1.get_id().get()); + } + if (target_type != current_location_type && + target_world.get_number_persons(target_id) < target_location.get_capacity().persons) { + //change person's location in all nodes + mio::abm::migrate(person_n1, target_location); + mio::abm::migrate(person_n2, target_location); + // invalidate both worlds' cache + node_to.get_simulation().get_world().invalidate_cache(); + node_from.get_simulation().get_world().invalidate_cache(); // change activeness status for commuted person node_to.get_simulation().get_world().change_activeness(p); node_from.get_simulation().get_world().change_activeness(p); diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 63994d2f3e..4620468e6d 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -72,8 +72,7 @@ set(TESTSOURCES test_lct_secir.cpp test_lct_initializer_flows.cpp test_ad.cpp - - # test_graph_abm.cpp + test_graph_abm.cpp ) if(MEMILIO_HAS_JSONCPP) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 70cbe350ab..8d26e21648 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -29,27 +29,30 @@ TEST(TestGraphAbm, test_activessness) { - auto world = mio::abm::World(size_t(1)); - world.parameters.get().set_multiple({mio::AgeGroup(0)}, true); + auto world = mio::abm::World(size_t(1)); + world.parameters.get()[mio::AgeGroup(0)] = true; auto work_id = world.add_location(mio::abm::LocationType::Work); auto home_id = world.add_location(mio::abm::LocationType::Home); - auto& p1 = world.add_person(home_id, mio::AgeGroup(0)); - auto& p2 = world.add_person(home_id, mio::AgeGroup(0)); - p1.set_assigned_location(work_id); - p2.set_assigned_location(work_id); - p1.set_assigned_location(home_id); - p2.set_assigned_location(home_id); - - auto& home = world.get_individualized_location(home_id); - auto& work = world.get_individualized_location(work_id); - - EXPECT_EQ(p1.get_location(), home); - EXPECT_EQ(p2.get_location(), home); + auto& home = world.get_location(home_id); + auto& work = world.get_location(work_id); + auto p1_id = world.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = world.add_person(home_id, mio::AgeGroup(0)); + auto& p1 = world.get_person(p1_id); + auto& p2 = world.get_person(p2_id); + p1.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); + p2.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); + p1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + p2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + + EXPECT_EQ(p1.get_location(), home_id); + EXPECT_EQ(p2.get_location(), home_id); + EXPECT_EQ(p1.get_location_world_id(), world.get_id()); + EXPECT_EQ(p2.get_location_world_id(), world.get_id()); EXPECT_EQ(world.get_activeness_statuses().size(), 2); - world.change_activeness(p1.get_person_id()); - EXPECT_EQ(world.get_activeness_statuses()[p1.get_person_id()], false); - EXPECT_EQ(world.get_activeness_statuses()[p2.get_person_id()], true); + world.change_activeness(p1.get_id()); + EXPECT_EQ(world.get_activeness_statuses()[p1.get_id().get()], false); + EXPECT_EQ(world.get_activeness_statuses()[p2.get_id().get()], true); auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); auto dt = mio::abm::hours(3); @@ -57,8 +60,8 @@ TEST(TestGraphAbm, test_activessness) world.evolve(t, dt); //inactive persons do not move - EXPECT_EQ(p1.get_location(), home); - EXPECT_EQ(p2.get_location(), work); + EXPECT_EQ(p1.get_location(), home_id); + EXPECT_EQ(p2.get_location(), work_id); } struct MockHistory { @@ -89,40 +92,44 @@ TEST(TestGraphAbm, test_apply_mobility) auto work_id_1 = world_1.add_location(mio::abm::LocationType::Work); auto home_id = world_1.add_location(mio::abm::LocationType::Home); auto work_id_2 = world_2.add_location(mio::abm::LocationType::Work); - - EXPECT_EQ(work_id_1.world_id, 1); - EXPECT_EQ(work_id_2.world_id, 2); - - auto& p1 = world_1.add_person(home_id, mio::AgeGroup(0)); - auto& p2 = world_1.add_person(home_id, mio::AgeGroup(0)); - p1.set_assigned_location(work_id_1); - p2.set_assigned_location(work_id_2); - p1.set_assigned_location(home_id); - p2.set_assigned_location(home_id); + auto& work_1 = world_1.get_location(work_id_1); + auto& work_2 = world_2.get_location(work_id_2); + auto& home = world_1.get_location(home_id); + + EXPECT_EQ(work_1.get_world_id(), 1); + EXPECT_EQ(work_2.get_world_id(), 2); + + auto p1_id = world_1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = world_1.add_person(home_id, mio::AgeGroup(0)); + auto& p1 = world_1.get_person(p1_id); + auto& p2 = world_1.get_person(p2_id); + p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_world_id()); + p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_world_id()); + p1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + p2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); //copy persons to world 2 world_2.copy_persons_from_other_world(world_1); - auto& work_2 = world_2.get_individualized_location(work_id_2); - auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); + auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); mio::ABMSimulationNode node1(MockHistory{}, t, std::move(world_1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(world_2)); - mio::ABMMobilityEdge edge({p2.get_person_id()}, {&mio::apply_commuting}); + mio::ABMMobilityEdge edge({p2.get_id().get()}, {&mio::apply_commuting}); edge.apply_mobility(node1, node2, t); - EXPECT_EQ(work_2.get_number_persons(), 1); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], true); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], false); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], false); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], true); + EXPECT_EQ(node2.get_simulation().get_world().get_number_persons(work_id_2), 1); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], true); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], false); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], false); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], true); //return home t += mio::abm::hours(12); edge.apply_mobility(node1, node2, t); - EXPECT_EQ(work_2.get_number_persons(), 0); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], true); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], true); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_person_id()], false); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_person_id()], false); + EXPECT_EQ(node2.get_simulation().get_world().get_number_persons(work_id_2), 0); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], true); + EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], true); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], false); + EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], false); } \ No newline at end of file From b4d44d56c9e8dc969a230f3c3e2d6238d4319905 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 25 Jul 2024 10:22:55 +0200 Subject: [PATCH 019/111] graph abm example --- cpp/examples/CMakeLists.txt | 7 +- cpp/examples/graph_abm.cpp | 87 +++++++++++++---------- cpp/models/abm/world.cpp | 26 +++---- cpp/models/abm/world.h | 12 ++-- cpp/models/graph_abm/graph_abm_mobility.h | 23 ++++-- cpp/models/graph_abm/mobility_rules.cpp | 25 ++++++- 6 files changed, 115 insertions(+), 65 deletions(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index e321c7d966..897625f3f3 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -112,9 +112,10 @@ add_executable(history_example history.cpp) target_link_libraries(history_example PRIVATE memilio) target_compile_options(history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -# add_executable(graph_abm_example graph_abm.cpp) -# target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) -# target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(graph_abm_example graph_abm.cpp) +target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) +target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + if(MEMILIO_HAS_JSONCPP) add_executable(ode_secir_read_graph_example ode_secir_read_graph.cpp) target_link_libraries(ode_secir_read_graph_example PRIVATE memilio ode_secir) diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index f26d2fd23d..a107be850d 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -18,10 +18,12 @@ * limitations under the License. */ -#include "abm/abm.h" +#include "abm/household.h" +#include "abm/world.h" #include "abm/infection_state.h" #include "abm/location_type.h" #include "abm/time.h" +#include "abm/person_id.h" #include "graph_abm/graph_abm_mobility.h" #include "graph_abm/mobility_rules.h" #include "memilio/io/history.h" @@ -41,20 +43,22 @@ struct Logger : mio::LogAlways { * - The total number of Persons at the location * - A map containing the number of Persons per InfectionState at the location */ - using Type = std::vector>>; + using Type = std::vector>>; static Type log(const mio::abm::Simulation& sim) { Type location_information{}; - std::map persons_per_infection_state; auto t = sim.get_time(); for (auto&& loc : sim.get_world().get_locations()) { + std::map persons_per_infection_state; for (size_t i = 0; i < static_cast(mio::abm::InfectionState::Count); ++i) { auto inf_state = mio::abm::InfectionState(i); - persons_per_infection_state.insert({inf_state, loc.get_subpopulation(t, inf_state)}); + persons_per_infection_state.insert( + {inf_state, sim.get_world().get_subpopulation(loc.get_id(), t, inf_state)}); } - location_information.push_back( - std::make_tuple(mio::abm::LocationId{loc.get_index(), loc.get_type(), loc.get_world_id()}, - loc.get_number_persons(), persons_per_infection_state)); + location_information.push_back(std::make_tuple(loc.get_world_id(), loc.get_type(), loc.get_id(), + sim.get_world().get_number_persons(loc.get_id()), + persons_per_infection_state)); } return location_information; } @@ -131,10 +135,12 @@ int main() //add persons from world 0 to vector for (auto& person : world1.get_persons()) { - persons.push_back(person); + mio::abm::PersonId new_id{static_cast(persons.size())}; + persons.emplace_back(person, new_id); } auto world2 = mio::abm::World(num_age_groups, 1); + //Household groups for world 2 auto single_hh_group_w2 = mio::abm::HouseholdGroup(); single_hh_group_w2.add_households(single_hh, 6); @@ -151,36 +157,37 @@ int main() //add persons from world 1 to vector for (auto& person : world2.get_persons()) { - persons.push_back(person); + mio::abm::PersonId new_id{static_cast(persons.size())}; + persons.emplace_back(person, new_id); } //Create locations for both worlds //world 0 auto event_w1 = world1.add_location(mio::abm::LocationType::SocialEvent); - world1.get_individualized_location(event_w1).get_infection_parameters().set(10); + world1.get_location(event_w1).get_infection_parameters().set(10); auto hospital_w1 = world1.add_location(mio::abm::LocationType::Hospital); - world1.get_individualized_location(hospital_w1).get_infection_parameters().set(10); + world1.get_location(hospital_w1).get_infection_parameters().set(10); auto icu_w1 = world1.add_location(mio::abm::LocationType::ICU); - world1.get_individualized_location(icu_w1).get_infection_parameters().set(5); + world1.get_location(icu_w1).get_infection_parameters().set(5); auto shop_w1 = world1.add_location(mio::abm::LocationType::BasicsShop); - world1.get_individualized_location(shop_w1).get_infection_parameters().set(20); + world1.get_location(shop_w1).get_infection_parameters().set(20); auto school_w1 = world1.add_location(mio::abm::LocationType::School); - world1.get_individualized_location(school_w1).get_infection_parameters().set(20); + world1.get_location(school_w1).get_infection_parameters().set(20); auto work_w1 = world1.add_location(mio::abm::LocationType::Work); - world1.get_individualized_location(work_w1).get_infection_parameters().set(10); + world1.get_location(work_w1).get_infection_parameters().set(10); //World 1 auto event_w2 = world2.add_location(mio::abm::LocationType::SocialEvent); - world2.get_individualized_location(event_w2).get_infection_parameters().set(10); + world2.get_location(event_w2).get_infection_parameters().set(10); auto hospital_w2 = world2.add_location(mio::abm::LocationType::Hospital); - world2.get_individualized_location(hospital_w2).get_infection_parameters().set(10); + world2.get_location(hospital_w2).get_infection_parameters().set(10); auto icu_w2 = world2.add_location(mio::abm::LocationType::ICU); - world2.get_individualized_location(icu_w2).get_infection_parameters().set(5); + world2.get_location(icu_w2).get_infection_parameters().set(5); auto shop_w2 = world2.add_location(mio::abm::LocationType::BasicsShop); - world2.get_individualized_location(shop_w2).get_infection_parameters().set(20); + world2.get_location(shop_w2).get_infection_parameters().set(20); auto school_w2 = world2.add_location(mio::abm::LocationType::School); - world2.get_individualized_location(school_w2).get_infection_parameters().set(20); + world2.get_location(school_w2).get_infection_parameters().set(20); auto work_w2 = world2.add_location(mio::abm::LocationType::Work); - world2.get_individualized_location(work_w2).get_infection_parameters().set(10); + world2.get_location(work_w2).get_infection_parameters().set(10); auto start_date = mio::abm::TimePoint(0); auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); @@ -192,52 +199,53 @@ int main() for (auto& person : 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(mio::thread_local_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(mio::thread_local_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), world1.parameters, start_date, infection_state)); } + // Assign locations to persons from world 1 if (person.get_assigned_location_world_id(mio::abm::LocationType::Home) == world1.get_id()) { - person.set_assigned_location(event_w1); - person.set_assigned_location(shop_w1); - person.set_assigned_location(hospital_w1); - person.set_assigned_location(icu_w1); + person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w1, world1.get_id()); + person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w1, world1.get_id()); + person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w1, world1.get_id()); + person.set_assigned_location(mio::abm::LocationType::ICU, icu_w1, world1.get_id()); if (person.get_age() == age_group_children) { - person.set_assigned_location(school_w1); + person.set_assigned_location(mio::abm::LocationType::School, school_w1, world1.get_id()); } if (person.get_age() == age_group_adults) { //10% of adults in world 0 work in world 1 size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), std::vector{0.9, 0.1}); if (work_world == 1) { //person works in other world - person.set_assigned_location(work_w2); + person.set_assigned_location(mio::abm::LocationType::Work, work_w2, world2.get_id()); //add person to edge parameters - params_e1.push_back(person.get_person_id()); + params_e1.push_back(person.get_id().get()); } else { //person works in same world - person.set_assigned_location(work_w1); + person.set_assigned_location(mio::abm::LocationType::Work, work_w1, world1.get_id()); } } } else { - person.set_assigned_location(event_w2); - person.set_assigned_location(shop_w2); - person.set_assigned_location(hospital_w2); - person.set_assigned_location(icu_w2); + person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w2, world2.get_id()); + person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w2, world2.get_id()); + person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w2, world2.get_id()); + person.set_assigned_location(mio::abm::LocationType::ICU, icu_w2, world2.get_id()); if (person.get_age() == age_group_children) { - person.set_assigned_location(school_w2); + person.set_assigned_location(mio::abm::LocationType::School, school_w2, world2.get_id()); } if (person.get_age() == age_group_adults) { //20% of adults in world 1 work in world 0 size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), std::vector{0.2, 0.8}); if (work_world == 0) { //person works in other world - person.set_assigned_location(work_w1); + person.set_assigned_location(mio::abm::LocationType::Work, work_w1, world1.get_id()); //add person to edge parameters - params_e2.push_back(person.get_person_id()); + params_e2.push_back(person.get_id().get()); } else { //person works in same world - person.set_assigned_location(work_w2); + person.set_assigned_location(mio::abm::LocationType::Work, work_w2, world2.get_id()); } } } @@ -259,5 +267,8 @@ int main() auto sim = mio::make_abm_graph_sim(start_date, mio::abm::hours(12), std::move(graph)); sim.advance(end_date); + auto& log_n1 = std::get<0>(sim.get_graph().nodes()[0].property.get_history()).get_log(); + auto& log_n2 = std::get<0>(sim.get_graph().nodes()[1].property.get_history()).get_log(); + return 0; } \ No newline at end of file diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index f41a10a489..d7a663dbd6 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -73,18 +73,6 @@ PersonId World::add_person(Person&& person) return new_id; } -// Person& World::add_external_person(Location& loc, 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, loc, age, person_id)); -// m_activeness_statuses.push_back(false); -// auto& person = *m_persons.back(); -// person.set_assigned_location(m_cemetery_id); -// loc.add_person(person); -// return person; -// } - void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); @@ -96,10 +84,16 @@ void World::evolve(TimePoint t, TimeSpan dt) void World::interaction(TimePoint t, TimeSpan dt) { + if (t.days() == 14) { + std::cout << "stop\n"; + } const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { if (m_activeness_statuses[person_id]) { + if (person_id == 41 && t.days() == 14) { + std::cout << "person= " << person_id << std::endl; + } interact(person_id, t, dt); } } @@ -256,9 +250,11 @@ 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().get(); - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], - m_contact_exposure_rates_cache[location], person, - get_location(person.get_id()), t, dt); + if (person.get_location_world_id() == m_id) { + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], + m_contact_exposure_rates_cache[location], person, + get_location(person.get_id()), t, dt); + } } // implicit taskloop barrier } // implicit single barrier } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index d0c6e183aa..3097e33ce9 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -75,10 +75,10 @@ class World */ World(const Parameters& params, int world_id = 0) : parameters(params.get_num_groups()) + , m_id(world_id) , m_trip_list() , m_use_migration_rules(true) , m_cemetery_id(add_location(LocationType::Cemetery)) - , m_id(world_id) { parameters = params; } @@ -91,6 +91,7 @@ class World , m_is_local_population_cache_valid(false) , m_are_exposure_caches_valid(false) , m_exposure_caches_need_rebuild(true) + , m_id(world_id) , m_persons(other.m_persons) , m_locations(other.m_locations) , m_activeness_statuses(other.m_activeness_statuses) @@ -101,7 +102,6 @@ class World , m_migration_rules(other.m_migration_rules) , m_cemetery_id(other.m_cemetery_id) , m_rng(other.m_rng) - , m_id(world_id) { } World& operator=(const World&) = default; @@ -380,7 +380,8 @@ class World size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { 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_world_id() == m_id && p.get_location() == location && + p.get_infection_state(t) == state; }); } @@ -485,6 +486,9 @@ class World void change_activeness(PersonId person_id) { m_activeness_statuses[person_id.get()] = !m_activeness_statuses[person_id.get()]; + if (get_person(person_id).get_location_world_id() != m_id && m_activeness_statuses[person_id.get()]) { + std::cout << "Problem\n"; + } } /** @@ -584,6 +588,7 @@ class World bool m_are_exposure_caches_valid = false; bool m_exposure_caches_need_rebuild = true; + int m_id; ///< World id. Is only used for abm graph model or hybrid model. std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. std::vector m_activeness_statuses; ///< Vector with activeness status for every person @@ -598,7 +603,6 @@ class World m_migration_rules; ///< Rules that govern the migration between Location%s. LocationId m_cemetery_id; // Central cemetery for all dead persons. RandomNumberGenerator m_rng; ///< Global random number generator - int m_id; ///< World id. Is only used for abm graph model or hybrid model. }; } // namespace abm diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 6e2229c1f0..7b90d7bf5d 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -53,7 +53,7 @@ class ABMSimulationNode } /** - *@brief get abm simulation in this node. + *@brief Get abm simulation in this node. */ Sim& get_simulation() { @@ -64,6 +64,18 @@ class ABMSimulationNode return m_simulation; } + /** + * @brief Get history object(s) in this node. + */ + std::tuple& get_history() + { + return m_history; + } + const std::tuple& get_history() const + { + return m_history; + } + /** * @brief advances the simulation in this node by t+dt and logs information in History object(s) * @tparam History history object type(s) @@ -236,9 +248,11 @@ class ABMMobilityEdge // invalidate both worlds' cache node_to.get_simulation().get_world().invalidate_cache(); node_from.get_simulation().get_world().invalidate_cache(); - // change activeness status for commuted person - node_to.get_simulation().get_world().change_activeness(p); - node_from.get_simulation().get_world().change_activeness(p); + if (target_world_id != current_world_id) { + // change activeness status for commuted person + node_to.get_simulation().get_world().change_activeness(p); + node_from.get_simulation().get_world().change_activeness(p); + } // only one mobility rule per person can be applied break; } @@ -275,6 +289,7 @@ void apply_mobility(abm::TimePoint t, abm::TimeSpan /*dt*/, ABMMobilityEdge void evolve_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode& node) { + std::cout << "t= " << t.days() << std::endl; node.evolve(t, dt); } diff --git a/cpp/models/graph_abm/mobility_rules.cpp b/cpp/models/graph_abm/mobility_rules.cpp index 16b536fd93..cab23286c8 100644 --- a/cpp/models/graph_abm/mobility_rules.cpp +++ b/cpp/models/graph_abm/mobility_rules.cpp @@ -20,6 +20,7 @@ #include "graph_abm/mobility_rules.h" #include "abm/location.h" +#include "abm/infection_state.h" namespace mio { @@ -34,7 +35,29 @@ abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, c return abm::LocationType::Work; } - // agents are sent home or to work every time this function is called i.e. if it is called too often they will be sent to work multiple times + //person is at hospital in non-home world + if (current_loc == abm::LocationType::Hospital && + person.get_location_world_id() != person.get_assigned_location_world_id(abm::LocationType::Home)) { + //if person is still infected it stays at hospital + if (person.is_infected(t)) { + return current_loc; + } + //if person has recovered it goes home + else if (person.get_infection_state(t) == abm::InfectionState::Recovered) { + return abm::LocationType::Home; + } + //if person is dead it is sent to cementary + else { + return abm::LocationType::Cemetery; + } + } + //person is at location in Home world (and should not go to work) it stays at that location + if (person.get_location_world_id() == person.get_assigned_location_world_id(abm::LocationType::Home)) { + return current_loc; + } + + // agents are sent home or to work (if none of the above cases occurs) every time this function is called + // i.e. if it is called too often they will be sent to work multiple times return abm::LocationType::Home; } } // namespace mio \ No newline at end of file From ae95301abf156ed782d45afc5f1958879cfa5bf7 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 25 Jul 2024 10:31:01 +0200 Subject: [PATCH 020/111] remove unnecessary comment --- cpp/models/abm/world.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index d0c6e183aa..1724014f61 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -192,15 +192,6 @@ class World */ PersonId add_person(Person&& person); - // /** - // * @brief Add an external Person i.e. a Person whoseHome location is in another World to the World. - // * Only used for abm graph model. - // * @param[in] loc Initial Location of the Person - // * @param[in] age AgeGroup of the Person - // * @return Reference to the newly created Person - // */ - // Person& add_external_person(Location& loc, AgeGroup age); - /** * @brief Get a range of all Location%s in the World. * @return A range of all Location%s. From e22009b1bbe9c7c5bf4d74ac9f22dbc3c4b8f75d Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 25 Jul 2024 10:39:15 +0200 Subject: [PATCH 021/111] fix bug --- cpp/models/abm/world.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 37025127f4..993e2be59a 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -61,10 +61,10 @@ class World */ World(size_t num_agegroups, int id = 0) : parameters(num_agegroups) + , m_id(id) , m_trip_list() , m_use_migration_rules(true) , m_cemetery_id(add_location(LocationType::Cemetery)) - , m_id(id) { assert(num_agegroups < MAX_NUM_AGE_GROUPS && "MAX_NUM_AGE_GROUPS exceeded."); } From 11c3ae861593aee7ce43bd83e7d1a4c6c4af6131 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 26 Jul 2024 09:24:29 +0200 Subject: [PATCH 022/111] delete dbugging comments --- cpp/models/abm/world.cpp | 6 ------ cpp/models/abm/world.h | 3 --- cpp/models/graph_abm/graph_abm_mobility.h | 1 - 3 files changed, 10 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index d7a663dbd6..c9ed70e5bd 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -84,16 +84,10 @@ void World::evolve(TimePoint t, TimeSpan dt) void World::interaction(TimePoint t, TimeSpan dt) { - if (t.days() == 14) { - std::cout << "stop\n"; - } const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { if (m_activeness_statuses[person_id]) { - if (person_id == 41 && t.days() == 14) { - std::cout << "person= " << person_id << std::endl; - } interact(person_id, t, dt); } } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 37025127f4..eaa761b3a6 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -477,9 +477,6 @@ class World void change_activeness(PersonId person_id) { m_activeness_statuses[person_id.get()] = !m_activeness_statuses[person_id.get()]; - if (get_person(person_id).get_location_world_id() != m_id && m_activeness_statuses[person_id.get()]) { - std::cout << "Problem\n"; - } } /** diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 7b90d7bf5d..deef17d8f8 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -289,7 +289,6 @@ void apply_mobility(abm::TimePoint t, abm::TimeSpan /*dt*/, ABMMobilityEdge void evolve_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode& node) { - std::cout << "t= " << t.days() << std::endl; node.evolve(t, dt); } From fdad4dae1379ccebcb09988ed68b84728eb06064 Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 31 Jul 2024 11:49:45 +0200 Subject: [PATCH 023/111] benchmark inactive persons --- cpp/benchmarks/abm.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++ cpp/models/abm/world.h | 5 ++++ 2 files changed, 68 insertions(+) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 7246db43a7..3371b9052d 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -1,6 +1,7 @@ #include "abm/simulation.h" #include "benchmark/benchmark.h" +#include mio::abm::Simulation make_simulation(size_t num_persons, std::initializer_list seeds) { @@ -144,6 +145,38 @@ void abm_benchmark(benchmark::State& state, size_t num_persons, std::initializer } } +void abm_inactive_persons_benchmark(benchmark::State& state, size_t num_persons, size_t num_inactive_persons, + std::initializer_list seeds) +{ + mio::set_log_level(mio::LogLevel::warn); + for (auto&& _ : state) { + state.PauseTiming(); //exclude the setup from the benchmark + auto sim = make_simulation(num_persons + num_inactive_persons, seeds); + //deactivate num_inactive_persons + for (size_t p_id = 0; p_id < num_inactive_persons; ++p_id) { + sim.get_world().set_activeness(mio::abm::PersonId(p_id)); + } + state.ResumeTiming(); + + //simulated time should be long enough to have full infection runs and migration to every location + auto final_time = sim.get_time() + mio::abm::days(10); + sim.advance(final_time); + + //debug output can be enabled to check for unexpected results (e.g. infections dieing out) + //normally should have no significant effect on runtime + const bool monitor_infection_activity = false; + if constexpr (monitor_infection_activity) { + std::cout << "num_persons = " << num_persons << "\n"; + for (auto inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { + std::cout << "inf_state = " << inf_state << ", sum = " + << sim.get_world().get_subpopulation_combined(sim.get_time(), + mio::abm::InfectionState(inf_state)) + << "\n"; + } + } + } +} + //Measure ABM simulation run time with different sizes and different seeds. //Fixed RNG seeds to make runs comparable. When there are code changes, the simulation will still //run differently due to different sequence of random numbers being drawn. But for large enough sizes @@ -156,4 +189,34 @@ BENCHMARK_CAPTURE(abm_benchmark, abm_benchmark_50k, 50000, {14159265u, 35897932u BENCHMARK_CAPTURE(abm_benchmark, abm_benchmark_100k, 100000, {38462643u, 38327950u})->Unit(benchmark::kMillisecond); BENCHMARK_CAPTURE(abm_benchmark, abm_benchmark_200k, 200000, {28841971u, 69399375u})->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_50k, 50000, 50000, + {14159265u, 35897932u}) + ->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_100k, 100000, 100000, + {38462643u, 38327950u}) + ->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_200k, 200000, 200000, + {28841971u, 69399375u}) + ->Unit(benchmark::kMillisecond); + +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_50k_2, 50000, 100000, + {14159265u, 35897932u}) + ->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_100k_2, 100000, 200000, + {38462643u, 38327950u}) + ->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_200k_2, 200000, 400000, + {28841971u, 69399375u}) + ->Unit(benchmark::kMillisecond); + +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_50k_3, 50000, 150000, + {14159265u, 35897932u}) + ->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_100k_3, 100000, 300000, + {38462643u, 38327950u}) + ->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_200k_3, 200000, 600000, + {28841971u, 69399375u}) + ->Unit(benchmark::kMillisecond); + BENCHMARK_MAIN(); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 7705045de2..0af2a7402d 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -479,6 +479,11 @@ class World m_activeness_statuses[person_id.get()] = !m_activeness_statuses[person_id.get()]; } + void set_activeness(PersonId person_id) + { + m_activeness_statuses[person_id.get()] = false; + } + /** * @brief Copy the persons from another World to this World. * If the persons are at a location in this world they are activated, otherwise they are deactivated. From b5b9338c9cb13a34a25a6d37fd999993abacdb05 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 1 Aug 2024 16:35:08 +0200 Subject: [PATCH 024/111] fix merge conflicts --- cpp/benchmarks/abm.cpp | 6 +- cpp/examples/graph_abm.cpp | 182 ++++++++++---------- cpp/models/abm/location.h | 18 +- cpp/models/abm/model.cpp | 103 ++++++----- cpp/models/abm/model.h | 109 +++++++++++- cpp/models/abm/model_functions.cpp | 2 +- cpp/models/abm/person.cpp | 18 +- cpp/models/abm/person.h | 34 ++-- cpp/models/graph_abm/graph_abm_mobility.cpp | 2 +- cpp/models/graph_abm/graph_abm_mobility.h | 42 ++--- cpp/models/graph_abm/mobility_rules.cpp | 8 +- cpp/models/graph_abm/mobility_rules.h | 2 +- cpp/tests/abm_helpers.cpp | 2 +- cpp/tests/test_abm_infection.cpp | 2 +- cpp/tests/test_abm_lockdown_rules.cpp | 48 +++--- cpp/tests/test_abm_masks.cpp | 4 +- cpp/tests/test_abm_mobility_rules.cpp | 36 ++-- cpp/tests/test_abm_model.cpp | 66 +++---- cpp/tests/test_abm_person.cpp | 12 +- cpp/tests/test_graph_abm.cpp | 118 ++++++------- 20 files changed, 463 insertions(+), 351 deletions(-) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 2a0b74f211..28b59486a3 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -154,11 +154,11 @@ void abm_inactive_persons_benchmark(benchmark::State& state, size_t num_persons, auto sim = make_simulation(num_persons + num_inactive_persons, seeds); //deactivate num_inactive_persons for (size_t p_id = 0; p_id < num_inactive_persons; ++p_id) { - sim.get_world().set_activeness(mio::abm::PersonId(p_id)); + sim.get_model().set_activeness(mio::abm::PersonId(p_id)); } state.ResumeTiming(); - //simulated time should be long enough to have full infection runs and migration to every location + //simulated time should be long enough to have full infection runs and mobility to every location auto final_time = sim.get_time() + mio::abm::days(10); sim.advance(final_time); @@ -169,7 +169,7 @@ void abm_inactive_persons_benchmark(benchmark::State& state, size_t num_persons, std::cout << "num_persons = " << num_persons << "\n"; for (auto inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { std::cout << "inf_state = " << inf_state << ", sum = " - << sim.get_world().get_subpopulation_combined(sim.get_time(), + << sim.get_model().get_subpopulation_combined(sim.get_time(), mio::abm::InfectionState(inf_state)) << "\n"; } diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index a107be850d..70a32d995e 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -19,7 +19,7 @@ */ #include "abm/household.h" -#include "abm/world.h" +#include "abm/model.h" #include "abm/infection_state.h" #include "abm/location_type.h" #include "abm/time.h" @@ -39,7 +39,7 @@ struct Logger : mio::LogAlways { /** * A vector of tuples with the Location information i.e. each tuple contains the following information: - * - The LocationId (including the world id) + * - The LocationId (including the model id) * - The total number of Persons at the location * - A map containing the number of Persons per InfectionState at the location */ @@ -49,15 +49,15 @@ struct Logger : mio::LogAlways { { Type location_information{}; auto t = sim.get_time(); - for (auto&& loc : sim.get_world().get_locations()) { + for (auto&& loc : sim.get_model().get_locations()) { std::map persons_per_infection_state; for (size_t i = 0; i < static_cast(mio::abm::InfectionState::Count); ++i) { auto inf_state = mio::abm::InfectionState(i); persons_per_infection_state.insert( - {inf_state, sim.get_world().get_subpopulation(loc.get_id(), t, inf_state)}); + {inf_state, sim.get_model().get_subpopulation(loc.get_id(), t, inf_state)}); } - location_information.push_back(std::make_tuple(loc.get_world_id(), loc.get_type(), loc.get_id(), - sim.get_world().get_number_persons(loc.get_id()), + location_information.push_back(std::make_tuple(loc.get_model_id(), loc.get_type(), loc.get_id(), + sim.get_model().get_number_persons(loc.get_id()), persons_per_infection_state)); } return location_information; @@ -72,22 +72,22 @@ int main() const auto age_group_adults = mio::AgeGroup(1); const auto age_group_seniors = mio::AgeGroup(2); - auto world1 = mio::abm::World(num_age_groups, 0); + auto model1 = mio::abm::Model(num_age_groups, 0); //Set infection parameters - world1.parameters.get() = 4.; - world1.parameters.get() = 2.; - world1.parameters.get() = 4.; - world1.parameters.get() = 5.; - world1.parameters.get() = 6.; - world1.parameters.get() = 8.; - world1.parameters.get() = 7.; - world1.parameters.get() = 10.; - world1.parameters.get() = 11.; + model1.parameters.get() = 4.; + model1.parameters.get() = 2.; + model1.parameters.get() = 4.; + model1.parameters.get() = 5.; + model1.parameters.get() = 6.; + model1.parameters.get() = 8.; + model1.parameters.get() = 7.; + model1.parameters.get() = 10.; + model1.parameters.get() = 11.; //Age group 0 goes to school and age group 1 goes to work - world1.parameters.get()[age_group_children] = true; - world1.parameters.get()[age_group_adults] = true; + model1.parameters.get()[age_group_children] = true; + model1.parameters.get()[age_group_adults] = true; //Household members can be child, parent or senior auto child = mio::abm::HouseholdMember(num_age_groups); @@ -116,10 +116,10 @@ int main() family_hh.add_members(child, 1); family_hh.add_members(parent, 2); - // Vector holding all persons for the graph simulation. This vector is copied to all worlds at the end. + // Vector holding all persons for the graph simulation. This vector is copied to all models at the end. std::vector persons; - //Household groups for world 1 + //Household groups for model 1 auto single_hh_group_w1 = mio::abm::HouseholdGroup(); single_hh_group_w1.add_households(single_hh, 5); auto two_adult_hh_group_w1 = mio::abm::HouseholdGroup(); @@ -128,20 +128,20 @@ int main() single_parent_hh_group_w1.add_households(single_parent_hh, 5); auto family_hh_group_w1 = mio::abm::HouseholdGroup(); family_hh_group_w1.add_households(family_hh, 10); - add_household_group_to_world(world1, single_hh_group_w1); - add_household_group_to_world(world1, two_adult_hh_group_w1); - add_household_group_to_world(world1, single_hh_group_w1); - add_household_group_to_world(world1, family_hh_group_w1); + add_household_group_to_model(model1, single_hh_group_w1); + add_household_group_to_model(model1, two_adult_hh_group_w1); + add_household_group_to_model(model1, single_hh_group_w1); + add_household_group_to_model(model1, family_hh_group_w1); - //add persons from world 0 to vector - for (auto& person : world1.get_persons()) { + //add persons from model 0 to vector + for (auto& person : model1.get_persons()) { mio::abm::PersonId new_id{static_cast(persons.size())}; persons.emplace_back(person, new_id); } - auto world2 = mio::abm::World(num_age_groups, 1); + auto model2 = mio::abm::Model(num_age_groups, 1); - //Household groups for world 2 + //Household groups for model 2 auto single_hh_group_w2 = mio::abm::HouseholdGroup(); single_hh_group_w2.add_households(single_hh, 6); auto two_adult_hh_group_w2 = mio::abm::HouseholdGroup(); @@ -150,44 +150,44 @@ int main() single_parent_hh_group_w2.add_households(single_parent_hh, 10); auto family_hh_group_w2 = mio::abm::HouseholdGroup(); family_hh_group_w2.add_households(family_hh, 11); - add_household_group_to_world(world2, single_hh_group_w2); - add_household_group_to_world(world2, two_adult_hh_group_w2); - add_household_group_to_world(world2, single_hh_group_w2); - add_household_group_to_world(world2, family_hh_group_w2); + add_household_group_to_model(model2, single_hh_group_w2); + add_household_group_to_model(model2, two_adult_hh_group_w2); + add_household_group_to_model(model2, single_hh_group_w2); + add_household_group_to_model(model2, family_hh_group_w2); - //add persons from world 1 to vector - for (auto& person : world2.get_persons()) { + //add persons from model 1 to vector + for (auto& person : model2.get_persons()) { mio::abm::PersonId new_id{static_cast(persons.size())}; persons.emplace_back(person, new_id); } - //Create locations for both worlds - //world 0 - auto event_w1 = world1.add_location(mio::abm::LocationType::SocialEvent); - world1.get_location(event_w1).get_infection_parameters().set(10); - auto hospital_w1 = world1.add_location(mio::abm::LocationType::Hospital); - world1.get_location(hospital_w1).get_infection_parameters().set(10); - auto icu_w1 = world1.add_location(mio::abm::LocationType::ICU); - world1.get_location(icu_w1).get_infection_parameters().set(5); - auto shop_w1 = world1.add_location(mio::abm::LocationType::BasicsShop); - world1.get_location(shop_w1).get_infection_parameters().set(20); - auto school_w1 = world1.add_location(mio::abm::LocationType::School); - world1.get_location(school_w1).get_infection_parameters().set(20); - auto work_w1 = world1.add_location(mio::abm::LocationType::Work); - world1.get_location(work_w1).get_infection_parameters().set(10); - //World 1 - auto event_w2 = world2.add_location(mio::abm::LocationType::SocialEvent); - world2.get_location(event_w2).get_infection_parameters().set(10); - auto hospital_w2 = world2.add_location(mio::abm::LocationType::Hospital); - world2.get_location(hospital_w2).get_infection_parameters().set(10); - auto icu_w2 = world2.add_location(mio::abm::LocationType::ICU); - world2.get_location(icu_w2).get_infection_parameters().set(5); - auto shop_w2 = world2.add_location(mio::abm::LocationType::BasicsShop); - world2.get_location(shop_w2).get_infection_parameters().set(20); - auto school_w2 = world2.add_location(mio::abm::LocationType::School); - world2.get_location(school_w2).get_infection_parameters().set(20); - auto work_w2 = world2.add_location(mio::abm::LocationType::Work); - world2.get_location(work_w2).get_infection_parameters().set(10); + //Create locations for both models + //model 0 + auto event_w1 = model1.add_location(mio::abm::LocationType::SocialEvent); + model1.get_location(event_w1).get_infection_parameters().set(10); + auto hospital_w1 = model1.add_location(mio::abm::LocationType::Hospital); + model1.get_location(hospital_w1).get_infection_parameters().set(10); + auto icu_w1 = model1.add_location(mio::abm::LocationType::ICU); + model1.get_location(icu_w1).get_infection_parameters().set(5); + auto shop_w1 = model1.add_location(mio::abm::LocationType::BasicsShop); + model1.get_location(shop_w1).get_infection_parameters().set(20); + auto school_w1 = model1.add_location(mio::abm::LocationType::School); + model1.get_location(school_w1).get_infection_parameters().set(20); + auto work_w1 = model1.add_location(mio::abm::LocationType::Work); + model1.get_location(work_w1).get_infection_parameters().set(10); + //model 1 + auto event_w2 = model2.add_location(mio::abm::LocationType::SocialEvent); + model2.get_location(event_w2).get_infection_parameters().set(10); + auto hospital_w2 = model2.add_location(mio::abm::LocationType::Hospital); + model2.get_location(hospital_w2).get_infection_parameters().set(10); + auto icu_w2 = model2.add_location(mio::abm::LocationType::ICU); + model2.get_location(icu_w2).get_infection_parameters().set(5); + auto shop_w2 = model2.add_location(mio::abm::LocationType::BasicsShop); + model2.get_location(shop_w2).get_infection_parameters().set(20); + auto school_w2 = model2.add_location(mio::abm::LocationType::School); + model2.get_location(school_w2).get_infection_parameters().set(20); + auto work_w2 = model2.add_location(mio::abm::LocationType::Work); + model2.get_location(work_w2).get_infection_parameters().set(10); auto start_date = mio::abm::TimePoint(0); auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); @@ -202,63 +202,63 @@ int main() auto rng = mio::abm::PersonalRandomNumberGenerator(mio::thread_local_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - world1.parameters, start_date, infection_state)); + model1.parameters, start_date, infection_state)); } - // Assign locations to persons from world 1 - if (person.get_assigned_location_world_id(mio::abm::LocationType::Home) == world1.get_id()) { - person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w1, world1.get_id()); - person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w1, world1.get_id()); - person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w1, world1.get_id()); - person.set_assigned_location(mio::abm::LocationType::ICU, icu_w1, world1.get_id()); + // Assign locations to persons from model 1 + if (person.get_assigned_location_model_id(mio::abm::LocationType::Home) == model1.get_id()) { + person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w1, model1.get_id()); + person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w1, model1.get_id()); + person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w1, model1.get_id()); + person.set_assigned_location(mio::abm::LocationType::ICU, icu_w1, model1.get_id()); if (person.get_age() == age_group_children) { - person.set_assigned_location(mio::abm::LocationType::School, school_w1, world1.get_id()); + person.set_assigned_location(mio::abm::LocationType::School, school_w1, model1.get_id()); } if (person.get_age() == age_group_adults) { - //10% of adults in world 0 work in world 1 - size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), + //10% of adults in model 0 work in model 1 + size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), std::vector{0.9, 0.1}); - if (work_world == 1) { //person works in other world - person.set_assigned_location(mio::abm::LocationType::Work, work_w2, world2.get_id()); + if (work_model == 1) { //person works in other model + person.set_assigned_location(mio::abm::LocationType::Work, work_w2, model2.get_id()); //add person to edge parameters params_e1.push_back(person.get_id().get()); } - else { //person works in same world - person.set_assigned_location(mio::abm::LocationType::Work, work_w1, world1.get_id()); + else { //person works in same model + person.set_assigned_location(mio::abm::LocationType::Work, work_w1, model1.get_id()); } } } else { - person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w2, world2.get_id()); - person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w2, world2.get_id()); - person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w2, world2.get_id()); - person.set_assigned_location(mio::abm::LocationType::ICU, icu_w2, world2.get_id()); + person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w2, model2.get_id()); + person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w2, model2.get_id()); + person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w2, model2.get_id()); + person.set_assigned_location(mio::abm::LocationType::ICU, icu_w2, model2.get_id()); if (person.get_age() == age_group_children) { - person.set_assigned_location(mio::abm::LocationType::School, school_w2, world2.get_id()); + person.set_assigned_location(mio::abm::LocationType::School, school_w2, model2.get_id()); } if (person.get_age() == age_group_adults) { - //20% of adults in world 1 work in world 0 - size_t work_world = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), + //20% of adults in model 1 work in model 0 + size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), std::vector{0.2, 0.8}); - if (work_world == 0) { //person works in other world - person.set_assigned_location(mio::abm::LocationType::Work, work_w1, world1.get_id()); + if (work_model == 0) { //person works in other model + person.set_assigned_location(mio::abm::LocationType::Work, work_w1, model1.get_id()); //add person to edge parameters params_e2.push_back(person.get_id().get()); } - else { //person works in same world - person.set_assigned_location(mio::abm::LocationType::Work, work_w2, world2.get_id()); + else { //person works in same model + person.set_assigned_location(mio::abm::LocationType::Work, work_w2, model2.get_id()); } } } } - //copy persons to both worlds - world1.set_persons(persons); - world2.set_persons(persons); + //copy persons to both models + model1.set_persons(persons); + model2.set_persons(persons); using HistoryType = mio::History; mio::Graph, mio::ABMMobilityEdge> graph; - graph.add_node(world1.get_id(), HistoryType{}, start_date, std::move(world1)); - graph.add_node(world2.get_id(), HistoryType{}, start_date, std::move(world2)); + graph.add_node(model1.get_id(), HistoryType{}, start_date, std::move(model1)); + graph.add_node(model2.get_id(), HistoryType{}, start_date, std::move(model2)); graph.add_edge(0, 1, params_e1, std::vector::MobilityRuleType>{&mio::apply_commuting}); graph.add_edge(1, 0, params_e2, diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 4cbe460a81..0a45b58786 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -281,21 +281,21 @@ class Location } /** - * @brief Get the world id the location is in. Is only relevant for graph ABM or hybrid model. - * @return World id of the location + * @brief Get the model id the location is in. Is only relevant for graph ABM or hybrid model. + * @return Model id of the location */ - int get_world_id() const + int get_model_id() const { - return m_world_id; + return m_model_id; } /** - * @brief Set world id of the Location. Is only relevant for graph ABM or hybrid model. - * @param[in] world_id The world id of the location. + * @brief Set model id of the Location. Is only relevant for graph ABM or hybrid model. + * @param[in] model_id The model id of the location. */ - void set_world_id(int world_id) + void set_model_id(int model_id) { - m_world_id = world_id; + m_model_id = model_id; } private: @@ -306,7 +306,7 @@ class Location 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. GeographicalLocation m_geographical_location; ///< Geographical location (longitude and latitude) of the Location. - int m_world_id = 0; ///< World id the location is in. Only used for ABM graph model or hybrid graph model. + int m_model_id = 0; ///< Model id the location is in. Only used for ABM graph model or hybrid graph model. }; } // namespace abm diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 3e690556c6..503d22a0bd 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -39,6 +39,7 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) LocationId id{static_cast(m_locations.size())}; m_locations.emplace_back(type, id, parameters.get_num_groups(), num_cells); m_has_locations[size_t(type)] = true; + m_locations[id.get()].set_model_id(m_id); // mark caches for rebuild m_is_local_population_cache_valid = false; @@ -50,7 +51,7 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) PersonId Model::add_person(const LocationId id, AgeGroup age) { - return add_person(Person(m_rng, get_location(id).get_type(), id, age)); + return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age)); } PersonId Model::add_person(Person&& person) @@ -62,8 +63,9 @@ PersonId Model::add_person(Person&& person) PersonId new_id{static_cast(m_persons.size())}; m_persons.emplace_back(person, new_id); + m_activeness_statuses.push_back(true); auto& new_person = m_persons.back(); - new_person.set_assigned_location(LocationType::Cemetery, m_cemetery_id); + new_person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); if (m_is_local_population_cache_valid) { ++m_local_population_cache[new_person.get_location().get()]; @@ -85,7 +87,9 @@ void Model::interaction(TimePoint t, TimeSpan dt) const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { - interact(person_id, t, dt); + if (m_activeness_statuses[person_id]) { + interact(person_id, t, dt); + } } } @@ -94,47 +98,52 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) 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_mobility_rule = [&](auto rule) -> bool { - //run mobility rule and check if change of location can actually happen - auto target_type = rule(personal_rng, person, t, dt, parameters); - 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) { - change_location(person_id, target_location.get_id()); + if (m_activeness_statuses[person_id]) { + Person& person = m_persons[person_id]; + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + + auto try_mobility_rule = [&](auto rule) -> bool { + //run mobility rule and check if change of location can actually happen + auto target_type = rule(personal_rng, person, t, dt, parameters); + if (person.get_assigned_location_model_id(target_type) == m_id) { + const Location& target_location = get_location(find_location(target_type, person_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) { + change_location(person_id, target_location.get_id()); + } + return true; + } } - return true; } + return false; + }; + + //run mobility rules one after the other if the corresponding location type exists + //shortcutting of bool operators ensures the rules stop after the first rule is applied + if (m_use_mobility_rules) { + (has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || + (has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || + (has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || + (has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || + (has_locations({LocationType::School, LocationType::Home}) && try_mobility_rule(&go_to_school)) || + (has_locations({LocationType::Work, LocationType::Home}) && try_mobility_rule(&go_to_work)) || + (has_locations({LocationType::BasicsShop, LocationType::Home}) && try_mobility_rule(&go_to_shop)) || + (has_locations({LocationType::SocialEvent, LocationType::Home}) && + try_mobility_rule(&go_to_event)) || + (has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); + } + else { + //no daily routine mobility, just infection related + (has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || + (has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || + (has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || + (has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || + (has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); } - return false; - }; - - //run mobility rules one after the other if the corresponding location type exists - //shortcutting of bool operators ensures the rules stop after the first rule is applied - if (m_use_mobility_rules) { - (has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || - (has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || - (has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || - (has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || - (has_locations({LocationType::School, LocationType::Home}) && try_mobility_rule(&go_to_school)) || - (has_locations({LocationType::Work, LocationType::Home}) && try_mobility_rule(&go_to_work)) || - (has_locations({LocationType::BasicsShop, LocationType::Home}) && try_mobility_rule(&go_to_shop)) || - (has_locations({LocationType::SocialEvent, LocationType::Home}) && try_mobility_rule(&go_to_event)) || - (has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); - } - else { - //no daily routine mobility, just infection related - (has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || - (has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || - (has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || - (has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || - (has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); } } @@ -176,7 +185,9 @@ void Model::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().get()]; + if (m_persons[i].get_location_model_id() == m_id) { + ++m_local_population_cache[m_persons[i].get_location().get()]; + } } // implicit taskloop barrier } // implicit single barrier } @@ -232,9 +243,11 @@ void Model::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().get(); - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], - m_contact_exposure_rates_cache[location], person, - get_location(person.get_id()), t, dt); + if (person.get_location_model_id() == m_id) { + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], + m_contact_exposure_rates_cache[location], person, + get_location(person.get_id()), t, dt); + } } // implicit taskloop barrier } // implicit single barrier } diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 69f6ae5005..948db7c2d8 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -59,8 +59,9 @@ class Model * @brief Create a Model. * @param[in] num_agegroups The number of AgeGroup%s in the simulated Model. Must be less than MAX_NUM_AGE_GROUPS. */ - Model(size_t num_agegroups) + Model(size_t num_agegroups, int id = 0) : parameters(num_agegroups) + , m_id(id) , m_trip_list() , m_use_mobility_rules(true) , m_cemetery_id(add_location(LocationType::Cemetery)) @@ -72,8 +73,9 @@ class Model * @brief Create a Model. * @param[in] params Initial simulation parameters. */ - Model(const Parameters& params) + Model(const Parameters& params, int id = 0) : parameters(params.get_num_groups()) + , m_id(id) , m_trip_list() , m_use_mobility_rules(true) , m_cemetery_id(add_location(LocationType::Cemetery)) @@ -81,7 +83,7 @@ class Model parameters = params; } - Model(const Model& other) + Model(const Model& other, int id = 0) : parameters(other.parameters) , m_local_population_cache() , m_air_exposure_rates_cache() @@ -89,8 +91,10 @@ class Model , m_is_local_population_cache_valid(false) , m_are_exposure_caches_valid(false) , m_exposure_caches_need_rebuild(true) + , m_id(id) , m_persons(other.m_persons) , m_locations(other.m_locations) + , m_activeness_statuses(other.m_activeness_statuses) , m_has_locations(other.m_has_locations) , m_testing_strategy(other.m_testing_strategy) , m_trip_list(other.m_trip_list) @@ -223,7 +227,7 @@ class Model */ void assign_location(PersonId person, LocationId location) { - get_person(person).set_assigned_location(get_location(location).get_type(), location); + get_person(person).set_assigned_location(get_location(location).get_type(), location, m_id); } /** @@ -304,6 +308,24 @@ class Model return m_rng; } + /** + * Get the model id. Is only relevant for graph abm or hybrid model. + * @return The model id + */ + int get_id() const + { + return m_id; + } + + /** + * Get activeness status of all persons in the model. + * @return Activeness vector + */ + std::vector& get_activeness_statuses() + { + return m_activeness_statuses; + } + /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at all Locations that have * the LocationType. @@ -349,7 +371,8 @@ class Model size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const { 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_model_id() == m_id && p.get_location() == location && + p.get_infection_state(t) == state; }); } @@ -448,6 +471,80 @@ class Model } /** @} */ + /** + * @brief Flip activeness status of a person in the model. + * @param[in] person_id PersonId of Person whose activeness status is fipped. + */ + void change_activeness(PersonId person_id) + { + m_activeness_statuses[person_id.get()] = !m_activeness_statuses[person_id.get()]; + } + + void set_activeness(PersonId person_id) + { + m_activeness_statuses[person_id.get()] = false; + } + + /** + * @brief Copy the persons from another Model to this Model. + * If the persons are at a location in this model they are activated, otherwise they are deactivated. + * If necessary the person ids are changed such that they correspond to the index in this model's m_persons vector. + * @param[in] other The Model the Person%s are copied from. + */ + void copy_persons_from_other_model(const Model& other) + { + for (auto& p : other.get_persons()) { + if (p.get_id() != static_cast(m_persons.size())) { + mio::log_debug("In model.copy_persons_from_other_model: PersonId does not correspond to index in " + "m_persons vector. Person is copied with adapted Id"); + } + PersonId new_id{static_cast(m_persons.size())}; + m_persons.emplace_back(p, new_id); + if (p.get_location_model_id() == m_id) { + m_activeness_statuses.push_back(true); + } + else { + m_activeness_statuses.push_back(false); + } + } + } + + /** + * @brief Set the Person%s of the Model. + * @param[in] persons The Person%s of the Model. + */ + void set_persons(std::vector& persons) + { + m_is_local_population_cache_valid = false; + m_are_exposure_caches_valid = false; + //first clear old person vector and corresponding activeness vector + m_persons.clear(); + m_activeness_statuses.clear(); + for (auto& p : persons) { + if (p.get_id() != static_cast(m_persons.size())) { + mio::log_debug("In model.copy_persons_from_other_model: PersonId does not correspond to index in " + "m_persons vector. Person is copied with adapted Id"); + } + PersonId new_id{static_cast(m_persons.size())}; + m_persons.emplace_back(p, new_id); + if (p.get_location_model_id() == m_id) { + m_activeness_statuses.push_back(true); + } + else { + m_activeness_statuses.push_back(false); + } + } + } + + /** + * @brief Invalidate local population and exposure rate cache. + */ + void invalidate_cache() + { + m_are_exposure_caches_valid = false; + m_is_local_population_cache_valid = false; + } + private: /** * @brief Person%s interact at their Location and may become infected. @@ -485,8 +582,10 @@ class Model bool m_are_exposure_caches_valid = false; bool m_exposure_caches_need_rebuild = true; + int m_id; ///< Model id. Is only used for abm graph model or hybrid model. std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. + std::vector m_activeness_statuses; ///< Vector with activeness status for every person 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. diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index 00c49f7d3c..5dab0e3a25 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -134,7 +134,7 @@ bool change_location(Person& person, const Location& destination, const Transpor })); // make sure cell indices are valid if (person.get_location() != destination.get_id()) { - person.set_location(destination.get_type(), destination.get_id(), destination.get_world_id()); + person.set_location(destination.get_type(), destination.get_id(), destination.get_model_id()); person.get_cells() = cells; person.set_last_transport_mode(mode); diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 24b68c6d55..8777255c60 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -32,10 +32,10 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_world_id, AgeGroup age, PersonId person_id) + int location_model_id, AgeGroup age, PersonId person_id) : m_location(location_id) , m_location_type(location_type) - , m_location_world_id(location_world_id) + , m_location_model_id(location_model_id) , m_assigned_locations((uint32_t)LocationType::Count, LocationId::invalid_id()) , m_quarantine_start(TimePoint(-(std::numeric_limits::max() / 2))) , m_age(age) @@ -47,7 +47,7 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_person_id(person_id) , m_cells{0} , m_last_transport_mode(TransportMode::Unknown) - , m_assigned_location_world_ids((int)LocationType::Count) + , m_assigned_location_model_ids((int)LocationType::Count) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); @@ -94,11 +94,11 @@ LocationId Person::get_location() const return m_location; } -void Person::set_location(LocationType type, LocationId id, int world_id) +void Person::set_location(LocationType type, LocationId id, int model_id) { m_location = id; m_location_type = type; - m_location_world_id = world_id; + m_location_model_id = model_id; m_time_at_location = TimeSpan(0); } @@ -112,10 +112,10 @@ Infection& Person::get_infection() return m_infections.back(); } -void Person::set_assigned_location(LocationType type, LocationId id, int world_id) +void Person::set_assigned_location(LocationType type, LocationId id, int model_id) { m_assigned_locations[static_cast(type)] = id; - m_assigned_location_world_ids[static_cast(type)] = world_id; + m_assigned_location_model_ids[static_cast(type)] = model_id; } LocationId Person::get_assigned_location(LocationType type) const @@ -123,9 +123,9 @@ LocationId Person::get_assigned_location(LocationType type) const return m_assigned_locations[static_cast(type)]; } -int Person::get_assigned_location_world_id(LocationType type) const +int Person::get_assigned_location_model_id(LocationType type) const { - return m_assigned_location_world_ids[static_cast(type)]; + return m_assigned_location_model_ids[static_cast(type)]; } bool Person::goes_to_work(TimePoint t, const Parameters& params) const diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 1becb2fa2a..fa19945c95 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -56,7 +56,7 @@ class Person * @param[in] person_id Index of the Person. */ explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_world_id, AgeGroup age, PersonId person_id = PersonId::invalid_id()); + int location_model_id, AgeGroup age, PersonId person_id = PersonId::invalid_id()); explicit Person(const Person& other, PersonId id); @@ -131,18 +131,18 @@ class Person return m_location_type; } - int get_location_world_id() const + int get_location_model_id() const { - return m_location_world_id; + return m_location_model_id; } /** * @brief Change the location of the person. * @param[in] type The LocationType of the new Location. * @param[in] id The LocationId of the new Location. - * @param[in] world_id The world id of the new Location. + * @param[in] model_id The model id of the new Location. */ - void set_location(LocationType type, LocationId id, int world_id); + void set_location(LocationType type, LocationId id, int model_id); /** * @brief Get the time the Person has been at its current Location. @@ -181,9 +181,9 @@ class Person * Location of a certain #LocationType. * @param[in] type The LocationType of the Location. * @param[in] id The LocationId of the Location. - * @param[in] world_id The world id of the Location. + * @param[in] model_id The model id of the Location. */ - void set_assigned_location(LocationType type, LocationId id, int world_id); + void set_assigned_location(LocationType type, LocationId id, int model_id); /** * @brief Returns the index of an assigned Location of the Person. @@ -203,20 +203,20 @@ class Person } /** - * @brief Returns the world id of an assigned location of the Person. + * @brief Returns the model id of an assigned location of the Person. * Assume that a Person has at most one assigned Location of a certain #LocationType. * @param[in] type #LocationType of the assigned Location. - * @return The world id of the assigned Location. + * @return The model id of the assigned Location. */ - int get_assigned_location_world_id(LocationType type) const; + int get_assigned_location_model_id(LocationType type) const; /** - * @brief Get the assigned locations' world ids of the Person. - * @return A vector with the world ids of the assigned locations of the Person + * @brief Get the assigned locations' model ids of the Person. + * @return A vector with the model ids of the assigned locations of the Person */ - const std::vector& get_assigned_location_world_ids() const + const std::vector& get_assigned_location_model_ids() const { - return m_assigned_location_world_ids; + return m_assigned_location_model_ids; } /** @@ -293,7 +293,7 @@ class Person /** * @brief Set the PersonId of the Person. - * The PersonID should correspond to the index in m_persons in world. + * The PersonID should correspond to the index in m_persons in model. */ void set_id(PersonId id); @@ -461,7 +461,7 @@ class Person private: LocationId m_location; ///< Current Location of the Person. LocationType m_location_type; ///< Type of the current Location. - int m_location_world_id; ///< World id of the current Location. Only used for Graph ABM. + int m_location_model_id; ///< Model id of the current Location. Only used for Graph ABM. std::vector m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the Person always visits the same Home or School etc. */ std::vector m_vaccinations; ///< Vector with all Vaccination%s the Person has received. @@ -482,7 +482,7 @@ class Person 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 std::vector - m_assigned_location_world_ids; ///< Vector with world ids of the assigned locations. Only used in graph abm. + m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. }; } // namespace abm diff --git a/cpp/models/graph_abm/graph_abm_mobility.cpp b/cpp/models/graph_abm/graph_abm_mobility.cpp index 7acf3c2b64..af3fae0c89 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.cpp +++ b/cpp/models/graph_abm/graph_abm_mobility.cpp @@ -20,7 +20,7 @@ #include "graph_abm/graph_abm_mobility.h" #include "abm/simulation.h" -#include "abm/world.h" +#include "abm/model.h" #include "abm/person.h" #include "abm/location_type.h" #include "abm/parameters.h" diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index deef17d8f8..0c9c9a916d 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -217,41 +217,41 @@ class ABMMobilityEdge { // iterate over all persons that could commute via the edge for (auto p : m_parameters.get_commuting_persons()) { - auto& person_n1 = node_from.get_simulation().get_world().get_person(mio::abm::PersonId(p)); - auto& person_n2 = node_to.get_simulation().get_world().get_person(mio::abm::PersonId(p)); - auto& params = node_from.get_simulation().get_world().parameters; + auto& person_n1 = node_from.get_simulation().get_model().get_person(mio::abm::PersonId(p)); + auto& person_n2 = node_to.get_simulation().get_model().get_person(mio::abm::PersonId(p)); + auto& params = node_from.get_simulation().get_model().parameters; // as all nodes have all person it doesn't matter which node's persons we take here auto current_location_type = person_n1.get_location_type(); auto current_id = person_n1.get_location(); - auto current_world_id = person_n1.get_location_world_id(); + auto current_model_id = person_n1.get_location_model_id(); for (auto& rule : m_parameters.get_mobility_rules()) { auto target_type = rule(person_n1, t, params); - auto target_world_id = person_n1.get_assigned_location_world_id(target_type); + auto target_model_id = person_n1.get_assigned_location_model_id(target_type); auto target_id = person_n1.get_assigned_location(target_type); - auto& target_world = (target_world_id == node_from.get_simulation().get_world().get_id()) - ? node_from.get_simulation().get_world() - : node_to.get_simulation().get_world(); - auto& target_location = target_world.get_location(target_id); - assert((node_from.get_simulation().get_world().get_id() == target_location.get_world_id() || - node_to.get_simulation().get_world().get_id() == target_location.get_world_id()) && + auto& target_model = (target_model_id == node_from.get_simulation().get_model().get_id()) + ? node_from.get_simulation().get_model() + : node_to.get_simulation().get_model(); + auto& target_location = target_model.get_location(target_id); + assert((node_from.get_simulation().get_model().get_id() == target_location.get_model_id() || + node_to.get_simulation().get_model().get_id() == target_location.get_model_id()) && "Wrong graph edge. Target location is no edge node."); if (target_type == current_location_type && - (target_id != current_id || target_world_id != current_world_id)) { + (target_id != current_id || target_model_id != current_model_id)) { mio::log_error("Person with index {} has two assigned locations of the same type.", person_n1.get_id().get()); } if (target_type != current_location_type && - target_world.get_number_persons(target_id) < target_location.get_capacity().persons) { + target_model.get_number_persons(target_id) < target_location.get_capacity().persons) { //change person's location in all nodes - mio::abm::migrate(person_n1, target_location); - mio::abm::migrate(person_n2, target_location); - // invalidate both worlds' cache - node_to.get_simulation().get_world().invalidate_cache(); - node_from.get_simulation().get_world().invalidate_cache(); - if (target_world_id != current_world_id) { + mio::abm::change_location(person_n1, target_location); + mio::abm::change_location(person_n2, target_location); + // invalidate both models' cache + node_to.get_simulation().get_model().invalidate_cache(); + node_from.get_simulation().get_model().invalidate_cache(); + if (target_model_id != current_model_id) { // change activeness status for commuted person - node_to.get_simulation().get_world().change_activeness(p); - node_from.get_simulation().get_world().change_activeness(p); + node_to.get_simulation().get_model().change_activeness(p); + node_from.get_simulation().get_model().change_activeness(p); } // only one mobility rule per person can be applied break; diff --git a/cpp/models/graph_abm/mobility_rules.cpp b/cpp/models/graph_abm/mobility_rules.cpp index cab23286c8..b22e264aec 100644 --- a/cpp/models/graph_abm/mobility_rules.cpp +++ b/cpp/models/graph_abm/mobility_rules.cpp @@ -35,9 +35,9 @@ abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, c return abm::LocationType::Work; } - //person is at hospital in non-home world + //person is at hospital in non-home model if (current_loc == abm::LocationType::Hospital && - person.get_location_world_id() != person.get_assigned_location_world_id(abm::LocationType::Home)) { + person.get_location_model_id() != person.get_assigned_location_model_id(abm::LocationType::Home)) { //if person is still infected it stays at hospital if (person.is_infected(t)) { return current_loc; @@ -51,8 +51,8 @@ abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, c return abm::LocationType::Cemetery; } } - //person is at location in Home world (and should not go to work) it stays at that location - if (person.get_location_world_id() == person.get_assigned_location_world_id(abm::LocationType::Home)) { + //person is at location in Home model (and should not go to work) it stays at that location + if (person.get_location_model_id() == person.get_assigned_location_model_id(abm::LocationType::Home)) { return current_loc; } diff --git a/cpp/models/graph_abm/mobility_rules.h b/cpp/models/graph_abm/mobility_rules.h index 68a226ad8d..60078d10ab 100644 --- a/cpp/models/graph_abm/mobility_rules.h +++ b/cpp/models/graph_abm/mobility_rules.h @@ -33,7 +33,7 @@ namespace mio * @brief Once a day commuters go to work in another node. * @param[in] person Person the rule is applies to * @param[in] t Current time point - * @param[in] params Parameters of person's Home world + * @param[in] params Parameters of person's Home model * @return LocationType the person is going to */ abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, const abm::Parameters& params); diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index b5fed986c2..792152070c 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_type(), location.get_id(), location.get_world_id(), age); + mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_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 1121e05b64..1274ce4eca 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -178,7 +178,7 @@ TEST(TestInfection, getPersonalProtectiveFactor) auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); auto person = - mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_world_id(), age_group_15_to_34); + mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_model_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 ce84988b93..7281f191d2 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_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); - p1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - p1.set_assigned_location(school.get_type(), school.get_id(), school.get_world_id()); - auto p2 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); - p2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - p2.set_assigned_location(school.get_type(), school.get_id(), school.get_world_id()); + auto p1 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p1.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); + auto p2 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p2.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); 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_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - p.set_assigned_location(school.get_type(), school.get_id(), school.get_world_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); 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_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); - auto person2 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); - person1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - person1.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); - person2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - person2.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); + auto person1 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); + auto person2 = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); + person1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + person1.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); + person2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + person2.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); auto 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_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - p.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); 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, 1, num_age_groups); - auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - p.set_assigned_location(event.get_type(), event.get_id(), event.get_world_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p.set_assigned_location(event.get_type(), event.get_id(), event.get_model_id()); mio::abm::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, 1, num_age_groups); - auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); - p.set_assigned_location(event.get_type(), event.get_id(), event.get_world_id()); - p.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + auto p = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + p.set_assigned_location(event.get_type(), event.get_id(), event.get_model_id()); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); mio::abm::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 0f707bfc5e..4dbb683a20 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -66,9 +66,9 @@ TEST(TestMasks, maskProtection) 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_type(), infection_location.get_id(), - infection_location.get_world_id(), age_group_15_to_34); + infection_location.get_model_id(), age_group_15_to_34); auto susc_person2 = mio::abm::Person(rng, infection_location.get_type(), infection_location.get_id(), - infection_location.get_world_id(), age_group_15_to_34); + infection_location.get_model_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 diff --git a/cpp/tests/test_abm_mobility_rules.cpp b/cpp/tests/test_abm_mobility_rules.cpp index 35fafc778f..3e8448d418 100644 --- a/cpp/tests/test_abm_mobility_rules.cpp +++ b/cpp/tests/test_abm_mobility_rules.cpp @@ -76,8 +76,8 @@ TEST(TestMobilityRules, student_goes_to_school) .WillRepeatedly(testing::Return(1.0)); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - auto p_child = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); - auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + auto p_child = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(7); auto t_weekend = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(7); @@ -120,10 +120,10 @@ TEST(TestMobilityRules, 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.get_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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); @@ -178,10 +178,10 @@ TEST(TestMobilityRules, students_go_to_school_in_different_times_with_smaller_ti 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_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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_type(), home.get_id(), home.get_world_id(), age_group_5_to_14); + mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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); @@ -215,7 +215,7 @@ TEST(TestMobilityRules, 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_type(), school.get_id(), school.get_world_id(), age_group_5_to_14); + auto p_child = mio::abm::Person(rng, school.get_type(), school.get_id(), school.get_model_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); @@ -242,9 +242,9 @@ TEST(TestMobilityRules, worker_goes_to_work) .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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); @@ -282,9 +282,9 @@ TEST(TestMobilityRules, 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_type(), home.get_id(), home.get_world_id(), age_group_60_to_79); + auto p_retiree = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(rng, p_retiree); - auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + auto p_adult = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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); @@ -324,10 +324,10 @@ TEST(TestMobilityRules, 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_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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_type(), home.get_id(), home.get_world_id(), age_group_15_to_34); + mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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(TestMobilityRules, 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_type(), work.get_id(), work.get_world_id(), age_group_35_to_59); + auto p_adult = mio::abm::Person(rng, work.get_type(), work.get_id(), work.get_model_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(TestMobilityRules, 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_type(), home.get_id(), home.get_world_id(), age_group_60_to_79); + auto p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); auto rng_home = mio::abm::PersonalRandomNumberGenerator(rng, p_home); EXPECT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), @@ -467,10 +467,10 @@ TEST(TestMobilityRules, 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_type(), work.get_id(), work.get_world_id(), age_group_35_to_59); + auto p_work = mio::abm::Person(rng, work.get_type(), work.get_id(), work.get_model_id(), age_group_35_to_59); auto rng_work = mio::abm::PersonalRandomNumberGenerator(rng, p_work); 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(), home.get_world_id(), age_group_60_to_79); + auto p_home = mio::abm::Person(rng, home.get_type(), home.get_id(), home.get_model_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); @@ -503,7 +503,7 @@ TEST(TestMobilityRules, 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, social_event.get_type(), social_event.get_id(), social_event.get_world_id(), + auto p = mio::abm::Person(rng, social_event.get_type(), social_event.get_id(), social_event.get_model_id(), age_group_15_to_34); auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 8979c04da0..1b9396ae66 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -106,9 +106,9 @@ TEST(TestModel, findLocation) auto person_id = add_test_person(model, home_id); auto& person = model.get_person(person_id); - person.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - person.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); - person.set_assigned_location(mio::abm::LocationType::School, school_id, world.get_id()); + person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + person.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); EXPECT_EQ(model.find_location(mio::abm::LocationType::Work, person_id), work_id); EXPECT_EQ(model.find_location(mio::abm::LocationType::School, person_id), school_id); @@ -154,9 +154,9 @@ TEST(TestModel, evolveStateTransition) auto& p2 = model.get_persons()[1]; auto& p3 = model.get_persons()[2]; - p1.set_assigned_location(mio::abm::LocationType::School, location1, world.get_id()); - p2.set_assigned_location(mio::abm::LocationType::School, location1, world.get_id()); - p3.set_assigned_location(mio::abm::LocationType::Work, location2, world.get_id()); + p1.set_assigned_location(mio::abm::LocationType::School, location1, model.get_id()); + p2.set_assigned_location(mio::abm::LocationType::School, location1, model.get_id()); + p3.set_assigned_location(mio::abm::LocationType::Work, location2, model.get_id()); //setup mock so p2 becomes infected ScopedMockDistribution>>> @@ -213,12 +213,12 @@ TEST(TestModel, evolveMobility) auto& p1 = model.get_person(pid1); auto& p2 = model.get_person(pid2); - p1.set_assigned_location(mio::abm::LocationType::School, school_id, world.get_id()); - p2.set_assigned_location(mio::abm::LocationType::School, school_id, world.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p1.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); + p2.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); ScopedMockDistribution>>> mock_exponential_dist; @@ -280,19 +280,19 @@ TEST(TestModel, evolveMobility) auto& p4 = model.get_person(pid4); auto& p5 = model.get_person(pid5); - p1.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, world.get_id()); - p2.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, world.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); - p1.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - p2.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - p3.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - p4.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - p3.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, world.get_id()); - p4.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, world.get_id()); - p5.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, world.get_id()); - p5.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); - p5.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p1.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); + p2.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p1.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p2.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p3.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p4.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p3.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); + p4.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); + p5.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); + p5.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p5.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); mio::abm::TripList& data = model.get_trip_list(); mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id); @@ -387,12 +387,12 @@ TEST(TestModel, evolveMobility) auto& p_dead = model.get_persons()[0]; auto& p_severe = model.get_persons()[1]; - p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, world.get_id()); - p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); - p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, world.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, world.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); + p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); + p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); // Add trip to see if a dead person can change location outside of cemetery by scheduled trips mio::abm::TripList& trip_list = model.get_trip_list(); @@ -436,8 +436,8 @@ TEST(TestModelTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time); auto& person = model.get_person(pid); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); - person.set_assigned_location(mio::abm::LocationType::Home, home_id, world.get_id()); - person.set_assigned_location(mio::abm::LocationType::Work, work_id, world.get_id()); + person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); auto testing_criteria = mio::abm::TestingCriteria(); testing_criteria.add_infection_state(mio::abm::InfectionState::InfectedSymptoms); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 89b5a7c4db..e40d061f16 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -35,7 +35,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.get_type(), location.get_id(), location.get_world_id(), age_group_60_to_79); + mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_model_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()); @@ -83,8 +83,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.get_type(), location.get_id(), location.get_world_id(), age_group_35_to_59); - person.set_assigned_location(location.get_type(), location.get_id(), location.get_world_id()); + mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_model_id(), age_group_35_to_59); + person.set_assigned_location(location.get_type(), location.get_id(), location.get_model_id()); EXPECT_EQ(person.get_assigned_location(mio::abm::LocationType::Work), mio::abm::LocationId(2)); person.set_assigned_location(mio::abm::LocationType::Work, mio::abm::LocationId(4), 0); @@ -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.get_type(), loc.get_id(), loc.get_world_id(), age_group_15_to_34); + auto susceptible = mio::abm::Person(rng, loc.get_type(), loc.get_id(), loc.get_model_id(), age_group_15_to_34); auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(rng, susceptible); auto pcr_parameters = params.get()[mio::abm::TestType::PCR]; @@ -206,7 +206,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_type(), loc.get_id(), loc.get_world_id(), age_group_15_to_34); + auto person = mio::abm::Person(rng, loc.get_type(), loc.get_id(), loc.get_model_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 +292,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_type(), location.get_id(), location.get_world_id(), age_group_15_to_34); + mio::abm::Person(rng, location.get_type(), location.get_id(), location.get_model_id(), age_group_15_to_34); auto prng = mio::abm::PersonalRandomNumberGenerator(rng, person); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 8d26e21648..2fa6a1725d 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -18,7 +18,7 @@ * limitations under the License. */ -#include "abm/world.h" +#include "abm/model.h" #include "abm/location_type.h" #include "abm/time.h" #include "graph_abm/graph_abm_mobility.h" @@ -29,35 +29,35 @@ TEST(TestGraphAbm, test_activessness) { - auto world = mio::abm::World(size_t(1)); - world.parameters.get()[mio::AgeGroup(0)] = true; - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto home_id = world.add_location(mio::abm::LocationType::Home); - auto& home = world.get_location(home_id); - auto& work = world.get_location(work_id); - auto p1_id = world.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = world.add_person(home_id, mio::AgeGroup(0)); - auto& p1 = world.get_person(p1_id); - auto& p2 = world.get_person(p2_id); - p1.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); - p2.set_assigned_location(work.get_type(), work.get_id(), work.get_world_id()); - p1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - p2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); + auto model = mio::abm::Model(size_t(1)); + model.parameters.get()[mio::AgeGroup(0)] = true; + auto work_id = model.add_location(mio::abm::LocationType::Work); + auto home_id = model.add_location(mio::abm::LocationType::Home); + auto& home = model.get_location(home_id); + auto& work = model.get_location(work_id); + auto p1_id = model.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model.add_person(home_id, mio::AgeGroup(0)); + auto& p1 = model.get_person(p1_id); + auto& p2 = model.get_person(p2_id); + p1.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); + p2.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); + p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); EXPECT_EQ(p1.get_location(), home_id); EXPECT_EQ(p2.get_location(), home_id); - EXPECT_EQ(p1.get_location_world_id(), world.get_id()); - EXPECT_EQ(p2.get_location_world_id(), world.get_id()); - EXPECT_EQ(world.get_activeness_statuses().size(), 2); + EXPECT_EQ(p1.get_location_model_id(), model.get_id()); + EXPECT_EQ(p2.get_location_model_id(), model.get_id()); + EXPECT_EQ(model.get_activeness_statuses().size(), 2); - world.change_activeness(p1.get_id()); - EXPECT_EQ(world.get_activeness_statuses()[p1.get_id().get()], false); - EXPECT_EQ(world.get_activeness_statuses()[p2.get_id().get()], true); + model.change_activeness(p1.get_id()); + EXPECT_EQ(model.get_activeness_statuses()[p1.get_id().get()], false); + EXPECT_EQ(model.get_activeness_statuses()[p2.get_id().get()], true); auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); auto dt = mio::abm::hours(3); - world.evolve(t, dt); + model.evolve(t, dt); //inactive persons do not move EXPECT_EQ(p1.get_location(), home_id); @@ -85,51 +85,51 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { - auto world_1 = mio::abm::World(size_t(1), 1); - auto world_2 = mio::abm::World(size_t(1), 2); - world_1.parameters.get()[mio::AgeGroup(0)] = true; - world_2.parameters.get()[mio::AgeGroup(0)] = true; - auto work_id_1 = world_1.add_location(mio::abm::LocationType::Work); - auto home_id = world_1.add_location(mio::abm::LocationType::Home); - auto work_id_2 = world_2.add_location(mio::abm::LocationType::Work); - auto& work_1 = world_1.get_location(work_id_1); - auto& work_2 = world_2.get_location(work_id_2); - auto& home = world_1.get_location(home_id); - - EXPECT_EQ(work_1.get_world_id(), 1); - EXPECT_EQ(work_2.get_world_id(), 2); - - auto p1_id = world_1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = world_1.add_person(home_id, mio::AgeGroup(0)); - auto& p1 = world_1.get_person(p1_id); - auto& p2 = world_1.get_person(p2_id); - p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_world_id()); - p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_world_id()); - p1.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - p2.set_assigned_location(home.get_type(), home.get_id(), home.get_world_id()); - - //copy persons to world 2 - world_2.copy_persons_from_other_world(world_1); + auto model_1 = mio::abm::Model(size_t(1), 1); + auto model_2 = mio::abm::Model(size_t(1), 2); + model_1.parameters.get()[mio::AgeGroup(0)] = true; + model_2.parameters.get()[mio::AgeGroup(0)] = true; + auto work_id_1 = model_1.add_location(mio::abm::LocationType::Work); + auto home_id = model_1.add_location(mio::abm::LocationType::Home); + auto work_id_2 = model_2.add_location(mio::abm::LocationType::Work); + auto& work_1 = model_1.get_location(work_id_1); + auto& work_2 = model_2.get_location(work_id_2); + auto& home = model_1.get_location(home_id); + + EXPECT_EQ(work_1.get_model_id(), 1); + EXPECT_EQ(work_2.get_model_id(), 2); + + auto p1_id = model_1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model_1.add_person(home_id, mio::AgeGroup(0)); + auto& p1 = model_1.get_person(p1_id); + auto& p2 = model_1.get_person(p2_id); + p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); + p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); + p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + + //copy persons to model 2 + model_2.copy_persons_from_other_model(model_1); auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); - mio::ABMSimulationNode node1(MockHistory{}, t, std::move(world_1)); - mio::ABMSimulationNode node2(MockHistory{}, t, std::move(world_2)); + mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model_1)); + mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model_2)); mio::ABMMobilityEdge edge({p2.get_id().get()}, {&mio::apply_commuting}); edge.apply_mobility(node1, node2, t); - EXPECT_EQ(node2.get_simulation().get_world().get_number_persons(work_id_2), 1); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], true); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], false); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], false); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], true); + EXPECT_EQ(node2.get_simulation().get_model().get_number_persons(work_id_2), 1); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], true); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], false); + EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], false); + EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], true); //return home t += mio::abm::hours(12); edge.apply_mobility(node1, node2, t); - EXPECT_EQ(node2.get_simulation().get_world().get_number_persons(work_id_2), 0); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], true); - EXPECT_EQ(node1.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], true); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p1.get_id().get()], false); - EXPECT_EQ(node2.get_simulation().get_world().get_activeness_statuses()[p2.get_id().get()], false); + EXPECT_EQ(node2.get_simulation().get_model().get_number_persons(work_id_2), 0); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], true); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], true); + EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], false); + EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], false); } \ No newline at end of file From 5ef826d30346c410197e3d8bd0631983ac06dfab Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 5 Aug 2024 10:23:45 +0200 Subject: [PATCH 025/111] [ci skip] model wrapper --- cpp/examples/CMakeLists.txt | 8 +- cpp/models/abm/model.h | 2 +- cpp/models/abm/person_id.h | 8 +- cpp/models/abm/trip_list.h | 38 ++++-- cpp/models/graph_abm/CMakeLists.txt | 2 + cpp/models/graph_abm/model_wrapper.cpp | 26 ++++ cpp/models/graph_abm/model_wrapper.h | 164 +++++++++++++++++++++++++ cpp/tests/CMakeLists.txt | 3 +- 8 files changed, 228 insertions(+), 23 deletions(-) create mode 100644 cpp/models/graph_abm/model_wrapper.cpp create mode 100644 cpp/models/graph_abm/model_wrapper.h diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index b3a74364dd..795f369dea 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -76,10 +76,9 @@ add_executable(ode_secir_ageres_example ode_secir_ageres.cpp) target_link_libraries(ode_secir_ageres_example PRIVATE memilio ode_secir) target_compile_options(ode_secir_ageres_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -add_executable(graph_example graph.cpp) -target_link_libraries(graph_example PRIVATE memilio ode_seir) -target_compile_options(graph_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - +# add_executable(graph_example graph.cpp) +# target_link_libraries(graph_example PRIVATE memilio ode_seir) +# target_compile_options(graph_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(graph_stochastic_mobility_example graph_stochastic_mobility.cpp) target_link_libraries(graph_stochastic_mobility_example PRIVATE memilio ode_secir) target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) @@ -98,7 +97,6 @@ target_link_libraries(twitter_mobility_example PRIVATE memilio ode_secir) target_compile_options(twitter_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - add_executable(ide_seir_example ide_seir.cpp) target_link_libraries(ide_seir_example PRIVATE memilio ide_seir) target_compile_options(ide_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 948db7c2d8..5b98b999fe 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -545,7 +545,7 @@ class Model m_is_local_population_cache_valid = false; } -private: +protected: /** * @brief Person%s interact at their Location and may become infected. * @param[in] t The current TimePoint. diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index 9f65193535..e6eaa019b9 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -30,16 +30,16 @@ namespace abm { /// Unique identifier for a Person within a Model. -struct MEMILIO_ENABLE_EBO 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) + PersonId(uint64_t id) + : mio::TypeSafe(id) { } /// @brief Create an invalid ID. PersonId() - : mio::TypeSafe(std::numeric_limits::max()) + : mio::TypeSafe(std::numeric_limits::max()) { } diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index f20db0df29..3181123f4b 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -24,6 +24,7 @@ #include "abm/mobility_data.h" #include "abm/person_id.h" #include "abm/time.h" +#include "abm/location_type.h" #include namespace mio @@ -35,48 +36,61 @@ namespace abm * @brief A trip describes a change of Location from one Location to another Location. */ struct Trip { + //TODO: Origin is currently not used for the trips. Should we delete it then? PersonId person_id; /**< Person that makes the trip and corresponds to the index into the structure m_persons from Model, where all Person%s are saved.*/ - TimePoint time; ///< Time at which a Person changes the Location. + TimePoint time; ///< Daytime at which a Person changes the Location. LocationId destination; ///< Location where the Person changes to. + int destination_model_id; ///< Model id of destination Location. LocationId origin; ///< Location where the Person starts the Trip. + int origin_model_id; ///< Model id of origin Location. std::vector cells; /**< If destination consists of different Cell%s, this gives the index of the Cell%s the Person changes to.*/ TransportMode trip_mode; ///< Mode of transportation. 1:Bike, 2:Car (Driver), 3:Car (Co-Driver)), 4:Public Transport, 5:Walking, 6:Other/Unknown - ActivityType - activity_type; ///< Type of activity. 1:Workplace, 2:Education, 3:Shopping, 4:Leisure, 5:Private Matters, 6:Other Activity, 7:Home, 8:Unknown Activity + LocationType destination_type; ///< Type of destination Location. /** * @brief Construct a new Trip. * @param[in] id ID of the Person that makes the Trip. * @param[in] time_new Time at which a Person changes the Location this currently cant be set for s specific day just a timepoint in a day. * @param[in] destination Location where the Person changes to. + * @param[in] destination_model_id Model the Person changes to. * @param[in] origin Location where the person starts the Trip. + * @param[in] origin_model_id Model the Person starts the Trip. * @param[in] input_cells The index of the Cell%s the Person changes to. */ - Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, - ActivityType type_of_activity, const std::vector& input_cells = {}) + Trip(PersonId id, TimePoint time_new, LocationId dest, int dest_model_id, LocationId orig, int orig_model_id, + TransportMode mode_of_transport, LocationType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) , destination(dest) + , destination_model_id(dest_model_id) , origin(orig) + , origin_model_id(orig_model_id) , cells(input_cells) , trip_mode(mode_of_transport) - , activity_type(type_of_activity) + , destination_type(type_of_activity) { } - Trip(PersonId id, TimePoint time_new, LocationId dest, const std::vector& input_cells = {}) - : Trip(id, time_new, dest, dest, mio::abm::TransportMode::Unknown, mio::abm::ActivityType::UnknownActivity, - input_cells) + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, + LocationType type_of_activity, const std::vector& input_cells = {}) + : person_id(id) + , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) + , destination(dest) + , destination_model_id(0) + , origin(orig) + , origin_model_id(0) + , cells(input_cells) + , trip_mode(mode_of_transport) + , destination_type(type_of_activity) { } - Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, const std::vector& input_cells = {}) - : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, mio::abm::ActivityType::UnknownActivity, - input_cells) + : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { } diff --git a/cpp/models/graph_abm/CMakeLists.txt b/cpp/models/graph_abm/CMakeLists.txt index 866bc85b29..d21b2cb4ea 100644 --- a/cpp/models/graph_abm/CMakeLists.txt +++ b/cpp/models/graph_abm/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(graph_abm + model_wrapper.h + model_wrapper.cpp graph_abm_mobility.cpp graph_abm_mobility.h mobility_rules.cpp diff --git a/cpp/models/graph_abm/model_wrapper.cpp b/cpp/models/graph_abm/model_wrapper.cpp new file mode 100644 index 0000000000..65858c2f63 --- /dev/null +++ b/cpp/models/graph_abm/model_wrapper.cpp @@ -0,0 +1,26 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "graph_abm/model_wrapper.h" + +namespace mio +{ + +} //namespace mio \ No newline at end of file diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h new file mode 100644 index 0000000000..9f2dd17d5c --- /dev/null +++ b/cpp/models/graph_abm/model_wrapper.h @@ -0,0 +1,164 @@ +/* +* Copyright (C) 2020-2024 MEmilio +* +* Authors: Julia Bicker +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef MIO_ABM_MODEL_WRAPPER_H +#define MIO_ABM_MODEL_WRAPPER_H + +#include "abm/model.h" +#include "abm/time.h" +#include "abm/location_id.h" +#include "memilio/utils/mioomp.h" +#include "abm/mobility_rules.h" +#include + +namespace mio +{ +using namespace abm; +class ModelWrapper : public abm::Model +{ + using Model::Model; + using Base = Model; + +public: + /** + * @brief Get a reference to a Person from this Model. + * @param[in] id A person's PersonId. + * First 32 bit are the Person's index and second 32 bit the Persons's home model id. + * @return Position of Person in m_persons vector. + * @{ + */ + size_t get_person_pos(PersonId id) + { + auto it = std::find(Base::m_persons.begin(), Base::m_persons.end(), [id](auto& person) { + person.get_id() == id; + }); + if (it == Base::m_persons.end()) { + log_error("Given PersonId is not in this Model."); + return std::numeric_limits::max(); + } + else { + return std::distance(Base::m_persons.begin(), it); + } + } + +private: + void perform_mobility(TimePoint t, TimeSpan dt) + { + const uint32_t num_persons = static_cast(Base::m_persons.size()); + PRAGMA_OMP(parallel for) + for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { + if (Base::m_activeness_statuses[person_id]) { + Person& person = Base::m_persons[person_id]; + auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); + + auto try_mobility_rule = [&](auto rule) -> bool { + //run mobility rule and check if change of location can actually happen + auto target_type = rule(personal_rng, person, t, dt, parameters); + if (person.get_assigned_location_model_id(target_type) == Base::m_id) { + const Location& target_location = Base::get_location(Base::find_location(target_type, person_id)); + const LocationId current_location = person.get_location(); + if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + if (target_location.get_id() != current_location && + Base::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) { + Base::change_location(person_id, target_location.get_id()); + } + return true; + } + } + } + else { //person moves to other world + Base::m_activeness_statuses[person_id] = false; + person.set_location(target_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); + person_buffer.push_back(person_id); + return true; + } + return false; + }; + + //run mobility rules one after the other if the corresponding location type exists + //shortcutting of bool operators ensures the rules stop after the first rule is applied + if (m_use_mobility_rules) { + (Base::has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || + (Base::has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || + (Base::has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || + (Base::has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || + (Base::has_locations({LocationType::School, LocationType::Home}) && + try_mobility_rule(&go_to_school)) || + (Base::has_locations({LocationType::Work, LocationType::Home}) && try_mobility_rule(&go_to_work)) || + (Base::has_locations({LocationType::BasicsShop, LocationType::Home}) && + try_mobility_rule(&go_to_shop)) || + (Base::has_locations({LocationType::SocialEvent, LocationType::Home}) && + try_mobility_rule(&go_to_event)) || + (Base::has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); + } + else { + //no daily routine mobility, just infection related + (Base::has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || + (Base::has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || + (Base::has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || + (Base::has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || + (Base::has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); + } + } + } + + // check if a person makes a trip + bool weekend = t.is_weekend(); + size_t num_trips = m_trip_list.num_trips(weekend); + + if (num_trips != 0) { + 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_pos = get_person_pos(trip.person_id); + auto& person = Base::get_person(person_pos); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { + if (trip.destination_model_id == Base::m_id) { + auto& target_location = get_location(trip.destination); + if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + person.apply_mask_intervention(personal_rng, target_location); + change_location(person.get_id(), target_location.get_id(), trip.trip_mode); + } + } + else { + //person moves to other world + Base::m_activeness_statuses[person_pos] = false; + person.set_location(trip.destination_type, abm::LocationId::invalid_id(), + std::numeric_limits::max()); + person_buffer.push_back(person_pos); + } + } + m_trip_list.increase_index(); + } + } + if (((t).days() < std::floor((t + dt).days()))) { + m_trip_list.reset_index(); + } + } + + std::list person_buffer; ///< List with indices of persons that are deactivated. +}; +} // namespace mio + +#endif //MIO_ABM_MODEL_WRAPPER_H \ No newline at end of file diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 90106bc870..039e0199c7 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -72,7 +72,8 @@ set(TESTSOURCES test_lct_secir.cpp test_lct_initializer_flows.cpp test_ad.cpp - test_graph_abm.cpp + + # test_graph_abm.cpp ) if(MEMILIO_HAS_JSONCPP) From 8ec2fcfdcf7a2e4ff85526c6f0ab2b0393ec1e4b Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 10:17:12 +0200 Subject: [PATCH 026/111] add tests --- cpp/benchmarks/abm.cpp | 2 +- cpp/examples/CMakeLists.txt | 14 +- cpp/examples/abm_history_object.cpp | 16 +- cpp/examples/abm_minimal.cpp | 2 +- cpp/models/abm/common_abm_loggers.h | 16 +- cpp/models/abm/household.cpp | 2 +- cpp/models/abm/model.cpp | 33 +-- cpp/models/abm/model.h | 149 ++++++++----- cpp/models/abm/simulation.cpp | 14 -- cpp/models/abm/simulation.h | 23 +- cpp/models/abm/trip_list.h | 19 +- cpp/models/graph_abm/graph_abm_mobility.h | 260 ++++++++++------------ cpp/models/graph_abm/model_wrapper.h | 98 ++++---- cpp/simulations/abm.cpp | 2 +- cpp/simulations/abm_braunschweig.cpp | 19 +- cpp/tests/CMakeLists.txt | 3 +- cpp/tests/test_abm_location.cpp | 17 +- cpp/tests/test_abm_model.cpp | 74 +++--- cpp/tests/test_abm_simulation.cpp | 58 ++--- cpp/tests/test_graph_abm.cpp | 127 +++++------ cpp/tests/test_json_serializer.cpp | 13 +- 21 files changed, 501 insertions(+), 460 deletions(-) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 28b59486a3..47b9870f38 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -3,7 +3,7 @@ #include "benchmark/benchmark.h" #include -mio::abm::Simulation make_simulation(size_t num_persons, std::initializer_list seeds) +mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list seeds) { auto rng = mio::RandomNumberGenerator(); rng.seed(seeds); diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 795f369dea..d5397d00b7 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -76,9 +76,10 @@ add_executable(ode_secir_ageres_example ode_secir_ageres.cpp) target_link_libraries(ode_secir_ageres_example PRIVATE memilio ode_secir) target_compile_options(ode_secir_ageres_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -# add_executable(graph_example graph.cpp) -# target_link_libraries(graph_example PRIVATE memilio ode_seir) -# target_compile_options(graph_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(graph_example graph.cpp) +target_link_libraries(graph_example PRIVATE memilio ode_seir) +target_compile_options(graph_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + add_executable(graph_stochastic_mobility_example graph_stochastic_mobility.cpp) target_link_libraries(graph_stochastic_mobility_example PRIVATE memilio ode_secir) target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) @@ -113,10 +114,9 @@ add_executable(history_example history.cpp) target_link_libraries(history_example PRIVATE memilio) target_compile_options(history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -add_executable(graph_abm_example graph_abm.cpp) -target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) -target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - +# add_executable(graph_abm_example graph_abm.cpp) +# target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) +# target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) if(MEMILIO_HAS_JSONCPP) add_executable(ode_secir_read_graph_example ode_secir_read_graph.cpp) target_link_libraries(ode_secir_read_graph_example PRIVATE memilio ode_secir) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 2c04cba986..cef922f492 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -152,17 +152,17 @@ int main() for (auto& person : model.get_persons()) { const auto pid = person.get_id(); //assign shop and event - model.assign_location(pid, event); - model.assign_location(pid, shop); + model.assign_location(model.get_person_index(pid), event); + model.assign_location(model.get_person_index(pid), shop); //assign hospital and ICU - model.assign_location(pid, hospital); - model.assign_location(pid, icu); + model.assign_location(model.get_person_index(pid), hospital); + model.assign_location(model.get_person_index(pid), icu); //assign work/school to people depending on their age if (person.get_age() == age_group_5_to_14) { - model.assign_location(pid, school); + model.assign_location(model.get_person_index(pid), school); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - model.assign_location(pid, work); + model.assign_location(model.get_person_index(pid), work); } } @@ -176,14 +176,14 @@ int main() struct LogTimePoint : mio::LogAlways { using Type = double; - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation<>& sim) { return sim.get_time().hours(); } }; struct LogLocationIds : mio::LogOnce { using Type = std::vector>; - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation<>& sim) { Type location_ids{}; for (auto& location : sim.get_model().get_locations()) { diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 1c29370999..e83b450df7 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -130,7 +130,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto id = person.get_id(); + const auto id = model.get_person_index(person.get_id()); //assign shop and event model.assign_location(id, event); model.assign_location(id, shop); diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index aa35b9ce8a..aec3e2e202 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -91,7 +91,7 @@ struct LogLocationInformation : mio::LogOnce { * -# The number of cells in the location. * -# The capacity of the location. */ - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation<>& sim) { Type location_information{}; for (auto& location : sim.get_model().get_locations()) { @@ -120,14 +120,16 @@ struct LogPersonInformation : mio::LogOnce { * -# The index of the home location. * -# The age group of the person. */ - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation<>& sim) { Type person_information{}; person_information.reserve(sim.get_model().get_persons().size()); for (auto& person : sim.get_model().get_persons()) { - person_information.push_back(std::make_tuple( - person.get_id(), sim.get_model().find_location(mio::abm::LocationType::Home, person.get_id()), - person.get_age())); + person_information.push_back( + std::make_tuple(person.get_id(), + sim.get_model().find_location(mio::abm::LocationType::Home, + sim.get_model().get_person_index(person.get_id())), + person.get_age())); } return person_information; } @@ -150,7 +152,7 @@ struct LogDataForMobility : mio::LogAlways { * -# The activity type. * -# The infection state. */ - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation<>& sim) { Type mobility_data{}; for (Person p : sim.get_model().get_persons()) { @@ -172,7 +174,7 @@ struct LogInfectionState : mio::LogAlways { * @param[in] sim The simulation of the abm. * @return A pair of the TimePoint and the TimeSeries of the number of Person%s in an #InfectionState. */ - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation<>& sim) { Eigen::VectorXd sum = Eigen::VectorXd::Zero(Eigen::Index(mio::abm::InfectionState::Count)); diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index db857b1f34..4a235168ed 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -70,7 +70,7 @@ void add_household_to_model(Model& model, const Household& household) for (int j = 0; j < count; j++) { auto age_group = pick_age_group_from_age_distribution(model.get_rng(), member.get_age_weights()); auto person = model.add_person(home, age_group); - model.assign_location(person, home); + model.assign_location(model.get_person_index(person), home); } } } diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 503d22a0bd..ce0dbb32fa 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -60,8 +60,8 @@ PersonId Model::add_person(Person&& person) assert(person.get_location() < LocationId((uint32_t)m_locations.size()) && "Added Person's location is not in Model."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); - - PersonId new_id{static_cast(m_persons.size())}; + uint64_t id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); + PersonId new_id{id}; m_persons.emplace_back(person, new_id); m_activeness_statuses.push_back(true); auto& new_person = m_persons.back(); @@ -86,9 +86,9 @@ void Model::interaction(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { - if (m_activeness_statuses[person_id]) { - interact(person_id, t, dt); + for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { + if (m_activeness_statuses[person_index]) { + interact(person_index, t, dt); } } } @@ -97,23 +97,23 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { - if (m_activeness_statuses[person_id]) { - Person& person = m_persons[person_id]; + for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { + if (m_activeness_statuses[person_index]) { + Person& person = m_persons[person_index]; auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); auto try_mobility_rule = [&](auto rule) -> bool { //run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); if (person.get_assigned_location_model_id(target_type) == m_id) { - const Location& target_location = get_location(find_location(target_type, person_id)); + const Location& target_location = get_location(find_location(target_type, person_index)); 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) { - change_location(person_id, target_location.get_id()); + change_location(person_index, target_location.get_id()); } return true; } @@ -155,13 +155,14 @@ void Model::perform_mobility(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 = get_person(trip.person_id); + auto& person = get_person(static_cast(trip.person_id.get())); 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_location(trip.destination); if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); - change_location(person.get_id(), target_location.get_id(), trip.trip_mode); + change_location(static_cast(trip.person_id.get()), target_location.get_id(), + trip.trip_mode); } } m_trip_list.increase_index(); @@ -245,8 +246,8 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) const auto location = person.get_location().get(); if (person.get_location_model_id() == m_id) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], - m_contact_exposure_rates_cache[location], person, - get_location(person.get_id()), t, dt); + m_contact_exposure_rates_cache[location], person, get_location(i), + t, dt); } } // implicit taskloop barrier } // implicit single barrier @@ -283,9 +284,9 @@ auto Model::get_persons() -> Range> return std::make_pair(m_persons.begin(), m_persons.end()); } -LocationId Model::find_location(LocationType type, const PersonId person) const +LocationId Model::find_location(LocationType type, const uint32_t person_index) const { - auto location_id = get_person(person).get_assigned_location(type); + auto location_id = get_person(person_index).get_assigned_location(type); assert(location_id != LocationId::invalid_id() && "The person has no assigned location of that type."); return location_id; } diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 5b98b999fe..5f4ad2f7cf 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -36,6 +36,7 @@ #include "memilio/utils/stl_util.h" #include +#include #include namespace mio @@ -213,21 +214,21 @@ class Model /** * @brief Find an assigned Location of a Person. * @param[in] type The #LocationType that specifies the assigned Location. - * @param[in] person PersonId of the Person. + * @param[in] person_index Index of the Person. * @return ID of the Location of LocationType type assigend to person. */ - LocationId find_location(LocationType type, const PersonId person) const; + LocationId find_location(LocationType type, const uint32_t person_index) const; /** * @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] person_index The Index of the person this location will be assigned to. * @param[in] location The LocationId of the Location. */ - void assign_location(PersonId person, LocationId location) + void assign_location(uint32_t person_index, LocationId location) { - get_person(person).set_assigned_location(get_location(location).get_type(), location, m_id); + get_person(person_index).set_assigned_location(get_location(location).get_type(), location, m_id); } /** @@ -344,22 +345,48 @@ class Model /** * @brief Get a reference to a Person from this Model. - * @param[in] id A person's PersonId. + * @param[in] index A Person's index in m_persons. * @return A reference to the Person. * @{ */ + Person& get_person(uint32_t index) + { + assert(index < m_persons.size() && "Given PersonId is not in this Model."); + return m_persons[index]; + } + + const Person& get_person(uint32_t index) const + { + assert(index < m_persons.size() && "Given PersonId is not in this Model."); + return m_persons[index]; + } + + /** + * @brief Get a reference to a Person from this Model. + * @param[in] id A Person's id. + * @return A reference to the Person. + */ Person& get_person(PersonId id) { - assert(id.get() < m_persons.size() && "Given PersonId is not in this Model."); - return m_persons[id.get()]; + auto it = std::find_if(m_persons.begin(), m_persons.end(), [id](auto& person) { + return person.get_id() == id; + }); + if (it == m_persons.end()) { + log_error("Given PersonId is not in this Model."); + } + return *it; } const Person& get_person(PersonId id) const { - assert(id.get() < m_persons.size() && "Given PersonId is not in this Model."); - return m_persons[id.get()]; + auto it = std::find_if(m_persons.begin(), m_persons.end(), [id](auto& person) { + return person.get_id() == id; + }); + if (it == m_persons.end()) { + log_error("Given PersonId is not in this Model."); + } + return *it; } - /** @} */ /** * @brief Get the number of Person%s of a particular #InfectionState for all Cell%s. @@ -392,17 +419,17 @@ class Model // Change the Location of a Person. this requires that Location is part of this Model. /** * @brief Let a Person change to another Location. - * @param[in] person PersonId of a Person from this Model. + * @param[in] person_index Index of a Person in m_persons vector of this Model. * @param[in] destination LocationId of the Location in this Model, which the Person should change to. * @param[in] mode The transport mode the person uses to change the Location. * @param[in] cells The cells within the destination the person should be in. */ - inline void change_location(PersonId person, LocationId destination, TransportMode mode = TransportMode::Unknown, - const std::vector& cells = {0}) + inline void change_location(uint32_t person_index, LocationId destination, + TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { - LocationId origin = get_location(person).get_id(); + LocationId origin = get_location(person_index).get_id(); const bool has_changed_location = - mio::abm::change_location(get_person(person), get_location(destination), mode, cells); + mio::abm::change_location(get_person(person_index), get_location(destination), mode, cells); // if the person has changed location, invalidate exposure caches but keep population caches valid if (has_changed_location) { m_are_exposure_caches_valid = false; @@ -415,11 +442,11 @@ class Model /** * @brief Let a person interact with the population at its current location. - * @param[in] person PersonId of a person from this Model. + * @param[in] person_index Index of a person in m_persons vector of this Model. * @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) + inline void interact(uint32_t person_index, TimePoint t, TimeSpan dt) { if (!m_are_exposure_caches_valid) { // checking caches is only needed for external calls @@ -427,10 +454,11 @@ class Model 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_id().get()], - m_contact_exposure_rates_cache[get_location(person).get_id().get()], t, dt, parameters); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person_index)); + mio::abm::interact(personal_rng, get_person(person_index), get_location(person_index), + m_air_exposure_rates_cache[get_location(person_index).get_id().get()], + m_contact_exposure_rates_cache[get_location(person_index).get_id().get()], t, dt, + parameters); } /** @@ -456,18 +484,18 @@ class Model /** * @brief Get a reference to the location of a person. - * @param[in] id PersonId of a person. + * @param[in] index Index of a person in m_persons. * @return Reference to the Location. * @{ */ - inline Location& get_location(PersonId id) + inline Location& get_location(uint32_t index) { - return get_location(get_person(id).get_location()); + return get_location(get_person(index).get_location()); } - inline const Location& get_location(PersonId id) const + inline const Location& get_location(uint32_t index) const { - return get_location(get_person(id).get_location()); + return get_location(get_person(index).get_location()); } /** @} */ @@ -485,29 +513,29 @@ class Model m_activeness_statuses[person_id.get()] = false; } - /** - * @brief Copy the persons from another Model to this Model. - * If the persons are at a location in this model they are activated, otherwise they are deactivated. - * If necessary the person ids are changed such that they correspond to the index in this model's m_persons vector. - * @param[in] other The Model the Person%s are copied from. - */ - void copy_persons_from_other_model(const Model& other) - { - for (auto& p : other.get_persons()) { - if (p.get_id() != static_cast(m_persons.size())) { - mio::log_debug("In model.copy_persons_from_other_model: PersonId does not correspond to index in " - "m_persons vector. Person is copied with adapted Id"); - } - PersonId new_id{static_cast(m_persons.size())}; - m_persons.emplace_back(p, new_id); - if (p.get_location_model_id() == m_id) { - m_activeness_statuses.push_back(true); - } - else { - m_activeness_statuses.push_back(false); - } - } - } + // /** + // * @brief Copy the persons from another Model to this Model. + // * If the persons are at a location in this model they are activated, otherwise they are deactivated. + // * If necessary the person ids are changed such that they correspond to the index in this model's m_persons vector. + // * @param[in] other The Model the Person%s are copied from. + // */ + // void copy_persons_from_other_model(const Model& other) + // { + // for (auto& p : other.get_persons()) { + // if (p.get_id() != static_cast(m_persons.size())) { + // mio::log_debug("In model.copy_persons_from_other_model: PersonId does not correspond to index in " + // "m_persons vector. Person is copied with adapted Id"); + // } + // PersonId new_id{static_cast(m_persons.size())}; + // m_persons.emplace_back(p, new_id); + // if (p.get_location_model_id() == m_id) { + // m_activeness_statuses.push_back(true); + // } + // else { + // m_activeness_statuses.push_back(false); + // } + // } + // } /** * @brief Set the Person%s of the Model. @@ -545,6 +573,27 @@ class Model m_is_local_population_cache_valid = false; } + /** + * @brief Get index of person in m_persons. + * @param[in] id A person's PersonId. + * First 32 bit are the Person's individual id and second 32 bit the Persons's home model id. + * @return Index of Person in m_persons vector. + * @{ + */ + uint32_t get_person_index(PersonId id) const + { + auto it = std::find_if(m_persons.begin(), m_persons.end(), [id](auto& person) { + return person.get_id() == id; + }); + if (it == m_persons.end()) { + log_error("Given PersonId is not in this Model."); + return std::numeric_limits::max(); + } + else { + return std::distance(m_persons.begin(), it); + } + } + protected: /** * @brief Person%s interact at their Location and may become infected. diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index 7e1b580989..657e6861aa 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -24,19 +24,5 @@ namespace mio namespace abm { -Simulation::Simulation(TimePoint t, Model&& model) - : m_model(std::move(model)) - , m_t(t) - , m_dt(hours(1)) -{ -} - -void Simulation::evolve_model(TimePoint tmax) -{ - auto dt = std::min(m_dt, tmax - m_t); - m_model.evolve(m_t, dt); - m_t += m_dt; -} - } // namespace abm } // namespace mio diff --git a/cpp/models/abm/simulation.h b/cpp/models/abm/simulation.h index 0e2ed61bb6..f51567e2c8 100644 --- a/cpp/models/abm/simulation.h +++ b/cpp/models/abm/simulation.h @@ -32,6 +32,7 @@ namespace abm /** * @brief Run the Simulation in discrete steps, evolve the Model and report results. */ +template class Simulation { @@ -41,7 +42,12 @@ class Simulation * @param[in] t0 The starting time of the Simulation. * @param[in] model The Model to simulate. */ - Simulation(TimePoint t0, Model&& model); + Simulation(TimePoint t0, M&& model) + : m_model(std::move(model)) + , m_t(t0) + , m_dt(hours(1)) + { + } /** * @brief Create a Simulation with an empty Model. @@ -50,7 +56,7 @@ class Simulation * @param[in] t0 The starting time of the Simulation. */ Simulation(TimePoint t0, size_t num_agegroups) - : Simulation(t0, Model(num_agegroups)) + : Simulation(t0, M(num_agegroups)) { } @@ -81,20 +87,25 @@ class Simulation /** * @brief Get the Model that this Simulation evolves. */ - Model& get_model() + M& get_model() { return m_model; } - const Model& get_model() const + const M& get_model() const { return m_model; } private: void store_result_at(TimePoint t); - void evolve_model(TimePoint tmax); + void evolve_model(TimePoint tmax) + { + auto dt = std::min(m_dt, tmax - m_t); + m_model.evolve(m_t, dt); + m_t += m_dt; + } - Model m_model; ///< The Model to simulate. + M m_model; ///< The Model to simulate. TimePoint m_t; ///< The current TimePoint of the Simulation. TimeSpan m_dt; ///< The length of the time steps. }; diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index 3181123f4b..695fc3fa58 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -25,6 +25,7 @@ #include "abm/person_id.h" #include "abm/time.h" #include "abm/location_type.h" +#include "memilio/io/io.h" #include namespace mio @@ -115,6 +116,7 @@ struct Trip { obj.add_element("time", time.seconds()); obj.add_element("destination", destination); obj.add_element("origin", origin); + obj.add_element("destination_type", destination_type); } /** @@ -124,17 +126,18 @@ 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_id = obj.expect_element("destination", Tag{}); - auto origin_id = obj.expect_element("origin", 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{}); + auto destination_type = obj.expect_element("destination_type", Tag{}); return apply( io, - [](auto&& person_id_, auto&& time_, auto&& destination_id_, auto&& origin_id_) { - return Trip(person_id_, TimePoint(time_), destination_id_, origin_id_); + [](auto&& person_id_, auto&& time_, auto&& destination_id_, auto&& origin_id_, auto&& destination_type_) { + return Trip(person_id_, TimePoint(time_), destination_id_, origin_id_, destination_type_); }, - person_id, time, destination_id, origin_id); + person_id, time, destination_id, origin_id, destination_type); } }; diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 0c9c9a916d..ee834b8a98 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -28,9 +28,12 @@ #include "abm/person.h" #include "abm/person_id.h" #include "abm/model_functions.h" +#include "graph_abm/model_wrapper.h" #include "memilio/mobility/graph_simulation.h" #include "memilio/mobility/graph.h" +#include "memilio/utils/compiler_diagnostics.h" #include +#include #include namespace mio @@ -43,7 +46,7 @@ class ABMSimulationNode { public: - using Sim = abm::Simulation; + using Sim = abm::Simulation; template ::value, void>> ABMSimulationNode(std::tuple history, Args&&... args) @@ -93,85 +96,85 @@ class ABMSimulationNode std::tuple m_history; }; -/** - * @brief Parameters influencing the mobility between two abm graph nodes. - */ -class ABMMobilityParameters -{ +// /** +// * @brief Parameters influencing the mobility between two abm graph nodes. +// */ +// class ABMMobilityParameters +// { -public: - using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); +// public: +// using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); - /** - * Constructor for initializing commuting persons - * @param commuting_persons Vector holding commuting persons' ids - */ - ABMMobilityParameters(const std::vector& commuting_persons, - const std::vector& mobility_rules) - : m_commuting_persons(commuting_persons) - , m_mobility_rules(mobility_rules) - { - } +// /** +// * Constructor for initializing commuting persons +// * @param commuting_persons Vector holding commuting persons' ids +// */ +// ABMMobilityParameters(const std::vector& commuting_persons, +// const std::vector& mobility_rules) +// : m_commuting_persons(commuting_persons) +// , m_mobility_rules(mobility_rules) +// { +// } - /** - * Equality comparison operators - */ - bool operator==(const ABMMobilityParameters& other) const - { - return m_commuting_persons == other.m_commuting_persons; - } - bool operator!=(const ABMMobilityParameters& other) const - { - return m_commuting_persons != other.m_commuting_persons; - } +// /** +// * Equality comparison operators +// */ +// bool operator==(const ABMMobilityParameters& other) const +// { +// return m_commuting_persons == other.m_commuting_persons; +// } +// bool operator!=(const ABMMobilityParameters& other) const +// { +// return m_commuting_persons != other.m_commuting_persons; +// } - /** - * Get/Set the commuting persons vector. - * The vector represents the persons (by their id) that commute from one node to another - * according to mobility rules. - */ - const std::vector& get_commuting_persons() const - { - return m_commuting_persons; - } +// /** +// * Get/Set the commuting persons vector. +// * The vector represents the persons (by their id) that commute from one node to another +// * according to mobility rules. +// */ +// const std::vector& get_commuting_persons() const +// { +// return m_commuting_persons; +// } - std::vector& get_commuting_persons() - { - return m_commuting_persons; - } - /** - * @param[in] commuting_persons Vector with commuting person ids. - */ - void set_commuting_persons(const std::vector& commuting_persons) - { - m_commuting_persons = commuting_persons; - } +// std::vector& get_commuting_persons() +// { +// return m_commuting_persons; +// } +// /** +// * @param[in] commuting_persons Vector with commuting person ids. +// */ +// void set_commuting_persons(const std::vector& commuting_persons) +// { +// m_commuting_persons = commuting_persons; +// } - /** - * Get/ the mobility rules. - * The rules are applied to the persons in m_commuting_persons every time exchange betwen two nodes is triggered. - */ - const std::vector& get_mobility_rules() const - { - return m_mobility_rules; - } - std::vector& get_mobility_rules() - { - return m_mobility_rules; - } - /** - * @brief Add mobility rule to member vector. - * @param[in] mobility_rule Rule to be added for mobility between nodes. - */ - void add_mobility_rule(const MobilityRuleType& mobility_rule) - { - m_mobility_rules.push_back(mobility_rule); - } +// /** +// * Get/ the mobility rules. +// * The rules are applied to the persons in m_commuting_persons every time exchange betwen two nodes is triggered. +// */ +// const std::vector& get_mobility_rules() const +// { +// return m_mobility_rules; +// } +// std::vector& get_mobility_rules() +// { +// return m_mobility_rules; +// } +// /** +// * @brief Add mobility rule to member vector. +// * @param[in] mobility_rule Rule to be added for mobility between nodes. +// */ +// void add_mobility_rule(const MobilityRuleType& mobility_rule) +// { +// m_mobility_rules.push_back(mobility_rule); +// } -private: - std::vector m_commuting_persons; ///< Person ids that are commuting via an edge - std::vector m_mobility_rules; ///< Rules for moving persons from one node to another -}; +// private: +// std::vector m_commuting_persons; ///< Person ids that are commuting via an edge +// std::vector m_mobility_rules; ///< Rules for moving persons from one node to another +// }; /** * Represents the mobility between two nodes. @@ -181,29 +184,29 @@ class ABMMobilityEdge { public: - using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); - /** - * Creates edge with mobility parameters - * @param params mobility parameters including people commuting via the edge and mobility rules - */ - ABMMobilityEdge(const ABMMobilityParameters& params) - : m_parameters(params) - { - } + // using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); + // /** + // * Creates edge with mobility parameters + // * @param params mobility parameters including people commuting via the edge and mobility rules + // */ + // ABMMobilityEdge(const ABMMobilityParameters& params) + // : m_parameters(params) + // { + // } - ABMMobilityEdge(const std::vector& commuting_persons, - const std::vector& mobility_rules = {}) - : m_parameters(commuting_persons, mobility_rules) - { - } + // ABMMobilityEdge(const std::vector& commuting_persons, + // const std::vector& mobility_rules = {}) + // : m_parameters(commuting_persons, mobility_rules) + // { + // } - /** - * @brief Get mobility paramters. - */ - const ABMMobilityParameters& get_parameters() const - { - return m_parameters; - } + // /** + // * @brief Get mobility paramters. + // */ + // const ABMMobilityParameters& get_parameters() const + // { + // return m_parameters; + // } /** * @brief Exchanges persons via the edge. @@ -213,55 +216,36 @@ class ABMMobilityEdge * @param[in] t Echange time point */ void apply_mobility(ABMSimulationNode& node_from, ABMSimulationNode& node_to, - abm::TimePoint t) + abm::TimePoint /*t*/) { - // iterate over all persons that could commute via the edge - for (auto p : m_parameters.get_commuting_persons()) { - auto& person_n1 = node_from.get_simulation().get_model().get_person(mio::abm::PersonId(p)); - auto& person_n2 = node_to.get_simulation().get_model().get_person(mio::abm::PersonId(p)); - auto& params = node_from.get_simulation().get_model().parameters; - // as all nodes have all person it doesn't matter which node's persons we take here - auto current_location_type = person_n1.get_location_type(); - auto current_id = person_n1.get_location(); - auto current_model_id = person_n1.get_location_model_id(); - for (auto& rule : m_parameters.get_mobility_rules()) { - auto target_type = rule(person_n1, t, params); - auto target_model_id = person_n1.get_assigned_location_model_id(target_type); - auto target_id = person_n1.get_assigned_location(target_type); - auto& target_model = (target_model_id == node_from.get_simulation().get_model().get_id()) - ? node_from.get_simulation().get_model() - : node_to.get_simulation().get_model(); - auto& target_location = target_model.get_location(target_id); - assert((node_from.get_simulation().get_model().get_id() == target_location.get_model_id() || - node_to.get_simulation().get_model().get_id() == target_location.get_model_id()) && - "Wrong graph edge. Target location is no edge node."); - if (target_type == current_location_type && - (target_id != current_id || target_model_id != current_model_id)) { - mio::log_error("Person with index {} has two assigned locations of the same type.", - person_n1.get_id().get()); - } - if (target_type != current_location_type && - target_model.get_number_persons(target_id) < target_location.get_capacity().persons) { - //change person's location in all nodes - mio::abm::change_location(person_n1, target_location); - mio::abm::change_location(person_n2, target_location); - // invalidate both models' cache - node_to.get_simulation().get_model().invalidate_cache(); - node_from.get_simulation().get_model().invalidate_cache(); - if (target_model_id != current_model_id) { - // change activeness status for commuted person - node_to.get_simulation().get_model().change_activeness(p); - node_from.get_simulation().get_model().change_activeness(p); - } - // only one mobility rule per person can be applied - break; + auto& model_from = node_from.get_simulation().get_model(); + auto& model_to = node_to.get_simulation().get_model(); + auto& persons_to_change = model_from.get_person_buffer(); + //iterate over all persons that change from node_from + for (int i = persons_to_change.size() - 1; i >= 0; --i) { + auto& person = model_from.get_persons()[persons_to_change[i]]; + auto target_type = person.get_location_type(); + //check if Person uses this edge + if (person.get_assigned_location_model_id(target_type) == model_to.get_id()) { + auto target_id = person.get_assigned_location(target_type); + //set correct location for person + person.set_location(target_type, target_id, model_to.get_id()); + //add person to model_to + model_to.add_person(std::move(person)); + //remove person from model_from + model_from.remove_person(persons_to_change[i]); + // correct indices in persons buffer from node_from + for (size_t j = i + 1; j < persons_to_change.size(); ++j) { + persons_to_change[j]--; } + //delete current index from list + persons_to_change.erase(persons_to_change.begin() + i); } } } -private: - ABMMobilityParameters m_parameters; ///< Mobility parameters + // private: + // ABMMobilityParameters m_parameters; ///< Mobility parameters }; /** @@ -313,4 +297,4 @@ make_abm_graph_sim(abm::TimePoint t0, abm::TimeSpan dt, } // namespace mio -#endif // MIO_ABM_GRAPH_MOBILITY_H \ No newline at end of file +#endif // MIO_ABM_GRAPH_MOBILITY_H diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 9f2dd17d5c..061caa086e 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -24,8 +24,10 @@ #include "abm/model.h" #include "abm/time.h" #include "abm/location_id.h" +#include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/mioomp.h" #include "abm/mobility_rules.h" +#include "abm/mobility_rules.h" #include namespace mio @@ -38,24 +40,27 @@ class ModelWrapper : public abm::Model public: /** - * @brief Get a reference to a Person from this Model. - * @param[in] id A person's PersonId. - * First 32 bit are the Person's index and second 32 bit the Persons's home model id. - * @return Position of Person in m_persons vector. - * @{ + * @brief Get person buffer for given model id. */ - size_t get_person_pos(PersonId id) + std::vector& get_person_buffer() { - auto it = std::find(Base::m_persons.begin(), Base::m_persons.end(), [id](auto& person) { - person.get_id() == id; - }); - if (it == Base::m_persons.end()) { - log_error("Given PersonId is not in this Model."); - return std::numeric_limits::max(); - } - else { - return std::distance(Base::m_persons.begin(), it); - } + return m_person_buffer; + } + + //TODO comment + void remove_person(size_t pos) + { + Base::m_persons.erase(Base::m_persons.begin() + pos); + Base::m_activeness_statuses.erase(Base::m_activeness_statuses.begin() + pos); + } + + void evolve(TimePoint t, TimeSpan dt) + { + Base::begin_step(t, dt); + log_info("Graph ABM Model interaction."); + Base::interaction(t, dt); + log_info("Graph ABM Model mobility."); + perform_mobility(t, dt); } private: @@ -89,7 +94,9 @@ class ModelWrapper : public abm::Model else { //person moves to other world Base::m_activeness_statuses[person_id] = false; person.set_location(target_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); - person_buffer.push_back(person_id); + m_person_buffer.push_back(person_id); + m_are_exposure_caches_valid = false; + m_is_local_population_cache_valid = false; return true; } return false; @@ -97,27 +104,18 @@ class ModelWrapper : public abm::Model //run mobility rules one after the other if the corresponding location type exists //shortcutting of bool operators ensures the rules stop after the first rule is applied - if (m_use_mobility_rules) { - (Base::has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || - (Base::has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || - (Base::has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || - (Base::has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || - (Base::has_locations({LocationType::School, LocationType::Home}) && - try_mobility_rule(&go_to_school)) || - (Base::has_locations({LocationType::Work, LocationType::Home}) && try_mobility_rule(&go_to_work)) || - (Base::has_locations({LocationType::BasicsShop, LocationType::Home}) && - try_mobility_rule(&go_to_shop)) || - (Base::has_locations({LocationType::SocialEvent, LocationType::Home}) && - try_mobility_rule(&go_to_event)) || - (Base::has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); + if (Base::m_use_mobility_rules) { + try_mobility_rule(&get_buried) || try_mobility_rule(&return_home_when_recovered) || + try_mobility_rule(&go_to_hospital) || try_mobility_rule(&go_to_icu) || + try_mobility_rule(&go_to_school) || try_mobility_rule(&go_to_work) || + try_mobility_rule(&go_to_shop) || try_mobility_rule(&go_to_event) || + try_mobility_rule(&go_to_quarantine); } else { //no daily routine mobility, just infection related - (Base::has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) || - (Base::has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) || - (Base::has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) || - (Base::has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) || - (Base::has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); + try_mobility_rule(&get_buried) || try_mobility_rule(&return_home_when_recovered) || + try_mobility_rule(&go_to_hospital) || try_mobility_rule(&go_to_icu) || + try_mobility_rule(&go_to_quarantine); } } } @@ -127,38 +125,40 @@ class ModelWrapper : public abm::Model size_t num_trips = m_trip_list.num_trips(weekend); if (num_trips != 0) { - 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_pos = get_person_pos(trip.person_id); - auto& person = Base::get_person(person_pos); - auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + while (Base::m_trip_list.get_current_index() < num_trips && + Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { + auto& trip = Base::m_trip_list.get_next_trip(weekend); + auto person_index = Base::get_person_index(trip.person_id); + auto& person = Base::get_person(person_index); + auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { if (trip.destination_model_id == Base::m_id) { auto& target_location = get_location(trip.destination); - if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); - change_location(person.get_id(), target_location.get_id(), trip.trip_mode); + Base::change_location(person_index, target_location.get_id(), trip.trip_mode); } } else { //person moves to other world - Base::m_activeness_statuses[person_pos] = false; + Base::m_activeness_statuses[person_index] = false; person.set_location(trip.destination_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); - person_buffer.push_back(person_pos); + m_person_buffer.push_back(person_index); + m_are_exposure_caches_valid = false; + m_is_local_population_cache_valid = false; } } - m_trip_list.increase_index(); + Base::m_trip_list.increase_index(); } } if (((t).days() < std::floor((t + dt).days()))) { - m_trip_list.reset_index(); + Base::m_trip_list.reset_index(); } } - std::list person_buffer; ///< List with indices of persons that are deactivated. + std::vector m_person_buffer; ///< List with indices of persons that are deactivated per target world. }; } // namespace mio -#endif //MIO_ABM_MODEL_WRAPPER_H \ No newline at end of file +#endif //MIO_ABM_MODEL_WRAPPER_H diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index c5dce6940b..7b2d66649f 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -379,7 +379,7 @@ void create_assign_locations(mio::abm::Model& model) //Assign locations to the people auto persons = model.get_persons(); for (auto& person : persons) { - const auto id = person.get_id(); + const auto id = model.get_person_index(person.get_id()); //assign shop and event model.assign_location(id, event); counter_event++; diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index 8ced5e0f66..41e64b134e 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -370,22 +370,23 @@ void create_model_from_data(mio::abm::Model& model, const std::string& filename, auto first_location = locations.find(first_location_id)->second; auto person_model_id = model.add_person(first_location, determine_age_group(age)); auto home = locations.find(home_id)->second; - model.assign_location(person_model_id, home); - model.assign_location(person_model_id, hospital); - model.assign_location(person_model_id, icu); + model.assign_location(model.get_person_index(person_model_id), home); + model.assign_location(model.get_person_index(person_model_id), hospital); + model.assign_location(model.get_person_index(person_model_id), icu); pid_itr = pids_data_to_model.insert_or_assign(person_data_id, person_model_id).first; } model.assign_location( - pid_itr->second, + model.get_person_index(pid_itr->second), target_location); //This assumes that we only have in each tripchain only one location type for each person if (locations.find(start_location_id) == locations.end()) { // For trips where the start location is not known use Home instead - start_location = model.get_person(pid_itr->second).get_assigned_location(mio::abm::LocationType::Home); + start_location = model.get_person(model.get_person_index(pid_itr->second)) + .get_assigned_location(mio::abm::LocationType::Home); } model.get_trip_list().add_trip(mio::abm::Trip( 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))); + mio::abm::TransportMode(transport_mode), mio::abm::LocationType(acticity_end))); } model.get_trip_list().use_weekday_trips_on_weekend(); } @@ -884,8 +885,8 @@ void set_parameters(mio::abm::Parameters params) * Create a sampled simulation with start time t0. * @param t0 The start time of the Simulation. */ -mio::abm::Simulation create_sampled_simulation(const std::string& input_file, const mio::abm::TimePoint& t0, - int max_num_persons) +mio::abm::Simulation<> create_sampled_simulation(const std::string& input_file, const mio::abm::TimePoint& t0, + int max_num_persons) { // Assumed percentage of infection state at the beginning of the simulation. ScalarType exposed_prob = 0.005, infected_no_symptoms_prob = 0.001, infected_symptoms_prob = 0.001, @@ -952,7 +953,7 @@ void write_log_to_file_trip_data(const T& history) int start_index = mobility_data_index - 1; using Type = std::tuple; + mio::abm::TransportMode, mio::abm::ActivityType, mio::abm::InfectionState>; while (!std::binary_search(std::begin(mobility_data[start_index]), std::end(mobility_data[start_index]), mobility_data[mobility_data_index][trip_index], [](const Type& v1, const Type& v2) { diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 039e0199c7..90106bc870 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -72,8 +72,7 @@ set(TESTSOURCES test_lct_secir.cpp test_lct_initializer_flows.cpp test_ad.cpp - - # test_graph_abm.cpp + test_graph_abm.cpp ) if(MEMILIO_HAS_JSONCPP) diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 48fd72ba97..481aaead01 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -74,10 +74,14 @@ TEST(TestLocation, reachCapacity) auto p1 = add_test_person(model, home_id, age_group_5_to_14, mio::abm::InfectionState::InfectedNoSymptoms); auto p2 = add_test_person(model, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible); - model.get_person(p1).set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); - model.get_person(p2).set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); - model.get_person(p1).set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - model.get_person(p2).set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + model.get_person(model.get_person_index(p1)) + .set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); + model.get_person(model.get_person_index(p2)) + .set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); + model.get_person(model.get_person_index(p1)) + .set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + model.get_person(model.get_person_index(p2)) + .set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); model.get_location(school_id).set_capacity(1, 66); @@ -87,8 +91,9 @@ TEST(TestLocation, reachCapacity) model.evolve(t, dt); - ASSERT_EQ(model.get_person(p1).get_location(), school_id); - ASSERT_EQ(model.get_person(p2).get_location(), home_id); // p2 should not be able to enter the school + ASSERT_EQ(model.get_person(model.get_person_index(p1)).get_location(), school_id); + ASSERT_EQ(model.get_person(model.get_person_index(p2)).get_location(), + home_id); // p2 should not be able to enter the school ASSERT_EQ(model.get_number_persons(school_id), 1); ASSERT_EQ(model.get_number_persons(home_id), 1); } diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 1b9396ae66..d6ab499e33 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -17,6 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/location.h" +#include "abm/location_type.h" #include "abm/person.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" @@ -104,20 +106,20 @@ TEST(TestModel, findLocation) auto school_id = model.add_location(mio::abm::LocationType::School); auto work_id = model.add_location(mio::abm::LocationType::Work); auto person_id = add_test_person(model, home_id); - auto& person = model.get_person(person_id); + auto& person = model.get_person(model.get_person_index(person_id)); person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); person.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); - EXPECT_EQ(model.find_location(mio::abm::LocationType::Work, person_id), work_id); - EXPECT_EQ(model.find_location(mio::abm::LocationType::School, person_id), school_id); - EXPECT_EQ(model.find_location(mio::abm::LocationType::Home, person_id), home_id); + EXPECT_EQ(model.find_location(mio::abm::LocationType::Work, model.get_person_index(person_id)), work_id); + EXPECT_EQ(model.find_location(mio::abm::LocationType::School, model.get_person_index(person_id)), school_id); + EXPECT_EQ(model.find_location(mio::abm::LocationType::Home, model.get_person_index(person_id)), home_id); auto&& model_test = std::as_const(model); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Work, person_id), work_id); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::School, person_id), school_id); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Home, person_id), home_id); + EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Work, model.get_person_index(person_id)), work_id); + EXPECT_EQ(model_test.find_location(mio::abm::LocationType::School, model.get_person_index(person_id)), school_id); + EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Home, model.get_person_index(person_id)), home_id); } TEST(TestModel, evolveStateTransition) @@ -210,8 +212,8 @@ TEST(TestModel, evolveMobility) auto pid1 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); - auto& p1 = model.get_person(pid1); - auto& p2 = model.get_person(pid2); + auto& p1 = model.get_person(model.get_person_index(pid1)); + auto& p2 = model.get_person(model.get_person_index(pid2)); p1.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); p2.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); @@ -274,11 +276,11 @@ TEST(TestModel, evolveMobility) auto pid4 = add_test_person(model, hospital_id, age_group_5_to_14, mio::abm::InfectionState::Recovered, t); auto pid5 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t); - auto& p1 = model.get_person(pid1); - auto& p2 = model.get_person(pid2); - auto& p3 = model.get_person(pid3); - auto& p4 = model.get_person(pid4); - auto& p5 = model.get_person(pid5); + auto& p1 = model.get_person(model.get_person_index(pid1)); + auto& p2 = model.get_person(model.get_person_index(pid2)); + auto& p3 = model.get_person(model.get_person_index(pid3)); + auto& p4 = model.get_person(model.get_person_index(pid4)); + auto& p5 = model.get_person(model.get_person_index(pid5)); p1.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); p2.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); @@ -295,9 +297,12 @@ TEST(TestModel, evolveMobility) p5.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); mio::abm::TripList& data = model.get_trip_list(); - mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, 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); + mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id, + mio::abm::LocationType::Work); + mio::abm::Trip trip2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, + mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, + mio::abm::LocationType::SocialEvent); data.add_trip(trip1); data.add_trip(trip2); data.add_trip(trip3); @@ -320,9 +325,9 @@ TEST(TestModel, evolveMobility) EXPECT_EQ(model.get_number_persons(home_id), 1); EXPECT_EQ(model.get_number_persons(hospital_id), 1); - model.change_location(p1.get_id(), home_id); - model.change_location(p2.get_id(), home_id); - model.change_location(p5.get_id(), home_id); + model.change_location(model.get_person_index(p1.get_id()), home_id); + model.change_location(model.get_person_index(p2.get_id()), home_id); + model.change_location(model.get_person_index(p5.get_id()), home_id); t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); model.get_trip_list().reset_index(); @@ -340,11 +345,11 @@ TEST(TestModel, evolveMobility) bool weekend = true; mio::abm::Trip tripweekend1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), - event_id); + event_id, work_id, mio::abm::LocationType::SocialEvent); mio::abm::Trip tripweekend2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), - home_id); + home_id, event_id, mio::abm::LocationType::Home); mio::abm::Trip tripweekend3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), - work_id); + work_id, event_id, mio::abm::LocationType::Work); data.add_trip(tripweekend1, weekend); data.add_trip(tripweekend2, weekend); data.add_trip(tripweekend3, weekend); @@ -396,24 +401,31 @@ TEST(TestModel, evolveMobility) // Add trip to see if a dead person can change location outside of cemetery by scheduled trips mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), 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); + mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, + mio::abm::LocationType::Work); + mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::LocationType::Home); + mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::LocationType::Home); 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 model.evolve(t, dt); - EXPECT_EQ(model.get_location(p_dead.get_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(model.get_person_index(p_dead.get_id())).get_type(), + mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Hospital); + EXPECT_EQ(model.get_location(model.get_person_index(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 model.evolve(t + dt, dt); - EXPECT_EQ(model.get_location(p_dead.get_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(model.get_person_index(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(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(model.get_person_index(p_severe.get_id())).get_type(), + mio::abm::LocationType::Cemetery); } } @@ -434,7 +446,7 @@ TEST(TestModelTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) auto current_time = mio::abm::TimePoint(0); auto pid = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time); - auto& person = model.get_person(pid); + auto& person = model.get_person(model.get_person_index(pid)); auto rng_person = mio::abm::PersonalRandomNumberGenerator(rng, person); person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index 295f479874..543a25d1da 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/location_type.h" #include "abm_helpers.h" #include "abm/common_abm_loggers.h" #include "matchers.h" @@ -32,10 +33,10 @@ TEST(TestSimulation, advance_random) auto p3 = model.add_person(location2, age_group_5_to_14); auto p4 = model.add_person(location2, age_group_5_to_14); - model.assign_location(p1, location1); - model.assign_location(p2, location1); - model.assign_location(p3, location2); - model.assign_location(p4, location2); + model.assign_location(model.get_person_index(p1), location1); + model.assign_location(model.get_person_index(p2), location1); + model.assign_location(model.get_person_index(p3), location2); + model.assign_location(model.get_person_index(p4), location2); auto sim = mio::abm::Simulation(mio::abm::TimePoint(0), std::move(model)); @@ -81,31 +82,38 @@ TEST(TestSimulation, advanceWithCommonHistory) auto person2 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Exposed); auto person3 = add_test_person(model, home_id, age_group_35_to_59, mio::abm::InfectionState::Dead); - model.assign_location(person1, home_id); - model.assign_location(person2, home_id); - model.assign_location(person3, home_id); - model.assign_location(person1, school_id); - model.assign_location(person2, work_id); - model.assign_location(person2, icu_id); - model.assign_location(person2, hospital_id); - model.assign_location(person1, social_id); - model.assign_location(person2, social_id); - model.assign_location(person3, social_id); - model.assign_location(person1, basics_id); - model.assign_location(person2, basics_id); - model.assign_location(person3, basics_id); - model.assign_location(person2, public_id); + model.assign_location(model.get_person_index(person1), home_id); + model.assign_location(model.get_person_index(person2), home_id); + model.assign_location(model.get_person_index(person3), home_id); + model.assign_location(model.get_person_index(person1), school_id); + model.assign_location(model.get_person_index(person2), work_id); + model.assign_location(model.get_person_index(person2), icu_id); + model.assign_location(model.get_person_index(person2), hospital_id); + model.assign_location(model.get_person_index(person1), social_id); + model.assign_location(model.get_person_index(person2), social_id); + model.assign_location(model.get_person_index(person3), social_id); + model.assign_location(model.get_person_index(person1), basics_id); + model.assign_location(model.get_person_index(person2), basics_id); + model.assign_location(model.get_person_index(person3), basics_id); + model.assign_location(model.get_person_index(person2), public_id); mio::abm::TripList& trip_list = model.get_trip_list(); // We add trips for person two to test the history and if it is working correctly - mio::abm::Trip trip1(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); + mio::abm::Trip trip1(person2, mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, + mio::abm::LocationType::Work); + mio::abm::Trip trip2(person2, mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id, home_id, + mio::abm::LocationType::ICU); + mio::abm::Trip trip3(person2, mio::abm::TimePoint(0) + mio::abm::hours(4), hospital_id, home_id, + mio::abm::LocationType::Hospital); + mio::abm::Trip trip4(person2, mio::abm::TimePoint(0) + mio::abm::hours(5), social_id, home_id, + mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip5(person2, mio::abm::TimePoint(0) + mio::abm::hours(6), basics_id, home_id, + mio::abm::LocationType::BasicsShop); + mio::abm::Trip trip6(person2, mio::abm::TimePoint(0) + mio::abm::hours(7), public_id, home_id, + mio::abm::LocationType::PublicTransport); + mio::abm::Trip trip7(person2, mio::abm::TimePoint(0) + mio::abm::hours(8), home_id, home_id, + mio::abm::LocationType::Home); trip_list.add_trip(trip1); trip_list.add_trip(trip2); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 2fa6a1725d..c4d23c6d7b 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -18,52 +18,20 @@ * limitations under the License. */ +#include "abm/location.h" +#include "abm/location_id.h" #include "abm/model.h" +#include "graph_abm/model_wrapper.h" #include "abm/location_type.h" #include "abm/time.h" #include "graph_abm/graph_abm_mobility.h" #include "graph_abm/mobility_rules.h" #include "memilio/epidemiology/age_group.h" +#include "memilio/utils/miompi.h" +#include #include #include -TEST(TestGraphAbm, test_activessness) -{ - auto model = mio::abm::Model(size_t(1)); - model.parameters.get()[mio::AgeGroup(0)] = true; - auto work_id = model.add_location(mio::abm::LocationType::Work); - auto home_id = model.add_location(mio::abm::LocationType::Home); - auto& home = model.get_location(home_id); - auto& work = model.get_location(work_id); - auto p1_id = model.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model.add_person(home_id, mio::AgeGroup(0)); - auto& p1 = model.get_person(p1_id); - auto& p2 = model.get_person(p2_id); - p1.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); - p2.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); - p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - - EXPECT_EQ(p1.get_location(), home_id); - EXPECT_EQ(p2.get_location(), home_id); - EXPECT_EQ(p1.get_location_model_id(), model.get_id()); - EXPECT_EQ(p2.get_location_model_id(), model.get_id()); - EXPECT_EQ(model.get_activeness_statuses().size(), 2); - - model.change_activeness(p1.get_id()); - EXPECT_EQ(model.get_activeness_statuses()[p1.get_id().get()], false); - EXPECT_EQ(model.get_activeness_statuses()[p2.get_id().get()], true); - - auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); - auto dt = mio::abm::hours(3); - - model.evolve(t, dt); - - //inactive persons do not move - EXPECT_EQ(p1.get_location(), home_id); - EXPECT_EQ(p2.get_location(), work_id); -} - struct MockHistory { template @@ -75,61 +43,70 @@ struct MockHistory { TEST(TestGraphAbm, test_evolve_node) { - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::hours(2); - mio::ABMSimulationNode node(MockHistory{}, t, size_t(1)); + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::hours(10); + auto model = mio::ModelWrapper(size_t(1), 1); + model.parameters.get()[mio::AgeGroup(0)] = true; + auto home_id = model.add_location(mio::abm::LocationType::Home); + auto& home = model.get_location(home_id); + auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0)); + auto pid = model.add_person(home_id, mio::AgeGroup(0)); + auto index = model.get_person_index(pid); + auto& p = model.get_person(pid); + p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p.set_assigned_location(work.get_type(), work.get_id(), 2); + mio::ABMSimulationNode node(MockHistory{}, t, std::move(model)); + EXPECT_EQ(node.get_simulation().get_model().get_activeness_statuses()[index], true); node.evolve(t, dt); EXPECT_EQ(node.get_simulation().get_time(), mio::abm::TimePoint(dt.seconds())); + EXPECT_EQ(node.get_simulation().get_model().get_activeness_statuses()[index], false); + EXPECT_EQ(node.get_simulation().get_model().get_person_buffer().size(), 1); + EXPECT_EQ(node.get_simulation().get_model().get_person_buffer()[0], index); } TEST(TestGraphAbm, test_apply_mobility) { - auto model_1 = mio::abm::Model(size_t(1), 1); - auto model_2 = mio::abm::Model(size_t(1), 2); - model_1.parameters.get()[mio::AgeGroup(0)] = true; - model_2.parameters.get()[mio::AgeGroup(0)] = true; - auto work_id_1 = model_1.add_location(mio::abm::LocationType::Work); - auto home_id = model_1.add_location(mio::abm::LocationType::Home); - auto work_id_2 = model_2.add_location(mio::abm::LocationType::Work); - auto& work_1 = model_1.get_location(work_id_1); - auto& work_2 = model_2.get_location(work_id_2); - auto& home = model_1.get_location(home_id); + auto model1 = mio::ModelWrapper(size_t(1), 1); + auto model2 = mio::ModelWrapper(size_t(1), 2); + model1.parameters.get()[mio::AgeGroup(0)] = true; + model2.parameters.get()[mio::AgeGroup(0)] = true; + auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); + auto home_id = model1.add_location(mio::abm::LocationType::Home); + auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); + auto& work_1 = model1.get_location(work_id_1); + auto& work_2 = model2.get_location(work_id_2); + auto& home = model1.get_location(home_id); EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); - auto p1_id = model_1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model_1.add_person(home_id, mio::AgeGroup(0)); - auto& p1 = model_1.get_person(p1_id); - auto& p2 = model_1.get_person(p2_id); + auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto p2_index = model1.get_person_index(p2_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); - //copy persons to model 2 - model_2.copy_persons_from_other_model(model_1); - - auto t = mio::abm::TimePoint(0) + mio::abm::hours(6); - mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model_1)); - mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model_2)); + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::hours(12); + mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); + mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); - mio::ABMMobilityEdge edge({p2.get_id().get()}, {&mio::apply_commuting}); - edge.apply_mobility(node1, node2, t); + node1.evolve(t, dt); + node2.evolve(t, dt); - EXPECT_EQ(node2.get_simulation().get_model().get_number_persons(work_id_2), 1); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], true); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], false); - EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], false); - EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], true); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - //return home - t += mio::abm::hours(12); + mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); - EXPECT_EQ(node2.get_simulation().get_model().get_number_persons(work_id_2), 0); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], true); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], true); - EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p1.get_id().get()], false); - EXPECT_EQ(node2.get_simulation().get_model().get_activeness_statuses()[p2.get_id().get()], false); -} \ No newline at end of file + + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 1); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 1); + EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); +} diff --git a/cpp/tests/test_json_serializer.cpp b/cpp/tests/test_json_serializer.cpp index e57ee2960f..571c5b14bf 100644 --- a/cpp/tests/test_json_serializer.cpp +++ b/cpp/tests/test_json_serializer.cpp @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/location_type.h" #include "memilio/io/json_serializer.h" #include "memilio/utils/parameter_distributions.h" #include "memilio/utils/stl_util.h" @@ -510,13 +511,15 @@ TEST(TestJsonSerializer, abmTrip) 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); + mio::abm::Trip trip(person.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), 1, 0, + mio::abm::LocationType::Work); 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"] = Json::UInt(1); // work - expected_json["origin"] = Json::UInt(0); // home + expected_json["person_id"] = Json::UInt64(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 + expected_json["destination_type"] = Json::UInt64(mio::abm::LocationType::Work); ASSERT_EQ(js.value(), expected_json); auto r = mio::deserialize_json(expected_json, mio::Tag()); From 07b268aaa840e59d591e4620445bc2828e619be2 Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 10:28:39 +0200 Subject: [PATCH 027/111] msvc is a diva --- cpp/models/abm/model.cpp | 4 ++-- cpp/models/abm/model.h | 2 +- cpp/models/graph_abm/graph_abm_mobility.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index ce0dbb32fa..5346edae8f 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -246,8 +246,8 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) const auto location = person.get_location().get(); if (person.get_location_model_id() == m_id) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], - m_contact_exposure_rates_cache[location], person, get_location(i), - t, dt); + m_contact_exposure_rates_cache[location], person, + get_location(uint32_t(i)), t, dt); } } // implicit taskloop barrier } // implicit single barrier diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 5f4ad2f7cf..b6e5661e8a 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -590,7 +590,7 @@ class Model return std::numeric_limits::max(); } else { - return std::distance(m_persons.begin(), it); + return static_cast(std::distance(m_persons.begin(), it)); } } diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index ee834b8a98..7663af2855 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -222,7 +222,7 @@ class ABMMobilityEdge auto& model_to = node_to.get_simulation().get_model(); auto& persons_to_change = model_from.get_person_buffer(); //iterate over all persons that change from node_from - for (int i = persons_to_change.size() - 1; i >= 0; --i) { + for (int i = int(persons_to_change.size()) - 1; i >= 0; --i) { auto& person = model_from.get_persons()[persons_to_change[i]]; auto target_type = person.get_location_type(); //check if Person uses this edge From 8623ad38bd28afeb508d3f6ccd5fabb6f99e67bb Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 10:40:23 +0200 Subject: [PATCH 028/111] add location model id to constructor --- cpp/models/abm/location.cpp | 3 ++- cpp/models/abm/location.h | 20 +++++++------------- cpp/models/abm/model.cpp | 3 +-- cpp/tests/test_abm_location.cpp | 4 ++-- cpp/tests/test_abm_person.cpp | 6 +++--- cpp/tests/test_graph_abm.cpp | 2 +- 6 files changed, 16 insertions(+), 22 deletions(-) diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index 81d803471e..b7dd257521 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -27,13 +27,14 @@ namespace mio namespace abm { -Location::Location(LocationType loc_type, LocationId loc_id, size_t num_agegroups, uint32_t num_cells) +Location::Location(LocationType loc_type, LocationId loc_id, size_t num_agegroups, int model_id, 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) , m_npi_active(false) + , m_model_id(model_id) { assert(num_cells > 0 && "Number of cells has to be larger than 0."); } diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 0a45b58786..4b8b9a93bc 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -100,19 +100,22 @@ class Location * @param[in] loc_type The #LocationType. * @param[in] loc_id The index of the Location in the Model. * @param[in] num_agegroups [Default: 1] The number of age groups in the model. + * @param[in] model_id [Default: 0] The model id the Location is in. * @param[in] num_cells [Default: 1] The number of Cell%s in which the Location is divided. */ - explicit Location(LocationType loc_type, LocationId loc_id, size_t num_agegroups = 1, uint32_t num_cells = 1); + explicit Location(LocationType loc_type, LocationId loc_id, size_t num_agegroups = 1, int model_id = 0, + uint32_t num_cells = 1); /** * @brief Construct a copy of a Location with a new ID. * @param[in] other The Location to copy from. * @param[in] id The ID for the new Location. */ - explicit Location(const Location& other, LocationId id) + explicit Location(const Location& other, LocationId id, int model_id = 0) : Location(other) { - m_id = id; + m_id = id; + m_model_id = model_id; } /** @@ -289,15 +292,6 @@ class Location return m_model_id; } - /** - * @brief Set model id of the Location. Is only relevant for graph ABM or hybrid model. - * @param[in] model_id The model id of the location. - */ - void set_model_id(int model_id) - { - m_model_id = model_id; - } - private: LocationType m_type; ///< Type of the Location. LocationId m_id; ///< Unique identifier for the Location in the Model owning it. @@ -306,7 +300,7 @@ class Location 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. GeographicalLocation m_geographical_location; ///< Geographical location (longitude and latitude) of the Location. - int m_model_id = 0; ///< Model id the location is in. Only used for ABM graph model or hybrid graph model. + int m_model_id; ///< Model id the location is in. Only used for ABM graph model or hybrid graph model. }; } // namespace abm diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 5346edae8f..5fb51a8dc8 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -37,9 +37,8 @@ namespace abm LocationId Model::add_location(LocationType type, uint32_t num_cells) { LocationId id{static_cast(m_locations.size())}; - m_locations.emplace_back(type, id, parameters.get_num_groups(), num_cells); + m_locations.emplace_back(type, id, parameters.get_num_groups(), m_id, num_cells); m_has_locations[size_t(type)] = true; - m_locations[id.get()].set_model_id(m_id); // mark caches for rebuild m_is_local_population_cache_valid = false; diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 481aaead01..d8154daaf2 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -27,7 +27,7 @@ TEST(TestLocation, initCell) { - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 2); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 6, 0, 2); ASSERT_EQ(location.get_cells().size(), 2); } @@ -102,7 +102,7 @@ TEST(TestLocation, computeSpacePerPersonRelative) { using testing::Return; - mio::abm::Location home(mio::abm::LocationType::Home, 0, 6, 3); + mio::abm::Location home(mio::abm::LocationType::Home, 0, 6, 0, 3); home.set_capacity(4, 264, 0); // Capacity for Cell 1 home.set_capacity(2, 132, 1); // Capacity for Cell 2 home.set_capacity(0, 0, 2); // Capacity for Cell 3 diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index e40d061f16..08afd492e3 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -47,9 +47,9 @@ TEST(TestPerson, change_location) auto rng = mio::RandomNumberGenerator(); mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); - mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 1, 6, 1); + mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 1, 6, 0, 1); mio::abm::Location loc2(mio::abm::LocationType::School, 2, num_age_groups); - mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 3, 6, 2); + mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 3, 6, 0, 2); auto person = make_test_person(home, age_group_0_to_4, mio::abm::InfectionState::Recovered); // check that a person does not change location to its current location @@ -188,7 +188,7 @@ 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, 1, 6, 7); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 1, 6, 0, 7); auto person = make_test_person(home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms); EXPECT_TRUE(mio::abm::change_location(person, location, mio::abm::TransportMode::Unknown, {3, 5})); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index c4d23c6d7b..8e0ba099f7 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -49,7 +49,7 @@ TEST(TestGraphAbm, test_evolve_node) model.parameters.get()[mio::AgeGroup(0)] = true; auto home_id = model.add_location(mio::abm::LocationType::Home); auto& home = model.get_location(home_id); - auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0)); + auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0), size_t(1), 1); auto pid = model.add_person(home_id, mio::AgeGroup(0)); auto index = model.get_person_index(pid); auto& p = model.get_person(pid); From d4e376569fa6f06d1cdad6df0e9b4baae9ceff09 Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 11:30:42 +0200 Subject: [PATCH 029/111] review comments --- cpp/benchmarks/abm.cpp | 62 ---------- cpp/memilio/mobility/graph_simulation.h | 8 +- .../metapopulation_mobility_instant.h | 28 +++-- cpp/models/abm/model.cpp | 10 ++ cpp/models/abm/model.h | 102 +++-------------- cpp/models/graph_abm/graph_abm_mobility.h | 107 ------------------ 6 files changed, 45 insertions(+), 272 deletions(-) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 47b9870f38..45d06ae8ca 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -145,38 +145,6 @@ void abm_benchmark(benchmark::State& state, size_t num_persons, std::initializer } } -void abm_inactive_persons_benchmark(benchmark::State& state, size_t num_persons, size_t num_inactive_persons, - std::initializer_list seeds) -{ - mio::set_log_level(mio::LogLevel::warn); - for (auto&& _ : state) { - state.PauseTiming(); //exclude the setup from the benchmark - auto sim = make_simulation(num_persons + num_inactive_persons, seeds); - //deactivate num_inactive_persons - for (size_t p_id = 0; p_id < num_inactive_persons; ++p_id) { - sim.get_model().set_activeness(mio::abm::PersonId(p_id)); - } - state.ResumeTiming(); - - //simulated time should be long enough to have full infection runs and mobility to every location - auto final_time = sim.get_time() + mio::abm::days(10); - sim.advance(final_time); - - //debug output can be enabled to check for unexpected results (e.g. infections dieing out) - //normally should have no significant effect on runtime - const bool monitor_infection_activity = false; - if constexpr (monitor_infection_activity) { - std::cout << "num_persons = " << num_persons << "\n"; - for (auto inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { - std::cout << "inf_state = " << inf_state << ", sum = " - << sim.get_model().get_subpopulation_combined(sim.get_time(), - mio::abm::InfectionState(inf_state)) - << "\n"; - } - } - } -} - //Measure ABM simulation run time with different sizes and different seeds. //Fixed RNG seeds to make runs comparable. When there are code changes, the simulation will still //run differently due to different sequence of random numbers being drawn. But for large enough sizes @@ -189,34 +157,4 @@ BENCHMARK_CAPTURE(abm_benchmark, abm_benchmark_50k, 50000, {14159265u, 35897932u BENCHMARK_CAPTURE(abm_benchmark, abm_benchmark_100k, 100000, {38462643u, 38327950u})->Unit(benchmark::kMillisecond); BENCHMARK_CAPTURE(abm_benchmark, abm_benchmark_200k, 200000, {28841971u, 69399375u})->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_50k, 50000, 50000, - {14159265u, 35897932u}) - ->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_100k, 100000, 100000, - {38462643u, 38327950u}) - ->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_200k, 200000, 200000, - {28841971u, 69399375u}) - ->Unit(benchmark::kMillisecond); - -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_50k_2, 50000, 100000, - {14159265u, 35897932u}) - ->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_100k_2, 100000, 200000, - {38462643u, 38327950u}) - ->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_200k_2, 200000, 400000, - {28841971u, 69399375u}) - ->Unit(benchmark::kMillisecond); - -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_50k_3, 50000, 150000, - {14159265u, 35897932u}) - ->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_100k_3, 100000, 300000, - {38462643u, 38327950u}) - ->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(abm_inactive_persons_benchmark, abm_inactive_persons_benchmark_200k_3, 200000, 600000, - {28841971u, 69399375u}) - ->Unit(benchmark::kMillisecond); - BENCHMARK_MAIN(); diff --git a/cpp/memilio/mobility/graph_simulation.h b/cpp/memilio/mobility/graph_simulation.h index 12f866b7a2..dacf336834 100644 --- a/cpp/memilio/mobility/graph_simulation.h +++ b/cpp/memilio/mobility/graph_simulation.h @@ -85,13 +85,13 @@ class GraphSimulationBase }; template + class edge_f = std::function, + class node_f = std::function> class GraphSimulation : public GraphSimulationBase { - using GraphSimulationBase::GraphSimulationBase; using Base = GraphSimulationBase; + using Base::GraphSimulationBase; public: void advance(Timepoint t_max = 1.0) diff --git a/cpp/memilio/mobility/metapopulation_mobility_instant.h b/cpp/memilio/mobility/metapopulation_mobility_instant.h index 3b8fe4379e..84222dae6f 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_instant.h +++ b/cpp/memilio/mobility/metapopulation_mobility_instant.h @@ -525,25 +525,29 @@ void apply_mobility(FP t, FP dt, MobilityEdge& mobilityEdge, SimulationNode< * @{ */ template -GraphSimulation, MobilityEdge>, FP, FP, - void (*)(double, double, mio::MobilityEdge<>&, mio::SimulationNode&, mio::SimulationNode&), - void (*)(double, double, mio::SimulationNode&)> +GraphSimulation< + Graph, MobilityEdge>, FP, FP, + std::function&, mio::SimulationNode&, mio::SimulationNode&)>, + std::function&)>> make_mobility_sim(FP t0, FP dt, const Graph, MobilityEdge>& graph) { - return make_graph_sim(t0, dt, graph, &evolve_model, - static_cast&, SimulationNode&, SimulationNode&)>( - &apply_mobility)); + return make_graph_sim( + t0, dt, graph, static_cast&)>>(&evolve_model), + static_cast&, SimulationNode&, SimulationNode&)>>( + &apply_mobility)); } template -GraphSimulation, MobilityEdge>, FP, FP, - void (*)(double, double, mio::MobilityEdge<>&, mio::SimulationNode&, mio::SimulationNode&), - void (*)(double, double, mio::SimulationNode&)> +GraphSimulation< + Graph, MobilityEdge>, FP, FP, + std::function&, mio::SimulationNode&, mio::SimulationNode&)>, + std::function&)>> make_mobility_sim(FP t0, FP dt, Graph, MobilityEdge>&& graph) { - return make_graph_sim(t0, dt, std::move(graph), &evolve_model, - static_cast&, SimulationNode&, SimulationNode&)>( - &apply_mobility)); + return make_graph_sim( + t0, dt, std::move(graph), static_cast&)>>(&evolve_model), + static_cast&, SimulationNode&, SimulationNode&)>>( + &apply_mobility)); } /** @} */ diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index ce0dbb32fa..f6306e9cb2 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -284,6 +284,16 @@ auto Model::get_persons() -> Range> return std::make_pair(m_persons.begin(), m_persons.end()); } +auto Model::get_activeness_statuses() const -> Range> +{ + return std::make_pair(m_activeness_statuses.cbegin(), m_activeness_statuses.cend()); +} + +auto Model::get_activeness_statuses() -> Range> +{ + return std::make_pair(m_activeness_statuses.begin(), m_activeness_statuses.end()); +} + LocationId Model::find_location(LocationType type, const uint32_t person_index) const { auto location_id = get_person(person_index).get_assigned_location(type); diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 5f4ad2f7cf..10c212fbbf 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -51,10 +51,12 @@ namespace abm class Model { public: - using LocationIterator = std::vector::iterator; - using ConstLocationIterator = std::vector::const_iterator; - using PersonIterator = std::vector::iterator; - using ConstPersonIterator = std::vector::const_iterator; + using LocationIterator = std::vector::iterator; + using ConstLocationIterator = std::vector::const_iterator; + using PersonIterator = std::vector::iterator; + using ConstPersonIterator = std::vector::const_iterator; + using ActivenessIterator = std::vector::iterator; + using ConstActivenessIterator = std::vector::const_iterator; /** * @brief Create a Model. @@ -211,6 +213,15 @@ class Model Range> get_persons(); /** @} */ + /** + * @brief Get a range of all Person%s activeness statuses in the Model. + * @return A range of all Person%s activeness statuses. + * @{ + */ + Range> get_activeness_statuses() const; + Range> get_activeness_statuses(); + /** @} */ + /** * @brief Find an assigned Location of a Person. * @param[in] type The #LocationType that specifies the assigned Location. @@ -318,15 +329,6 @@ class Model return m_id; } - /** - * Get activeness status of all persons in the model. - * @return Activeness vector - */ - std::vector& get_activeness_statuses() - { - return m_activeness_statuses; - } - /** * @brief Add a TestingScheme to the set of schemes that are checked for testing at all Locations that have * the LocationType. @@ -499,80 +501,6 @@ class Model } /** @} */ - /** - * @brief Flip activeness status of a person in the model. - * @param[in] person_id PersonId of Person whose activeness status is fipped. - */ - void change_activeness(PersonId person_id) - { - m_activeness_statuses[person_id.get()] = !m_activeness_statuses[person_id.get()]; - } - - void set_activeness(PersonId person_id) - { - m_activeness_statuses[person_id.get()] = false; - } - - // /** - // * @brief Copy the persons from another Model to this Model. - // * If the persons are at a location in this model they are activated, otherwise they are deactivated. - // * If necessary the person ids are changed such that they correspond to the index in this model's m_persons vector. - // * @param[in] other The Model the Person%s are copied from. - // */ - // void copy_persons_from_other_model(const Model& other) - // { - // for (auto& p : other.get_persons()) { - // if (p.get_id() != static_cast(m_persons.size())) { - // mio::log_debug("In model.copy_persons_from_other_model: PersonId does not correspond to index in " - // "m_persons vector. Person is copied with adapted Id"); - // } - // PersonId new_id{static_cast(m_persons.size())}; - // m_persons.emplace_back(p, new_id); - // if (p.get_location_model_id() == m_id) { - // m_activeness_statuses.push_back(true); - // } - // else { - // m_activeness_statuses.push_back(false); - // } - // } - // } - - /** - * @brief Set the Person%s of the Model. - * @param[in] persons The Person%s of the Model. - */ - void set_persons(std::vector& persons) - { - m_is_local_population_cache_valid = false; - m_are_exposure_caches_valid = false; - //first clear old person vector and corresponding activeness vector - m_persons.clear(); - m_activeness_statuses.clear(); - for (auto& p : persons) { - if (p.get_id() != static_cast(m_persons.size())) { - mio::log_debug("In model.copy_persons_from_other_model: PersonId does not correspond to index in " - "m_persons vector. Person is copied with adapted Id"); - } - PersonId new_id{static_cast(m_persons.size())}; - m_persons.emplace_back(p, new_id); - if (p.get_location_model_id() == m_id) { - m_activeness_statuses.push_back(true); - } - else { - m_activeness_statuses.push_back(false); - } - } - } - - /** - * @brief Invalidate local population and exposure rate cache. - */ - void invalidate_cache() - { - m_are_exposure_caches_valid = false; - m_is_local_population_cache_valid = false; - } - /** * @brief Get index of person in m_persons. * @param[in] id A person's PersonId. diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index ee834b8a98..241f20a39f 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -96,86 +96,6 @@ class ABMSimulationNode std::tuple m_history; }; -// /** -// * @brief Parameters influencing the mobility between two abm graph nodes. -// */ -// class ABMMobilityParameters -// { - -// public: -// using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); - -// /** -// * Constructor for initializing commuting persons -// * @param commuting_persons Vector holding commuting persons' ids -// */ -// ABMMobilityParameters(const std::vector& commuting_persons, -// const std::vector& mobility_rules) -// : m_commuting_persons(commuting_persons) -// , m_mobility_rules(mobility_rules) -// { -// } - -// /** -// * Equality comparison operators -// */ -// bool operator==(const ABMMobilityParameters& other) const -// { -// return m_commuting_persons == other.m_commuting_persons; -// } -// bool operator!=(const ABMMobilityParameters& other) const -// { -// return m_commuting_persons != other.m_commuting_persons; -// } - -// /** -// * Get/Set the commuting persons vector. -// * The vector represents the persons (by their id) that commute from one node to another -// * according to mobility rules. -// */ -// const std::vector& get_commuting_persons() const -// { -// return m_commuting_persons; -// } - -// std::vector& get_commuting_persons() -// { -// return m_commuting_persons; -// } -// /** -// * @param[in] commuting_persons Vector with commuting person ids. -// */ -// void set_commuting_persons(const std::vector& commuting_persons) -// { -// m_commuting_persons = commuting_persons; -// } - -// /** -// * Get/ the mobility rules. -// * The rules are applied to the persons in m_commuting_persons every time exchange betwen two nodes is triggered. -// */ -// const std::vector& get_mobility_rules() const -// { -// return m_mobility_rules; -// } -// std::vector& get_mobility_rules() -// { -// return m_mobility_rules; -// } -// /** -// * @brief Add mobility rule to member vector. -// * @param[in] mobility_rule Rule to be added for mobility between nodes. -// */ -// void add_mobility_rule(const MobilityRuleType& mobility_rule) -// { -// m_mobility_rules.push_back(mobility_rule); -// } - -// private: -// std::vector m_commuting_persons; ///< Person ids that are commuting via an edge -// std::vector m_mobility_rules; ///< Rules for moving persons from one node to another -// }; - /** * Represents the mobility between two nodes. */ @@ -184,30 +104,6 @@ class ABMMobilityEdge { public: - // using MobilityRuleType = abm::LocationType (*)(const abm::Person&, abm::TimePoint, const abm::Parameters&); - // /** - // * Creates edge with mobility parameters - // * @param params mobility parameters including people commuting via the edge and mobility rules - // */ - // ABMMobilityEdge(const ABMMobilityParameters& params) - // : m_parameters(params) - // { - // } - - // ABMMobilityEdge(const std::vector& commuting_persons, - // const std::vector& mobility_rules = {}) - // : m_parameters(commuting_persons, mobility_rules) - // { - // } - - // /** - // * @brief Get mobility paramters. - // */ - // const ABMMobilityParameters& get_parameters() const - // { - // return m_parameters; - // } - /** * @brief Exchanges persons via the edge. * Commuters are given by the ABMMobilityParameters and exchanged via mobility rules also given ABMMobilityParameters. @@ -243,9 +139,6 @@ class ABMMobilityEdge } } } - - // private: - // ABMMobilityParameters m_parameters; ///< Mobility parameters }; /** From a34989e9f2749acf734f9d2b900c83ac7dc3c06d Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 11:50:23 +0200 Subject: [PATCH 030/111] bug fix --- cpp/examples/graph_abm.cpp | 6 ++-- cpp/memilio/mobility/graph_simulation.h | 6 ++-- .../metapopulation_mobility_instant.h | 29 +++++++++---------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 70a32d995e..1646a6ef41 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -25,7 +25,7 @@ #include "abm/time.h" #include "abm/person_id.h" #include "graph_abm/graph_abm_mobility.h" -#include "graph_abm/mobility_rules.h" +#include "graph_abm/model_wrapper.h" #include "memilio/io/history.h" #include "memilio/mobility/graph.h" #include @@ -72,7 +72,7 @@ int main() const auto age_group_adults = mio::AgeGroup(1); const auto age_group_seniors = mio::AgeGroup(2); - auto model1 = mio::abm::Model(num_age_groups, 0); + auto model1 = mio::abm::WrapperModel(num_age_groups, 0); //Set infection parameters model1.parameters.get() = 4.; @@ -271,4 +271,4 @@ int main() auto& log_n2 = std::get<0>(sim.get_graph().nodes()[1].property.get_history()).get_log(); return 0; -} \ No newline at end of file +} diff --git a/cpp/memilio/mobility/graph_simulation.h b/cpp/memilio/mobility/graph_simulation.h index dacf336834..a0ff2250f2 100644 --- a/cpp/memilio/mobility/graph_simulation.h +++ b/cpp/memilio/mobility/graph_simulation.h @@ -85,9 +85,9 @@ class GraphSimulationBase }; template , - class node_f = std::function> + class edge_f = void (*)(Timepoint, Timespan, typename Graph::EdgeProperty&, typename Graph::NodeProperty&, + typename Graph::NodeProperty&), + class node_f = void (*)(Timepoint, Timespan, typename Graph::NodeProperty&)> class GraphSimulation : public GraphSimulationBase { using Base = GraphSimulationBase; diff --git a/cpp/memilio/mobility/metapopulation_mobility_instant.h b/cpp/memilio/mobility/metapopulation_mobility_instant.h index 84222dae6f..5538ec1ab0 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_instant.h +++ b/cpp/memilio/mobility/metapopulation_mobility_instant.h @@ -525,29 +525,26 @@ void apply_mobility(FP t, FP dt, MobilityEdge& mobilityEdge, SimulationNode< * @{ */ template -GraphSimulation< - Graph, MobilityEdge>, FP, FP, - std::function&, mio::SimulationNode&, mio::SimulationNode&)>, - std::function&)>> +GraphSimulation, MobilityEdge>, FP, FP, + void (*)(double, double, mio::MobilityEdge<>&, mio::SimulationNode&, mio::SimulationNode&), + void (*)(double, double, mio::SimulationNode&)> make_mobility_sim(FP t0, FP dt, const Graph, MobilityEdge>& graph) { - return make_graph_sim( - t0, dt, graph, static_cast&)>>(&evolve_model), - static_cast&, SimulationNode&, SimulationNode&)>>( - &apply_mobility)); + return make_graph_sim(t0, dt, graph, static_cast&)>(&evolve_model), + static_cast&, SimulationNode&, SimulationNode&)>( + &apply_mobility)); } template -GraphSimulation< - Graph, MobilityEdge>, FP, FP, - std::function&, mio::SimulationNode&, mio::SimulationNode&)>, - std::function&)>> +GraphSimulation, MobilityEdge>, FP, FP, + void (*)(double, double, mio::MobilityEdge<>&, mio::SimulationNode&, mio::SimulationNode&), + void (*)(double, double, mio::SimulationNode&)> make_mobility_sim(FP t0, FP dt, Graph, MobilityEdge>&& graph) { - return make_graph_sim( - t0, dt, std::move(graph), static_cast&)>>(&evolve_model), - static_cast&, SimulationNode&, SimulationNode&)>>( - &apply_mobility)); + return make_graph_sim(t0, dt, std::move(graph), + static_cast&)>(&evolve_model), + static_cast&, SimulationNode&, SimulationNode&)>( + &apply_mobility)); } /** @} */ From 9d76ae639c1873abdbfa85c6f42950d461711d8d Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 15:18:48 +0200 Subject: [PATCH 031/111] add example --- cpp/examples/CMakeLists.txt | 7 +- cpp/examples/graph_abm.cpp | 241 +++++++++++++++++----------------- cpp/models/abm/personal_rng.h | 3 +- 3 files changed, 124 insertions(+), 127 deletions(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index d5397d00b7..6c4631e685 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -114,9 +114,10 @@ add_executable(history_example history.cpp) target_link_libraries(history_example PRIVATE memilio) target_compile_options(history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -# add_executable(graph_abm_example graph_abm.cpp) -# target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) -# target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +add_executable(graph_abm_example graph_abm.cpp) +target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm) +target_compile_options(graph_abm_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + if(MEMILIO_HAS_JSONCPP) add_executable(ode_secir_read_graph_example ode_secir_read_graph.cpp) target_link_libraries(ode_secir_read_graph_example PRIVATE memilio ode_secir) diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 1646a6ef41..20fdb1aa2c 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -45,9 +45,10 @@ struct Logger : mio::LogAlways { */ using Type = std::vector>>; - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation& sim) { Type location_information{}; + location_information.reserve(size_t(mio::abm::LocationType::Count)); auto t = sim.get_time(); for (auto&& loc : sim.get_model().get_locations()) { std::map persons_per_infection_state; @@ -72,7 +73,7 @@ int main() const auto age_group_adults = mio::AgeGroup(1); const auto age_group_seniors = mio::AgeGroup(2); - auto model1 = mio::abm::WrapperModel(num_age_groups, 0); + auto model1 = mio::ModelWrapper(num_age_groups, 1); //Set infection parameters model1.parameters.get() = 4.; @@ -116,159 +117,153 @@ int main() family_hh.add_members(child, 1); family_hh.add_members(parent, 2); - // Vector holding all persons for the graph simulation. This vector is copied to all models at the end. - std::vector persons; - //Household groups for model 1 - auto single_hh_group_w1 = mio::abm::HouseholdGroup(); - single_hh_group_w1.add_households(single_hh, 5); - auto two_adult_hh_group_w1 = mio::abm::HouseholdGroup(); - two_adult_hh_group_w1.add_households(two_adult_hh, 3); - auto single_parent_hh_group_w1 = mio::abm::HouseholdGroup(); - single_parent_hh_group_w1.add_households(single_parent_hh, 5); - auto family_hh_group_w1 = mio::abm::HouseholdGroup(); - family_hh_group_w1.add_households(family_hh, 10); - add_household_group_to_model(model1, single_hh_group_w1); - add_household_group_to_model(model1, two_adult_hh_group_w1); - add_household_group_to_model(model1, single_hh_group_w1); - add_household_group_to_model(model1, family_hh_group_w1); + auto single_hh_group_m1 = mio::abm::HouseholdGroup(); + single_hh_group_m1.add_households(single_hh, 5); + auto two_adult_hh_group_m1 = mio::abm::HouseholdGroup(); + two_adult_hh_group_m1.add_households(two_adult_hh, 3); + auto single_parent_hh_group_m1 = mio::abm::HouseholdGroup(); + single_parent_hh_group_m1.add_households(single_parent_hh, 5); + auto family_hh_group_m1 = mio::abm::HouseholdGroup(); + family_hh_group_m1.add_households(family_hh, 10); + add_household_group_to_model(model1, single_hh_group_m1); + add_household_group_to_model(model1, two_adult_hh_group_m1); + add_household_group_to_model(model1, single_hh_group_m1); + add_household_group_to_model(model1, family_hh_group_m1); - //add persons from model 0 to vector - for (auto& person : model1.get_persons()) { - mio::abm::PersonId new_id{static_cast(persons.size())}; - persons.emplace_back(person, new_id); - } + auto model2 = mio::ModelWrapper(num_age_groups, 2); - auto model2 = mio::abm::Model(num_age_groups, 1); + //Set infection parameters + model1.parameters.get() = 4.; + model1.parameters.get() = 2.; + model1.parameters.get() = 4.; + model1.parameters.get() = 5.; + model1.parameters.get() = 6.; + model1.parameters.get() = 8.; + model1.parameters.get() = 7.; + model1.parameters.get() = 10.; + model1.parameters.get() = 11.; - //Household groups for model 2 - auto single_hh_group_w2 = mio::abm::HouseholdGroup(); - single_hh_group_w2.add_households(single_hh, 6); - auto two_adult_hh_group_w2 = mio::abm::HouseholdGroup(); - two_adult_hh_group_w2.add_households(two_adult_hh, 2); - auto single_parent_hh_group_w2 = mio::abm::HouseholdGroup(); - single_parent_hh_group_w2.add_households(single_parent_hh, 10); - auto family_hh_group_w2 = mio::abm::HouseholdGroup(); - family_hh_group_w2.add_households(family_hh, 11); - add_household_group_to_model(model2, single_hh_group_w2); - add_household_group_to_model(model2, two_adult_hh_group_w2); - add_household_group_to_model(model2, single_hh_group_w2); - add_household_group_to_model(model2, family_hh_group_w2); + //Age group 0 goes to school and age group 1 goes to work + model1.parameters.get()[age_group_children] = true; + model1.parameters.get()[age_group_adults] = true; - //add persons from model 1 to vector - for (auto& person : model2.get_persons()) { - mio::abm::PersonId new_id{static_cast(persons.size())}; - persons.emplace_back(person, new_id); - } + //Household groups for model 2 + auto single_hh_group_m2 = mio::abm::HouseholdGroup(); + single_hh_group_m2.add_households(single_hh, 6); + auto two_adult_hh_group_m2 = mio::abm::HouseholdGroup(); + two_adult_hh_group_m2.add_households(two_adult_hh, 2); + auto single_parent_hh_group_m2 = mio::abm::HouseholdGroup(); + single_parent_hh_group_m2.add_households(single_parent_hh, 10); + auto family_hh_group_m2 = mio::abm::HouseholdGroup(); + family_hh_group_m2.add_households(family_hh, 11); + add_household_group_to_model(model2, single_hh_group_m2); + add_household_group_to_model(model2, two_adult_hh_group_m2); + add_household_group_to_model(model2, single_hh_group_m2); + add_household_group_to_model(model2, family_hh_group_m2); //Create locations for both models - //model 0 - auto event_w1 = model1.add_location(mio::abm::LocationType::SocialEvent); - model1.get_location(event_w1).get_infection_parameters().set(10); - auto hospital_w1 = model1.add_location(mio::abm::LocationType::Hospital); - model1.get_location(hospital_w1).get_infection_parameters().set(10); - auto icu_w1 = model1.add_location(mio::abm::LocationType::ICU); - model1.get_location(icu_w1).get_infection_parameters().set(5); - auto shop_w1 = model1.add_location(mio::abm::LocationType::BasicsShop); - model1.get_location(shop_w1).get_infection_parameters().set(20); - auto school_w1 = model1.add_location(mio::abm::LocationType::School); - model1.get_location(school_w1).get_infection_parameters().set(20); - auto work_w1 = model1.add_location(mio::abm::LocationType::Work); - model1.get_location(work_w1).get_infection_parameters().set(10); //model 1 - auto event_w2 = model2.add_location(mio::abm::LocationType::SocialEvent); - model2.get_location(event_w2).get_infection_parameters().set(10); - auto hospital_w2 = model2.add_location(mio::abm::LocationType::Hospital); - model2.get_location(hospital_w2).get_infection_parameters().set(10); - auto icu_w2 = model2.add_location(mio::abm::LocationType::ICU); - model2.get_location(icu_w2).get_infection_parameters().set(5); - auto shop_w2 = model2.add_location(mio::abm::LocationType::BasicsShop); - model2.get_location(shop_w2).get_infection_parameters().set(20); - auto school_w2 = model2.add_location(mio::abm::LocationType::School); - model2.get_location(school_w2).get_infection_parameters().set(20); - auto work_w2 = model2.add_location(mio::abm::LocationType::Work); - model2.get_location(work_w2).get_infection_parameters().set(10); + auto event_m1 = model1.add_location(mio::abm::LocationType::SocialEvent); + model1.get_location(event_m1).get_infection_parameters().set(10); + auto hospital_m1 = model1.add_location(mio::abm::LocationType::Hospital); + model1.get_location(hospital_m1).get_infection_parameters().set(10); + auto icu_m1 = model1.add_location(mio::abm::LocationType::ICU); + model1.get_location(icu_m1).get_infection_parameters().set(5); + auto shop_m1 = model1.add_location(mio::abm::LocationType::BasicsShop); + model1.get_location(shop_m1).get_infection_parameters().set(20); + auto school_m1 = model1.add_location(mio::abm::LocationType::School); + model1.get_location(school_m1).get_infection_parameters().set(20); + auto work_m1 = model1.add_location(mio::abm::LocationType::Work); + model1.get_location(work_m1).get_infection_parameters().set(10); + //model 2 + auto event_m2 = model2.add_location(mio::abm::LocationType::SocialEvent); + model2.get_location(event_m2).get_infection_parameters().set(10); + auto hospital_m2 = model2.add_location(mio::abm::LocationType::Hospital); + model2.get_location(hospital_m2).get_infection_parameters().set(10); + auto icu_m2 = model2.add_location(mio::abm::LocationType::ICU); + model2.get_location(icu_m2).get_infection_parameters().set(5); + auto shop_m2 = model2.add_location(mio::abm::LocationType::BasicsShop); + model2.get_location(shop_m2).get_infection_parameters().set(20); + auto school_m2 = model2.add_location(mio::abm::LocationType::School); + model2.get_location(school_m2).get_infection_parameters().set(20); + auto work_m2 = model2.add_location(mio::abm::LocationType::Work); + model2.get_location(work_m2).get_infection_parameters().set(10); auto start_date = mio::abm::TimePoint(0); auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - std::vector params_e1; - std::vector params_e2; + mio::unused(end_date); - //Assign infection states and locations - std::vector infection_distribution{0.5, 0.3, 0.05, 0.05, 0.05, 0.05, 0.0, 0.0}; - for (auto& person : persons) { + //Assign infection states and locations to persons from model 1 + std::vector infection_distribution_m1{0.5, 0.3, 0.05, 0.05, 0.05, 0.05, 0.0, 0.0}; + for (auto& person : model1.get_persons()) { mio::abm::InfectionState infection_state = mio::abm::InfectionState( - mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), infection_distribution)); - auto rng = mio::abm::PersonalRandomNumberGenerator(mio::thread_local_rng(), person); + mio::DiscreteDistribution::get_instance()(model1.get_rng(), infection_distribution_m1)); + auto rng = mio::abm::PersonalRandomNumberGenerator(model1.get_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), model1.parameters, start_date, infection_state)); } - // Assign locations to persons from model 1 - if (person.get_assigned_location_model_id(mio::abm::LocationType::Home) == model1.get_id()) { - person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w1, model1.get_id()); - person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w1, model1.get_id()); - person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w1, model1.get_id()); - person.set_assigned_location(mio::abm::LocationType::ICU, icu_w1, model1.get_id()); - if (person.get_age() == age_group_children) { - person.set_assigned_location(mio::abm::LocationType::School, school_w1, model1.get_id()); + person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_m1, model1.get_id()); + person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_m1, model1.get_id()); + person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_m1, model1.get_id()); + person.set_assigned_location(mio::abm::LocationType::ICU, icu_m1, model1.get_id()); + if (person.get_age() == age_group_children) { + person.set_assigned_location(mio::abm::LocationType::School, school_m1, model1.get_id()); + } + if (person.get_age() == age_group_adults) { + //10% of adults in model 1 work in model 2 + size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), + std::vector{0.9, 0.1}); + if (work_model == 1) { //person works in other model + person.set_assigned_location(mio::abm::LocationType::Work, work_m2, model2.get_id()); } - if (person.get_age() == age_group_adults) { - //10% of adults in model 0 work in model 1 - size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), - std::vector{0.9, 0.1}); - if (work_model == 1) { //person works in other model - person.set_assigned_location(mio::abm::LocationType::Work, work_w2, model2.get_id()); - //add person to edge parameters - params_e1.push_back(person.get_id().get()); - } - else { //person works in same model - person.set_assigned_location(mio::abm::LocationType::Work, work_w1, model1.get_id()); - } + else { //person works in same model + person.set_assigned_location(mio::abm::LocationType::Work, work_m1, model1.get_id()); } } - else { - person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w2, model2.get_id()); - person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w2, model2.get_id()); - person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w2, model2.get_id()); - person.set_assigned_location(mio::abm::LocationType::ICU, icu_w2, model2.get_id()); - if (person.get_age() == age_group_children) { - person.set_assigned_location(mio::abm::LocationType::School, school_w2, model2.get_id()); + } + + //Assign infection states and locations to persons from model 2 + std::vector infection_distribution_m2{0.7, 0.1, 0.05, 0.05, 0.1, 0.0, 0.0, 0.0}; + for (auto& person : model2.get_persons()) { + mio::abm::InfectionState infection_state = mio::abm::InfectionState( + mio::DiscreteDistribution::get_instance()(model2.get_rng(), infection_distribution_m2)); + auto rng = mio::abm::PersonalRandomNumberGenerator(model2.get_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) { + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + model2.parameters, start_date, infection_state)); + } + person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_m2, model2.get_id()); + person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_m2, model2.get_id()); + person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_m2, model2.get_id()); + person.set_assigned_location(mio::abm::LocationType::ICU, icu_m2, model2.get_id()); + if (person.get_age() == age_group_children) { + person.set_assigned_location(mio::abm::LocationType::School, school_m2, model2.get_id()); + } + if (person.get_age() == age_group_adults) { + //20% of adults in model 2 work in model 1 + size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), + std::vector{0.2, 0.8}); + if (work_model == 1) { //person works in same model + person.set_assigned_location(mio::abm::LocationType::Work, work_m2, model2.get_id()); } - if (person.get_age() == age_group_adults) { - //20% of adults in model 1 work in model 0 - size_t work_model = mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), - std::vector{0.2, 0.8}); - if (work_model == 0) { //person works in other model - person.set_assigned_location(mio::abm::LocationType::Work, work_w1, model1.get_id()); - //add person to edge parameters - params_e2.push_back(person.get_id().get()); - } - else { //person works in same model - person.set_assigned_location(mio::abm::LocationType::Work, work_w2, model2.get_id()); - } + else { //person works in other model + person.set_assigned_location(mio::abm::LocationType::Work, work_m1, model1.get_id()); } } } - //copy persons to both models - model1.set_persons(persons); - model2.set_persons(persons); - using HistoryType = mio::History; mio::Graph, mio::ABMMobilityEdge> graph; + mio::unused(graph); graph.add_node(model1.get_id(), HistoryType{}, start_date, std::move(model1)); graph.add_node(model2.get_id(), HistoryType{}, start_date, std::move(model2)); - graph.add_edge(0, 1, params_e1, - std::vector::MobilityRuleType>{&mio::apply_commuting}); - graph.add_edge(1, 0, params_e2, - std::vector::MobilityRuleType>{&mio::apply_commuting}); + graph.add_edge(0, 1); + graph.add_edge(1, 0); auto sim = mio::make_abm_graph_sim(start_date, mio::abm::hours(12), std::move(graph)); sim.advance(end_date); - auto& log_n1 = std::get<0>(sim.get_graph().nodes()[0].property.get_history()).get_log(); - auto& log_n2 = std::get<0>(sim.get_graph().nodes()[1].property.get_history()).get_log(); - return 0; } diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h index 654e8dea5a..99f1287bf0 100644 --- a/cpp/models/abm/personal_rng.h +++ b/cpp/models/abm/personal_rng.h @@ -23,6 +23,7 @@ #include "memilio/utils/random_number_generator.h" #include "abm/person_id.h" +#include namespace mio { @@ -77,7 +78,7 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase get_counter() const { - return mio::rng_totalsequence_counter(m_person_id.get(), m_counter); + return mio::rng_totalsequence_counter(static_cast(m_person_id.get()), m_counter); } /** From 56c9708efc0a0c2eeea64a481bc21584c8615f1d Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 15:46:24 +0200 Subject: [PATCH 032/111] fix bindings --- .../memilio-simulation/memilio/simulation/abm.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 54a5db47a7..83c48640ff 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -183,14 +183,16 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_Range().get_persons())>(m, "_ModelPersonsRange"); pymio::bind_class(m, "Trip") - .def(py::init>(), - py::arg("person_id"), py::arg("time"), py::arg("destination"), py::arg("origin"), - py::arg("cells") = std::vector()) + py::arg("person_id"), py::arg("time"), py::arg("destination"), py::arg("destination_model_id"), + py::arg("origin"), py::arg("origin_model_id"), py::arg("cells") = std::vector()) .def_readwrite("person_id", &mio::abm::Trip::person_id) .def_readwrite("time", &mio::abm::Trip::time) .def_readwrite("destination", &mio::abm::Trip::destination) + .def_readwrite("destination_model_id", &mio::abm::Trip::destination_model_id) .def_readwrite("origin", &mio::abm::Trip::origin) + .def_readwrite("destination_model_id", &mio::abm::Trip::origin_model_id) .def_readwrite("cells", &mio::abm::Trip::cells); pymio::bind_class(m, "TripList") @@ -225,12 +227,12 @@ PYBIND11_MODULE(_simulation_abm, m) }, py::return_value_policy::reference_internal); - pymio::bind_class(m, "Simulation") + pymio::bind_class, pymio::EnablePickling::Never>(m, "Simulation") .def(py::init()) .def("advance", - static_cast(&mio::abm::Simulation::advance), + static_cast::*)(mio::abm::TimePoint)>(&mio::abm::Simulation<>::advance), py::arg("tmax")) - .def_property_readonly("model", py::overload_cast<>(&mio::abm::Simulation::get_model)); + .def_property_readonly("model", py::overload_cast<>(&mio::abm::Simulation<>::get_model)); } PYMIO_IGNORE_VALUE_TYPE(decltype(std::declval().get_locations())) From 251012c86eb1e28e7b74a9a688ae70d72d2455f7 Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 7 Aug 2024 16:06:09 +0200 Subject: [PATCH 033/111] python bindings --- pycode/memilio-simulation/memilio/simulation/abm.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 83c48640ff..1bdf881772 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -184,15 +184,18 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_class(m, "Trip") .def(py::init>(), + mio::abm::TransportMode, mio::abm::LocationType, std::vector>(), py::arg("person_id"), py::arg("time"), py::arg("destination"), py::arg("destination_model_id"), - py::arg("origin"), py::arg("origin_model_id"), py::arg("cells") = std::vector()) + py::arg("origin"), py::arg("origin_model_id"), py::arg("trip_mode") = mio::abm::TransportMode::Unknown, + py::arg("destination_type") = mio::abm::LocationType::Home, py::arg("cells") = std::vector()) .def_readwrite("person_id", &mio::abm::Trip::person_id) .def_readwrite("time", &mio::abm::Trip::time) .def_readwrite("destination", &mio::abm::Trip::destination) .def_readwrite("destination_model_id", &mio::abm::Trip::destination_model_id) .def_readwrite("origin", &mio::abm::Trip::origin) .def_readwrite("destination_model_id", &mio::abm::Trip::origin_model_id) + .def_readwrite("trip_mode", &mio::abm::Trip::trip_mode) + .def_readwrite("destination_type", &mio::abm::Trip::destination_type) .def_readwrite("cells", &mio::abm::Trip::cells); pymio::bind_class(m, "TripList") From b9a777dbd1e80caddec28605fd9e9f4fab4a58be Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 8 Aug 2024 08:02:52 +0200 Subject: [PATCH 034/111] fix python test --- 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 fa1e40f62c..6019fb1a23 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -106,9 +106,9 @@ def test_simulation(self): # trips trip_list = abm.TripList() trip_list.add_trip(abm.Trip(0, abm.TimePoint( - 0) + abm.hours(8), social_event_id, home_id)) + 0) + abm.hours(8), social_event_id, 0, home_id, 0)) trip_list.add_trip(abm.Trip(1, abm.TimePoint(0) + - abm.hours(8), work_id, home_id)) + abm.hours(8), work_id, 0, home_id, 0)) model.trip_list = trip_list model.use_mobility_rules = False self.assertEqual(model.trip_list.num_trips(), 2) From 8478a941ed1788b0042850df7653becdc65f5f7f Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 8 Aug 2024 11:42:25 +0200 Subject: [PATCH 035/111] fix python test --- cpp/models/abm/mobility_data.h | 3 ++- .../memilio/simulation/abm.cpp | 16 +++++++++++++--- .../memilio/simulation_test/test_abm.py | 8 ++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cpp/models/abm/mobility_data.h b/cpp/models/abm/mobility_data.h index be536ac324..74de94ca22 100644 --- a/cpp/models/abm/mobility_data.h +++ b/cpp/models/abm/mobility_data.h @@ -39,7 +39,8 @@ enum class TransportMode : uint32_t PublicTransport, Walking, Other, - Unknown + Unknown, + Count //last!! }; /** diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 1bdf881772..da53084d5e 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -19,6 +19,7 @@ */ //Includes from pymio +#include "abm/mobility_data.h" #include "pybind_util.h" #include "utils/custom_index_array.h" #include "utils/parameter_set.h" @@ -67,6 +68,15 @@ PYBIND11_MODULE(_simulation_abm, m) .value("PublicTransport", mio::abm::LocationType::PublicTransport) .value("TransportWithoutContact", mio::abm::LocationType::TransportWithoutContact); + pymio::iterable_enum(m, "TransportMode") + .value("Bike", mio::abm::TransportMode::Bike) + .value("CarDriver", mio::abm::TransportMode::CarDriver) + .value("CarPassenger", mio::abm::TransportMode::CarPassenger) + .value("PublicTransport", mio::abm::TransportMode::PublicTransport) + .value("Walking", mio::abm::TransportMode::Walking) + .value("Other", mio::abm::TransportMode::Other) + .value("Unknown", mio::abm::TransportMode::Unknown); + pymio::iterable_enum(m, "TestType") .value("Generic", mio::abm::TestType::Generic) .value("Antigen", mio::abm::TestType::Antigen) @@ -186,8 +196,8 @@ PYBIND11_MODULE(_simulation_abm, m) .def(py::init>(), py::arg("person_id"), py::arg("time"), py::arg("destination"), py::arg("destination_model_id"), - py::arg("origin"), py::arg("origin_model_id"), py::arg("trip_mode") = mio::abm::TransportMode::Unknown, - py::arg("destination_type") = mio::abm::LocationType::Home, py::arg("cells") = std::vector()) + py::arg("origin"), py::arg("origin_model_id"), py::arg("trip_mode"), py::arg("destination_type"), + py::arg("cells") = std::vector()) .def_readwrite("person_id", &mio::abm::Trip::person_id) .def_readwrite("time", &mio::abm::Trip::time) .def_readwrite("destination", &mio::abm::Trip::destination) @@ -209,7 +219,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def("add_location", &mio::abm::Model::add_location, py::arg("location_type"), py::arg("num_cells") = 1) .def("add_person", py::overload_cast(&mio::abm::Model::add_person), py::arg("location_id"), py::arg("age_group")) - .def("assign_location", &mio::abm::Model::assign_location, py::arg("person_id"), py::arg("location_id")) + .def("assign_location", &mio::abm::Model::assign_location, py::arg("person_index"), py::arg("location_id")) .def_property_readonly("locations", py::overload_cast<>(&mio::abm::Model::get_locations, py::const_), py::keep_alive<1, 0>{}) //keep this model alive while contents are referenced in ranges .def_property_readonly("persons", py::overload_cast<>(&mio::abm::Model::get_persons, py::const_), diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index 6019fb1a23..accd6589d5 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -95,8 +95,8 @@ def test_simulation(self): p2_id = model.add_person(home_id, mio.AgeGroup(2)) for loc_id in [home_id, social_event_id, work_id]: - model.assign_location(p1_id, loc_id) - model.assign_location(p2_id, loc_id) + model.assign_location(0, loc_id) + model.assign_location(1, loc_id) model.parameters.InfectedSymptomsToSevere[abm.VirusVariant.Wildtype, mio.AgeGroup( 0)] = 0.0 @@ -106,9 +106,9 @@ def test_simulation(self): # trips trip_list = abm.TripList() trip_list.add_trip(abm.Trip(0, abm.TimePoint( - 0) + abm.hours(8), social_event_id, 0, home_id, 0)) + 0) + abm.hours(8), social_event_id, 0, home_id, 0, abm.TransportMode.Unknown, abm.LocationType.SocialEvent)) trip_list.add_trip(abm.Trip(1, abm.TimePoint(0) + - abm.hours(8), work_id, 0, home_id, 0)) + abm.hours(8), work_id, 0, home_id, 0, abm.TransportMode.Unknown, abm.LocationType.Work)) model.trip_list = trip_list model.use_mobility_rules = False self.assertEqual(model.trip_list.num_trips(), 2) From 41f7f21b3edde28f0edde1a615f648164d559da5 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 8 Aug 2024 13:01:36 +0200 Subject: [PATCH 036/111] adjust benchmark --- cpp/benchmarks/abm.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 45d06ae8ca..a2e0905583 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -2,6 +2,8 @@ #include "benchmark/benchmark.h" #include +#include +#include mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list seeds) { @@ -27,7 +29,7 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list auto age = mio::AgeGroup(mio::UniformIntDistribution::get_instance()( model.get_rng(), size_t(0), model.parameters.get_num_groups() - 1)); auto person = model.add_person(home, age); - model.assign_location(person, home); + model.assign_location(uint32_t(i), home); home_size++; } @@ -41,10 +43,10 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list std::generate(locs.begin(), locs.end(), [&] { return model.add_location(loc_type); }); - for (auto& person : model.get_persons()) { + for (size_t p = 0; p < num_persons; ++p) { auto loc_idx = mio::UniformIntDistribution::get_instance()(model.get_rng(), size_t(0), num_locs - 1); - model.assign_location(person.get_id(), locs[loc_idx]); + model.assign_location(uint32_t(p), locs[loc_idx]); } } From c29f670264665b562a9c4f37d1e165a56751996e Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 8 Aug 2024 15:17:59 +0200 Subject: [PATCH 037/111] extend tests --- cpp/examples/graph_abm.cpp | 1 - cpp/models/graph_abm/CMakeLists.txt | 2 - cpp/models/graph_abm/mobility_rules.cpp | 63 ---------------------- cpp/models/graph_abm/mobility_rules.h | 43 --------------- cpp/models/graph_abm/model_wrapper.h | 7 ++- cpp/tests/test_graph_abm.cpp | 70 ++++++++++++++++++++----- 6 files changed, 62 insertions(+), 124 deletions(-) delete mode 100644 cpp/models/graph_abm/mobility_rules.cpp delete mode 100644 cpp/models/graph_abm/mobility_rules.h diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 20fdb1aa2c..7a143e3336 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -256,7 +256,6 @@ int main() using HistoryType = mio::History; mio::Graph, mio::ABMMobilityEdge> graph; - mio::unused(graph); graph.add_node(model1.get_id(), HistoryType{}, start_date, std::move(model1)); graph.add_node(model2.get_id(), HistoryType{}, start_date, std::move(model2)); graph.add_edge(0, 1); diff --git a/cpp/models/graph_abm/CMakeLists.txt b/cpp/models/graph_abm/CMakeLists.txt index d21b2cb4ea..571b774afb 100644 --- a/cpp/models/graph_abm/CMakeLists.txt +++ b/cpp/models/graph_abm/CMakeLists.txt @@ -3,8 +3,6 @@ add_library(graph_abm model_wrapper.cpp graph_abm_mobility.cpp graph_abm_mobility.h - mobility_rules.cpp - mobility_rules.h ) target_link_libraries(graph_abm PUBLIC memilio) diff --git a/cpp/models/graph_abm/mobility_rules.cpp b/cpp/models/graph_abm/mobility_rules.cpp deleted file mode 100644 index b22e264aec..0000000000 --- a/cpp/models/graph_abm/mobility_rules.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* -* Copyright (C) 2020-2024 MEmilio -* -* Authors: Julia Bicker -* -* Contact: Martin J. Kuehn -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "graph_abm/mobility_rules.h" -#include "abm/location.h" -#include "abm/infection_state.h" - -namespace mio -{ - -abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, const abm::Parameters& params) -{ - abm::LocationType current_loc = person.get_location_type(); - - if (current_loc == abm::LocationType::Home && params.get()[person.get_age()] && - t < params.get() && t.day_of_week() < 5 && person.goes_to_work(t, params) && - !person.is_in_quarantine(t, params)) { - return abm::LocationType::Work; - } - - //person is at hospital in non-home model - if (current_loc == abm::LocationType::Hospital && - person.get_location_model_id() != person.get_assigned_location_model_id(abm::LocationType::Home)) { - //if person is still infected it stays at hospital - if (person.is_infected(t)) { - return current_loc; - } - //if person has recovered it goes home - else if (person.get_infection_state(t) == abm::InfectionState::Recovered) { - return abm::LocationType::Home; - } - //if person is dead it is sent to cementary - else { - return abm::LocationType::Cemetery; - } - } - //person is at location in Home model (and should not go to work) it stays at that location - if (person.get_location_model_id() == person.get_assigned_location_model_id(abm::LocationType::Home)) { - return current_loc; - } - - // agents are sent home or to work (if none of the above cases occurs) every time this function is called - // i.e. if it is called too often they will be sent to work multiple times - return abm::LocationType::Home; -} -} // namespace mio \ No newline at end of file diff --git a/cpp/models/graph_abm/mobility_rules.h b/cpp/models/graph_abm/mobility_rules.h deleted file mode 100644 index 60078d10ab..0000000000 --- a/cpp/models/graph_abm/mobility_rules.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright (C) 2020-2024 MEmilio -* -* Authors: Julia Bicker -* -* Contact: Martin J. Kuehn -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#ifndef MIO_ABM_GRAPH_MOBILITY_RULES_H -#define MIO_ABM_GRAPH_MOBILITY_RULES_H - -#include "abm/location_type.h" -#include "abm/time.h" -#include "abm/parameters.h" -#include "abm/person.h" - -namespace mio -{ - -/** - * @brief Once a day commuters go to work in another node. - * @param[in] person Person the rule is applies to - * @param[in] t Current time point - * @param[in] params Parameters of person's Home model - * @return LocationType the person is going to - */ -abm::LocationType apply_commuting(const abm::Person& person, abm::TimePoint t, const abm::Parameters& params); - -} // namespace mio - -#endif //MIO_ABM_GRAPH_MOBILITY_RULES_H \ No newline at end of file diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 061caa086e..a94e66b4dd 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -40,14 +40,17 @@ class ModelWrapper : public abm::Model public: /** - * @brief Get person buffer for given model id. + * @brief Get person buffer. */ std::vector& get_person_buffer() { return m_person_buffer; } - //TODO comment + /** + * @brief Removes person from the model. + * @param[in] pos Index of person in m_persons vector. + */ void remove_person(size_t pos) { Base::m_persons.erase(Base::m_persons.begin() + pos); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 8e0ba099f7..62a6923089 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -20,14 +20,15 @@ #include "abm/location.h" #include "abm/location_id.h" +#include "abm/mobility_data.h" #include "abm/model.h" #include "graph_abm/model_wrapper.h" #include "abm/location_type.h" #include "abm/time.h" #include "graph_abm/graph_abm_mobility.h" -#include "graph_abm/mobility_rules.h" #include "memilio/epidemiology/age_group.h" #include "memilio/utils/miompi.h" +#include "memilio/mobility/graph.h" #include #include #include @@ -67,29 +68,51 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { - auto model1 = mio::ModelWrapper(size_t(1), 1); - auto model2 = mio::ModelWrapper(size_t(1), 2); + auto model1 = mio::ModelWrapper(size_t(2), 1); + auto model2 = mio::ModelWrapper(size_t(2), 2); model1.parameters.get()[mio::AgeGroup(0)] = true; model2.parameters.get()[mio::AgeGroup(0)] = true; - auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); - auto home_id = model1.add_location(mio::abm::LocationType::Home); - auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); - auto& work_1 = model1.get_location(work_id_1); - auto& work_2 = model2.get_location(work_id_2); - auto& home = model1.get_location(home_id); + auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); + auto home_id = model1.add_location(mio::abm::LocationType::Home); + auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); + auto event_id_1 = model1.add_location(mio::abm::LocationType::SocialEvent); + auto event_id_2 = model2.add_location(mio::abm::LocationType::SocialEvent); + auto& work_1 = model1.get_location(work_id_1); + auto& work_2 = model2.get_location(work_id_2); + auto& home = model1.get_location(home_id); + auto& event_1 = model1.get_location(event_id_1); + auto& event_2 = model2.get_location(event_id_2); EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_index = model1.get_person_index(p2_id); + auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_index = model1.get_person_index(p4_id); auto& p1 = model1.get_person(p1_id); auto& p2 = model1.get_person(p2_id); - auto p2_index = model1.get_person_index(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p3.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p4.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p3.set_assigned_location(event_1.get_type(), event_1.get_id(), event_1.get_model_id()); + p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); + + mio::abm::TripList& trips = model1.get_trip_list(); + mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, + model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, + model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + + trips.add_trip(trip1); + trips.add_trip(trip2); auto t = mio::abm::TimePoint(0); auto dt = mio::abm::hours(12); @@ -100,13 +123,34 @@ TEST(TestGraphAbm, test_apply_mobility) node2.evolve(t, dt); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); - EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 1); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 1); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); } + +TEST(TestGraphABM, test_graph_simulation) +{ + auto model1 = mio::ModelWrapper(size_t(1), 1); + auto model2 = mio::ModelWrapper(size_t(1), 2); + + mio::abm::TimePoint t0 = mio::abm::TimePoint(0); + mio::abm::TimePoint tmax = t0 + mio::abm::days(5); + + mio::Graph, mio::ABMMobilityEdge> graph; + graph.add_node(model1.get_id(), MockHistory{}, t0, std::move(model1)); + graph.add_node(model2.get_id(), MockHistory{}, t0, std::move(model2)); + graph.add_edge(0, 1); + graph.add_edge(1, 0); + + auto sim = mio::make_abm_graph_sim(t0, mio::abm::hours(12), std::move(graph)); + sim.advance(tmax); + + EXPECT_EQ(sim.get_t(), tmax); +} From af1e7f675a55acda3ac0ed7e6245e97efada3be7 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 8 Aug 2024 16:05:09 +0200 Subject: [PATCH 038/111] test ci --- cpp/tests/test_graph_abm.cpp | 50 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 62a6923089..d61f91183a 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -86,16 +86,16 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); - auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_index = model1.get_person_index(p2_id); - auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_index = model1.get_person_index(p4_id); - auto& p1 = model1.get_person(p1_id); - auto& p2 = model1.get_person(p2_id); - auto& p3 = model1.get_person(p3_id); - auto& p4 = model1.get_person(p4_id); + auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); + //auto p2_index = model1.get_person_index(p2_id); + auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); + //auto p4_index = model1.get_person_index(p4_id); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); @@ -114,25 +114,25 @@ TEST(TestGraphAbm, test_apply_mobility) trips.add_trip(trip1); trips.add_trip(trip2); - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::hours(12); - mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); - mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); + // auto t = mio::abm::TimePoint(0); + // auto dt = mio::abm::hours(12); + // mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); + // mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); - node1.evolve(t, dt); - node2.evolve(t, dt); + // node1.evolve(t, dt); + // node2.evolve(t, dt); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); - EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); + // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); + // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); + // EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); + // EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); - mio::ABMMobilityEdge edge; - edge.apply_mobility(node1, node2, t); + // mio::ABMMobilityEdge edge; + // edge.apply_mobility(node1, node2, t); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); - EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); + // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); + // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); + // EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); } TEST(TestGraphABM, test_graph_simulation) From 983ca21adad5a25ef4261b984d3fc39af91ab6b1 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 08:06:57 +0200 Subject: [PATCH 039/111] ci test --- cpp/models/graph_abm/model_wrapper.h | 2 +- cpp/tests/test_graph_abm.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index a94e66b4dd..688b94fdd2 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -125,7 +125,7 @@ class ModelWrapper : public abm::Model // check if a person makes a trip bool weekend = t.is_weekend(); - size_t num_trips = m_trip_list.num_trips(weekend); + size_t num_trips = Base::m_trip_list.num_trips(weekend); if (num_trips != 0) { while (Base::m_trip_list.get_current_index() < num_trips && diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index d61f91183a..ab8409b595 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -114,10 +114,10 @@ TEST(TestGraphAbm, test_apply_mobility) trips.add_trip(trip1); trips.add_trip(trip2); - // auto t = mio::abm::TimePoint(0); - // auto dt = mio::abm::hours(12); - // mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); - // mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); + auto t = mio::abm::TimePoint(0); + //auto dt = mio::abm::hours(12); + mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); + mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); // node1.evolve(t, dt); // node2.evolve(t, dt); From 59c445051329dc64ff34416fb6e70238e0e5d9bd Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 08:42:49 +0200 Subject: [PATCH 040/111] ci test --- cpp/models/graph_abm/model_wrapper.h | 2 +- cpp/tests/test_graph_abm.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 688b94fdd2..72e362c70d 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -136,7 +136,7 @@ class ModelWrapper : public abm::Model auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { if (trip.destination_model_id == Base::m_id) { - auto& target_location = get_location(trip.destination); + auto& target_location = Base::get_location(trip.destination); if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); Base::change_location(person_index, target_location.get_id(), trip.trip_mode); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index ab8409b595..195e8f3fcc 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -114,13 +114,13 @@ TEST(TestGraphAbm, test_apply_mobility) trips.add_trip(trip1); trips.add_trip(trip2); - auto t = mio::abm::TimePoint(0); - //auto dt = mio::abm::hours(12); + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::hours(12); mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); - // node1.evolve(t, dt); - // node2.evolve(t, dt); + node1.evolve(t, dt); + node2.evolve(t, dt); // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); From a8e84a4ccf5709c845bca65f3713d72fc080d71c Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 09:25:35 +0200 Subject: [PATCH 041/111] ci test --- cpp/tests/test_graph_abm.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 195e8f3fcc..664ed59a2f 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -86,16 +86,16 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); - auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); - //auto p2_index = model1.get_person_index(p2_id); - auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - //auto p4_index = model1.get_person_index(p4_id); - auto& p1 = model1.get_person(p1_id); - auto& p2 = model1.get_person(p2_id); - auto& p3 = model1.get_person(p3_id); - auto& p4 = model1.get_person(p4_id); + auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_index = model1.get_person_index(p2_id); + auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_index = model1.get_person_index(p4_id); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); @@ -122,10 +122,10 @@ TEST(TestGraphAbm, test_apply_mobility) node1.evolve(t, dt); node2.evolve(t, dt); - // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); - // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); - // EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - // EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); // mio::ABMMobilityEdge edge; // edge.apply_mobility(node1, node2, t); From 9e568d75ca412df53cee104b775ca7ce084da453 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 09:53:27 +0200 Subject: [PATCH 042/111] test ci --- cpp/tests/test_graph_abm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 664ed59a2f..fe2528c163 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -125,7 +125,7 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); + //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); // mio::ABMMobilityEdge edge; // edge.apply_mobility(node1, node2, t); From 53d19a185d7ee8885645c44e7aae9ee7972563d4 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 09:59:14 +0200 Subject: [PATCH 043/111] ci --- cpp/tests/test_graph_abm.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index fe2528c163..06a334b861 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -91,11 +91,11 @@ TEST(TestGraphAbm, test_apply_mobility) auto p2_index = model1.get_person_index(p2_id); auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_index = model1.get_person_index(p4_id); - auto& p1 = model1.get_person(p1_id); - auto& p2 = model1.get_person(p2_id); - auto& p3 = model1.get_person(p3_id); - auto& p4 = model1.get_person(p4_id); + //auto p4_index = model1.get_person_index(p4_id); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); From f8897e48b936952724f7a6343dad30ef19cd6637 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 10:56:54 +0200 Subject: [PATCH 044/111] test ci --- cpp/tests/test_graph_abm.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 06a334b861..e9a2efd143 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -86,11 +86,11 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); - auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_index = model1.get_person_index(p2_id); - auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); + //auto p2_index = model1.get_person_index(p2_id); + auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); //auto p4_index = model1.get_person_index(p4_id); auto& p1 = model1.get_person(p1_id); auto& p2 = model1.get_person(p2_id); @@ -124,7 +124,7 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); + //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); // mio::ABMMobilityEdge edge; From e4a75fdd2091da2e73ff6eb6d3b4a279250db0c4 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 11:31:50 +0200 Subject: [PATCH 045/111] ci test --- cpp/tests/test_graph_abm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index e9a2efd143..31433df558 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -122,8 +122,8 @@ TEST(TestGraphAbm, test_apply_mobility) node1.evolve(t, dt); node2.evolve(t, dt); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); - EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); + // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); + // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); From cadfecdeb51eaaca408d97411be6fc61f89d73a6 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 12:30:55 +0200 Subject: [PATCH 046/111] CI comment out node.evolve --- cpp/tests/test_graph_abm.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 31433df558..a628a40082 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -114,13 +114,13 @@ TEST(TestGraphAbm, test_apply_mobility) trips.add_trip(trip1); trips.add_trip(trip2); - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::hours(12); + auto t = mio::abm::TimePoint(0); + //auto dt = mio::abm::hours(12); mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); - node1.evolve(t, dt); - node2.evolve(t, dt); + // node1.evolve(t, dt); + // node2.evolve(t, dt); // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); From 99c1d4c7b16cdfd2079e3e3d7347bd7f1d5c01d0 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 13:35:39 +0200 Subject: [PATCH 047/111] CI comment in node.evolve --- cpp/tests/test_graph_abm.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index a628a40082..31433df558 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -114,13 +114,13 @@ TEST(TestGraphAbm, test_apply_mobility) trips.add_trip(trip1); trips.add_trip(trip2); - auto t = mio::abm::TimePoint(0); - //auto dt = mio::abm::hours(12); + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::hours(12); mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); - // node1.evolve(t, dt); - // node2.evolve(t, dt); + node1.evolve(t, dt); + node2.evolve(t, dt); // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); From 6b8d9b016fea94a1fd3c3e5fd0615bd083a6797b Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 14:39:30 +0200 Subject: [PATCH 048/111] log in test --- cpp/models/graph_abm/model_wrapper.h | 11 +++++++++-- cpp/tests/test_graph_abm.cpp | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 72e362c70d..a14f8722e5 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -25,6 +25,7 @@ #include "abm/time.h" #include "abm/location_id.h" #include "memilio/utils/compiler_diagnostics.h" +#include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" #include "abm/mobility_rules.h" #include "abm/mobility_rules.h" @@ -69,6 +70,7 @@ class ModelWrapper : public abm::Model private: void perform_mobility(TimePoint t, TimeSpan dt) { + log_warning("perform_mobility"); const uint32_t num_persons = static_cast(Base::m_persons.size()); PRAGMA_OMP(parallel for) for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { @@ -123,6 +125,8 @@ class ModelWrapper : public abm::Model } } + log_warning("begin trips"); + // check if a person makes a trip bool weekend = t.is_weekend(); size_t num_trips = Base::m_trip_list.num_trips(weekend); @@ -130,9 +134,12 @@ class ModelWrapper : public abm::Model if (num_trips != 0) { while (Base::m_trip_list.get_current_index() < num_trips && Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { - auto& trip = Base::m_trip_list.get_next_trip(weekend); + auto& trip = Base::m_trip_list.get_next_trip(weekend); + log_warning("get_index"); auto person_index = Base::get_person_index(trip.person_id); - auto& person = Base::get_person(person_index); + log_warning("get_index end"); + auto& person = Base::get_person(person_index); + log_warning("get_person end"); auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { if (trip.destination_model_id == Base::m_id) { diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 31433df558..0e366d5736 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -27,6 +27,7 @@ #include "abm/time.h" #include "graph_abm/graph_abm_mobility.h" #include "memilio/epidemiology/age_group.h" +#include "memilio/utils/logging.h" #include "memilio/utils/miompi.h" #include "memilio/mobility/graph.h" #include @@ -119,8 +120,11 @@ TEST(TestGraphAbm, test_apply_mobility) mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); + mio::log_warning("Evolve node 1"); node1.evolve(t, dt); + mio::log_warning("Evolve node 2"); node2.evolve(t, dt); + mio::log_warning("Evolve node 2 end"); // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); From 336998a497011caef67afd59403c088f94066d8d Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 15:35:15 +0200 Subject: [PATCH 049/111] test --- cpp/tests/test_graph_abm.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 0e366d5736..847c7219f0 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -126,17 +126,17 @@ TEST(TestGraphAbm, test_apply_mobility) node2.evolve(t, dt); mio::log_warning("Evolve node 2 end"); - // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); - // EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); - //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); - // mio::ABMMobilityEdge edge; - // edge.apply_mobility(node1, node2, t); + mio::ABMMobilityEdge edge; + edge.apply_mobility(node1, node2, t); - // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); - // EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); - // EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); } TEST(TestGraphABM, test_graph_simulation) From b5a6a0b7d6f07749c1edc4eb8991536aada18c2a Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 15:43:49 +0200 Subject: [PATCH 050/111] test --- cpp/tests/test_graph_abm.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 847c7219f0..911e0b59ac 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -87,16 +87,16 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); - auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); - //auto p2_index = model1.get_person_index(p2_id); - auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - //auto p4_index = model1.get_person_index(p4_id); - auto& p1 = model1.get_person(p1_id); - auto& p2 = model1.get_person(p2_id); - auto& p3 = model1.get_person(p3_id); - auto& p4 = model1.get_person(p4_id); + auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_index = model1.get_person_index(p2_id); + auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_index = model1.get_person_index(p4_id); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); From 7484ce99398d9e2675b079065e0aa49de133a720 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 16:03:17 +0200 Subject: [PATCH 051/111] more tests --- cpp/models/graph_abm/model_wrapper.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index a14f8722e5..ee691d0384 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -142,11 +142,16 @@ class ModelWrapper : public abm::Model log_warning("get_person end"); auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { + log_warning("if id is dest model id"); if (trip.destination_model_id == Base::m_id) { + log_warning("target loc"); auto& target_location = Base::get_location(trip.destination); + log_warning("target loc end"); if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); + log_warning("change loc"); Base::change_location(person_index, target_location.get_id(), trip.trip_mode); + log_warning("change loc end"); } } else { From 53c45e275d8467ed0ff96d6f4d7ad8aceee73370 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 9 Aug 2024 16:44:45 +0200 Subject: [PATCH 052/111] more logs --- cpp/models/graph_abm/model_wrapper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index ee691d0384..5d6ce0dd1d 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -155,6 +155,7 @@ class ModelWrapper : public abm::Model } } else { + log_warning("changes model"); //person moves to other world Base::m_activeness_statuses[person_index] = false; person.set_location(trip.destination_type, abm::LocationId::invalid_id(), @@ -162,6 +163,7 @@ class ModelWrapper : public abm::Model m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; + log_warning("changes model end"); } } Base::m_trip_list.increase_index(); From de0cae66586a665f48fa49141f982d69a4415d96 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 08:56:54 +0200 Subject: [PATCH 053/111] bug fix --- cpp/models/graph_abm/model_wrapper.h | 17 ++--------------- cpp/tests/test_graph_abm.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 5d6ce0dd1d..223a9f8b83 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -70,7 +70,6 @@ class ModelWrapper : public abm::Model private: void perform_mobility(TimePoint t, TimeSpan dt) { - log_warning("perform_mobility"); const uint32_t num_persons = static_cast(Base::m_persons.size()); PRAGMA_OMP(parallel for) for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { @@ -125,8 +124,6 @@ class ModelWrapper : public abm::Model } } - log_warning("begin trips"); - // check if a person makes a trip bool weekend = t.is_weekend(); size_t num_trips = Base::m_trip_list.num_trips(weekend); @@ -134,28 +131,19 @@ class ModelWrapper : public abm::Model if (num_trips != 0) { while (Base::m_trip_list.get_current_index() < num_trips && Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { - auto& trip = Base::m_trip_list.get_next_trip(weekend); - log_warning("get_index"); + auto& trip = Base::m_trip_list.get_next_trip(weekend); auto person_index = Base::get_person_index(trip.person_id); - log_warning("get_index end"); - auto& person = Base::get_person(person_index); - log_warning("get_person end"); + auto& person = Base::get_person(person_index); auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { - log_warning("if id is dest model id"); if (trip.destination_model_id == Base::m_id) { - log_warning("target loc"); auto& target_location = Base::get_location(trip.destination); - log_warning("target loc end"); if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); - log_warning("change loc"); Base::change_location(person_index, target_location.get_id(), trip.trip_mode); - log_warning("change loc end"); } } else { - log_warning("changes model"); //person moves to other world Base::m_activeness_statuses[person_index] = false; person.set_location(trip.destination_type, abm::LocationId::invalid_id(), @@ -163,7 +151,6 @@ class ModelWrapper : public abm::Model m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; - log_warning("changes model end"); } } Base::m_trip_list.increase_index(); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 911e0b59ac..27518ae77d 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -22,6 +22,7 @@ #include "abm/location_id.h" #include "abm/mobility_data.h" #include "abm/model.h" +#include "abm/parameters.h" #include "graph_abm/model_wrapper.h" #include "abm/location_type.h" #include "abm/time.h" @@ -69,10 +70,12 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { - auto model1 = mio::ModelWrapper(size_t(2), 1); - auto model2 = mio::ModelWrapper(size_t(2), 2); - model1.parameters.get()[mio::AgeGroup(0)] = true; - model2.parameters.get()[mio::AgeGroup(0)] = true; + auto model1 = mio::ModelWrapper(size_t(2), 1); + auto model2 = mio::ModelWrapper(size_t(2), 2); + model1.parameters.get()[mio::AgeGroup(0)] = true; + model2.parameters.get()[mio::AgeGroup(0)] = true; + model1.parameters.get()[mio::AgeGroup(0)] = 0.0; + model2.parameters.get()[mio::AgeGroup(0)] = 0.0; auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); auto home_id = model1.add_location(mio::abm::LocationType::Home); auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); @@ -120,11 +123,8 @@ TEST(TestGraphAbm, test_apply_mobility) mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); - mio::log_warning("Evolve node 1"); node1.evolve(t, dt); - mio::log_warning("Evolve node 2"); node2.evolve(t, dt); - mio::log_warning("Evolve node 2 end"); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); From 4daeac64a2cb3ee78cb0273ed36c261f00be0456 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 10:37:17 +0200 Subject: [PATCH 054/111] another bug fix --- cpp/models/graph_abm/model_wrapper.h | 1 + cpp/tests/test_graph_abm.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 223a9f8b83..49c41df145 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -21,6 +21,7 @@ #ifndef MIO_ABM_MODEL_WRAPPER_H #define MIO_ABM_MODEL_WRAPPER_H +#include "abm/location_type.h" #include "abm/model.h" #include "abm/time.h" #include "abm/location_id.h" diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 27518ae77d..430604fc62 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -76,6 +76,11 @@ TEST(TestGraphAbm, test_apply_mobility) model2.parameters.get()[mio::AgeGroup(0)] = true; model1.parameters.get()[mio::AgeGroup(0)] = 0.0; model2.parameters.get()[mio::AgeGroup(0)] = 0.0; + auto no_social_events = Eigen::VectorXd::Constant(2, 0.0); + model1.parameters.get().add_damping(no_social_events, + mio::SimulationTime(mio::abm::TimePoint(0).days())); + model2.parameters.get().add_damping(no_social_events, + mio::SimulationTime(mio::abm::TimePoint(0).days())); auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); auto home_id = model1.add_location(mio::abm::LocationType::Home); auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); From ef0bff33225a77470c883ec3a8e79360d49e7689 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 12:15:47 +0200 Subject: [PATCH 055/111] logs --- cpp/models/graph_abm/model_wrapper.h | 9 +++++++++ cpp/tests/test_graph_abm.cpp | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 49c41df145..4c13b6ee78 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -90,18 +90,22 @@ class ModelWrapper : public abm::Model target_location.get_capacity().persons) { bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { + mio::log_warning("Change loc rules"); Base::change_location(person_id, target_location.get_id()); + mio::log_warning("End change loc rules"); } return true; } } } else { //person moves to other world + mio::log_warning("Inactive person rules"); Base::m_activeness_statuses[person_id] = false; person.set_location(target_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); m_person_buffer.push_back(person_id); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; + mio::log_warning("End inactive person rules"); return true; } return false; @@ -132,6 +136,7 @@ class ModelWrapper : public abm::Model if (num_trips != 0) { while (Base::m_trip_list.get_current_index() < num_trips && Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { + mio::log_warning("Start trips"); auto& trip = Base::m_trip_list.get_next_trip(weekend); auto person_index = Base::get_person_index(trip.person_id); auto& person = Base::get_person(person_index); @@ -141,10 +146,13 @@ class ModelWrapper : public abm::Model auto& target_location = Base::get_location(trip.destination); if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); + mio::log_warning("Change loc trips"); Base::change_location(person_index, target_location.get_id(), trip.trip_mode); + mio::log_warning("End change loc trip"); } } else { + mio::log_warning("Inactive persons trip"); //person moves to other world Base::m_activeness_statuses[person_index] = false; person.set_location(trip.destination_type, abm::LocationId::invalid_id(), @@ -152,6 +160,7 @@ class ModelWrapper : public abm::Model m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; + mio::log_warning("End inactive persons trip"); } } Base::m_trip_list.increase_index(); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 430604fc62..39f1616bac 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -128,8 +128,11 @@ TEST(TestGraphAbm, test_apply_mobility) mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); + mio::log_warning("Evolve node 1"); node1.evolve(t, dt); + mio::log_warning("Evolve node 2"); node2.evolve(t, dt); + mio::log_warning("End evolve"); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); @@ -137,7 +140,9 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); mio::ABMMobilityEdge edge; + mio::log_warning("Apply mobility"); edge.apply_mobility(node1, node2, t); + mio::log_warning("End apply mobility"); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); From afdbc6ded07cb652fb136a3a015e0466ea077d5d Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 12:40:16 +0200 Subject: [PATCH 056/111] more logs --- cpp/models/abm/model.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index d457de4d1d..fbc5a229f9 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -429,15 +429,21 @@ class Model inline void change_location(uint32_t person_index, LocationId destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { + mio::log_warning("Get origin"); LocationId origin = get_location(person_index).get_id(); + mio::log_warning("has_changed_location"); const bool has_changed_location = mio::abm::change_location(get_person(person_index), get_location(destination), mode, cells); + mio::log_warning("has_changed_location end"); // if the person has changed location, invalidate exposure caches but keep population caches valid if (has_changed_location) { m_are_exposure_caches_valid = false; if (m_is_local_population_cache_valid) { + mio::log_warning("Adjust cache 1"); --m_local_population_cache[origin.get()]; + mio::log_warning("Adjust cache 2"); ++m_local_population_cache[destination.get()]; + mio::log_warning("Adjust cache 2 end"); } } } From 39d3b483b326a8f96af35dfdcfd69d2d9879c67a Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 12:53:35 +0200 Subject: [PATCH 057/111] test log --- cpp/models/abm/model.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index fbc5a229f9..cf2e7d59f8 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -477,6 +477,7 @@ class Model */ const Location& get_location(LocationId id) const { + log_error("here"); assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model."); return m_locations[id.get()]; @@ -484,6 +485,7 @@ class Model Location& get_location(LocationId id) { + log_error("here 1"); assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model."); return m_locations[id.get()]; From f0aafd40387fb87d539afbaf31148b0569d9e19f Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 13:27:37 +0200 Subject: [PATCH 058/111] rename world.get_location --- cpp/models/abm/model.cpp | 2 +- cpp/models/abm/model.h | 12 ++++++------ cpp/models/graph_abm/model_wrapper.h | 15 ++++++++------- cpp/tests/test_abm_model.cpp | 8 ++++---- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index b181a12cde..d08cb2964b 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -246,7 +246,7 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) if (person.get_location_model_id() == m_id) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, - get_location(uint32_t(i)), t, dt); + get_location_by_person(uint32_t(i)), t, dt); } } // implicit taskloop barrier } // implicit single barrier diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index cf2e7d59f8..535beffc0f 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -430,7 +430,7 @@ class Model TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { mio::log_warning("Get origin"); - LocationId origin = get_location(person_index).get_id(); + LocationId origin = get_location_by_person(person_index).get_id(); mio::log_warning("has_changed_location"); const bool has_changed_location = mio::abm::change_location(get_person(person_index), get_location(destination), mode, cells); @@ -463,9 +463,9 @@ class Model m_are_exposure_caches_valid = true; } auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person_index)); - mio::abm::interact(personal_rng, get_person(person_index), get_location(person_index), - m_air_exposure_rates_cache[get_location(person_index).get_id().get()], - m_contact_exposure_rates_cache[get_location(person_index).get_id().get()], t, dt, + mio::abm::interact(personal_rng, get_person(person_index), get_location_by_person(person_index), + m_air_exposure_rates_cache[get_location_by_person(person_index).get_id().get()], + m_contact_exposure_rates_cache[get_location_by_person(person_index).get_id().get()], t, dt, parameters); } @@ -498,12 +498,12 @@ class Model * @return Reference to the Location. * @{ */ - inline Location& get_location(uint32_t index) + inline Location& get_location_by_person(uint32_t index) { return get_location(get_person(index).get_location()); } - inline const Location& get_location(uint32_t index) const + inline const Location& get_location_by_person(uint32_t index) const { return get_location(get_person(index).get_location()); } diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 4c13b6ee78..3a259f2270 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -73,16 +73,17 @@ class ModelWrapper : public abm::Model { const uint32_t num_persons = static_cast(Base::m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { - if (Base::m_activeness_statuses[person_id]) { - Person& person = Base::m_persons[person_id]; + for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { + if (Base::m_activeness_statuses[person_index]) { + Person& person = Base::m_persons[person_index]; auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); auto try_mobility_rule = [&](auto rule) -> bool { //run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); if (person.get_assigned_location_model_id(target_type) == Base::m_id) { - const Location& target_location = Base::get_location(Base::find_location(target_type, person_id)); + const Location& target_location = + Base::get_location(Base::find_location(target_type, person_index)); const LocationId current_location = person.get_location(); if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { if (target_location.get_id() != current_location && @@ -91,7 +92,7 @@ class ModelWrapper : public abm::Model bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { mio::log_warning("Change loc rules"); - Base::change_location(person_id, target_location.get_id()); + Base::change_location(person_index, target_location.get_id()); mio::log_warning("End change loc rules"); } return true; @@ -100,9 +101,9 @@ class ModelWrapper : public abm::Model } else { //person moves to other world mio::log_warning("Inactive person rules"); - Base::m_activeness_statuses[person_id] = false; + Base::m_activeness_statuses[person_index] = false; person.set_location(target_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); - m_person_buffer.push_back(person_id); + m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; mio::log_warning("End inactive person rules"); diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index bc7f0f0513..51373029ae 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -413,18 +413,18 @@ TEST(TestModel, evolveMobility) // Check the dead person got burried and the severely infected person starts in Hospital model.evolve(t, dt); - EXPECT_EQ(model.get_location(model.get_person_index(p_dead.get_id())).get_type(), + EXPECT_EQ(model.get_location_by_person(model.get_person_index(p_dead.get_id())).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(model.get_location(model.get_person_index(p_severe.get_id())).get_type(), + EXPECT_EQ(model.get_location_by_person(model.get_person_index(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 model.evolve(t + dt, dt); - EXPECT_EQ(model.get_location(model.get_person_index(p_dead.get_id())).get_type(), + EXPECT_EQ(model.get_location_by_person(model.get_person_index(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(model.get_location(model.get_person_index(p_severe.get_id())).get_type(), + EXPECT_EQ(model.get_location_by_person(model.get_person_index(p_severe.get_id())).get_type(), mio::abm::LocationType::Cemetery); } } From de4fd4634219469b83926bfeac73b1f1e3a60ddb Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 14:00:59 +0200 Subject: [PATCH 059/111] more logs --- cpp/models/abm/model.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 535beffc0f..d3ef7b0c24 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -32,6 +32,7 @@ #include "abm/random_events.h" #include "abm/testing_strategy.h" #include "memilio/epidemiology/age_group.h" +#include "memilio/utils/logging.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" @@ -488,6 +489,8 @@ class Model log_error("here 1"); assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model."); + log_warning("id get is {}", id.get()); + log_warning("m_locations size is {}", m_locations.size()); return m_locations[id.get()]; } /** @} */ @@ -500,6 +503,7 @@ class Model */ inline Location& get_location_by_person(uint32_t index) { + log_warning("Location Id is {}", get_person(index).get_location()); return get_location(get_person(index).get_location()); } From c5bc31bf5a1dc1ec81049974c8e6aacfc16ee254 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 14:07:39 +0200 Subject: [PATCH 060/111] log fix --- cpp/models/abm/model.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index d3ef7b0c24..d578943727 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -503,7 +503,7 @@ class Model */ inline Location& get_location_by_person(uint32_t index) { - log_warning("Location Id is {}", get_person(index).get_location()); + log_warning("Location Id is {}", get_person(index).get_location().get()); return get_location(get_person(index).get_location()); } From 0632c13f0815bfd2d2c76f01646365f810f3c3d8 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 14:33:56 +0200 Subject: [PATCH 061/111] log --- cpp/models/abm/model.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index d578943727..7f0f919ae9 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -503,6 +503,9 @@ class Model */ inline Location& get_location_by_person(uint32_t index) { + log_warning("Person index is {}", index); + log_warning("Person location type is {}", int(get_person(index).get_location_type())); + log_warning("Person model id is {}", get_person(index).get_location_model_id()); log_warning("Location Id is {}", get_person(index).get_location().get()); return get_location(get_person(index).get_location()); } From db910b7c7bd214c7a50be5b0401c81064b5422da Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 15:36:06 +0200 Subject: [PATCH 062/111] Mock exponential dist in test --- cpp/tests/test_graph_abm.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 39f1616bac..79fef25465 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -31,6 +31,7 @@ #include "memilio/utils/logging.h" #include "memilio/utils/miompi.h" #include "memilio/mobility/graph.h" +#include "abm_helpers.h" #include #include #include @@ -123,6 +124,10 @@ TEST(TestGraphAbm, test_apply_mobility) trips.add_trip(trip1); trips.add_trip(trip2); + ScopedMockDistribution>>> + mock_exponential_dist; + EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillRepeatedly(testing::Return(1.)); //no random transitions + auto t = mio::abm::TimePoint(0); auto dt = mio::abm::hours(12); mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); From 31935e49762bb511face61f42f54017e448887ed Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 12 Aug 2024 16:31:51 +0200 Subject: [PATCH 063/111] don't use trip and mobility rules --- cpp/tests/test_graph_abm.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 79fef25465..3ec3002aa5 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -101,11 +101,11 @@ TEST(TestGraphAbm, test_apply_mobility) auto p2_index = model1.get_person_index(p2_id); auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_index = model1.get_person_index(p4_id); - auto& p1 = model1.get_person(p1_id); - auto& p2 = model1.get_person(p2_id); - auto& p3 = model1.get_person(p3_id); - auto& p4 = model1.get_person(p4_id); + //auto p4_index = model1.get_person_index(p4_id); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); @@ -115,14 +115,14 @@ TEST(TestGraphAbm, test_apply_mobility) p3.set_assigned_location(event_1.get_type(), event_1.get_id(), event_1.get_model_id()); p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); - mio::abm::TripList& trips = model1.get_trip_list(); - mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, - model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, - model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + // mio::abm::TripList& trips = model1.get_trip_list(); + // mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, + // model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + // mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, + // model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); - trips.add_trip(trip1); - trips.add_trip(trip2); + // trips.add_trip(trip1); + // trips.add_trip(trip2); ScopedMockDistribution>>> mock_exponential_dist; @@ -142,15 +142,15 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); + //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); mio::ABMMobilityEdge edge; mio::log_warning("Apply mobility"); edge.apply_mobility(node1, node2, t); mio::log_warning("End apply mobility"); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 3); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 1); EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); } From 72200ee65f2321200be2c61045317c729db33d07 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 13 Aug 2024 08:41:07 +0200 Subject: [PATCH 064/111] make mobility rules input --- cpp/models/abm/model.cpp | 2 +- cpp/models/abm/model.h | 47 ++++++++++++------------ cpp/models/graph_abm/model_wrapper.h | 33 +++++++++-------- cpp/tests/test_abm_model.cpp | 8 ++--- cpp/tests/test_graph_abm.cpp | 53 ++++++++++++---------------- 5 files changed, 68 insertions(+), 75 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index d08cb2964b..b181a12cde 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -246,7 +246,7 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) if (person.get_location_model_id() == m_id) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, - get_location_by_person(uint32_t(i)), t, dt); + get_location(uint32_t(i)), t, dt); } } // implicit taskloop barrier } // implicit single barrier diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 7f0f919ae9..3a71e97136 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -58,6 +58,8 @@ class Model using ConstPersonIterator = std::vector::const_iterator; using ActivenessIterator = std::vector::iterator; using ConstActivenessIterator = std::vector::const_iterator; + using MobilityRuleType = LocationType (*)(PersonalRandomNumberGenerator&, const Person&, TimePoint, TimeSpan, + const Parameters&); /** * @brief Create a Model. @@ -430,21 +432,21 @@ class Model inline void change_location(uint32_t person_index, LocationId destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { - mio::log_warning("Get origin"); - LocationId origin = get_location_by_person(person_index).get_id(); - mio::log_warning("has_changed_location"); + //mio::log_warning("Get origin"); + LocationId origin = get_location(person_index).get_id(); + //mio::log_warning("has_changed_location"); const bool has_changed_location = mio::abm::change_location(get_person(person_index), get_location(destination), mode, cells); - mio::log_warning("has_changed_location end"); + //mio::log_warning("has_changed_location end"); // if the person has changed location, invalidate exposure caches but keep population caches valid if (has_changed_location) { m_are_exposure_caches_valid = false; if (m_is_local_population_cache_valid) { - mio::log_warning("Adjust cache 1"); + //mio::log_warning("Adjust cache 1"); --m_local_population_cache[origin.get()]; - mio::log_warning("Adjust cache 2"); + //mio::log_warning("Adjust cache 2"); ++m_local_population_cache[destination.get()]; - mio::log_warning("Adjust cache 2 end"); + //mio::log_warning("Adjust cache 2 end"); } } } @@ -464,9 +466,9 @@ class Model m_are_exposure_caches_valid = true; } auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person_index)); - mio::abm::interact(personal_rng, get_person(person_index), get_location_by_person(person_index), - m_air_exposure_rates_cache[get_location_by_person(person_index).get_id().get()], - m_contact_exposure_rates_cache[get_location_by_person(person_index).get_id().get()], t, dt, + mio::abm::interact(personal_rng, get_person(person_index), get_location(person_index), + m_air_exposure_rates_cache[get_location(person_index).get_id().get()], + m_contact_exposure_rates_cache[get_location(person_index).get_id().get()], t, dt, parameters); } @@ -478,7 +480,7 @@ class Model */ const Location& get_location(LocationId id) const { - log_error("here"); + //log_error("here"); assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model."); return m_locations[id.get()]; @@ -486,11 +488,11 @@ class Model Location& get_location(LocationId id) { - log_error("here 1"); + //log_error("here 1"); assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model."); - log_warning("id get is {}", id.get()); - log_warning("m_locations size is {}", m_locations.size()); + // log_warning("id get is {}", id.get()); + // log_warning("m_locations size is {}", m_locations.size()); return m_locations[id.get()]; } /** @} */ @@ -501,16 +503,16 @@ class Model * @return Reference to the Location. * @{ */ - inline Location& get_location_by_person(uint32_t index) + inline Location& get_location(uint32_t index) { - log_warning("Person index is {}", index); - log_warning("Person location type is {}", int(get_person(index).get_location_type())); - log_warning("Person model id is {}", get_person(index).get_location_model_id()); - log_warning("Location Id is {}", get_person(index).get_location().get()); + // log_warning("Person index is {}", index); + // log_warning("Person location type is {}", int(get_person(index).get_location_type())); + // log_warning("Person model id is {}", get_person(index).get_location_model_id()); + // log_warning("Location Id is {}", get_person(index).get_location().get()); return get_location(get_person(index).get_location()); } - inline const Location& get_location_by_person(uint32_t index) const + inline const Location& get_location(uint32_t index) const { return get_location(get_person(index).get_location()); } @@ -583,10 +585,7 @@ class Model 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_mobility_rules; ///< Whether mobility rules are considered. - std::vector>> - m_mobility_rules; ///< Rules that govern the mobility between Location%s. + std::vector m_mobility_rules; ///< Rules that govern the mobility between Location%s. LocationId m_cemetery_id; // Central cemetery for all dead persons. RandomNumberGenerator m_rng; ///< Global random number generator }; diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 3a259f2270..1727e83e74 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -30,17 +30,28 @@ #include "memilio/utils/mioomp.h" #include "abm/mobility_rules.h" #include "abm/mobility_rules.h" +#include #include +#include namespace mio { using namespace abm; class ModelWrapper : public abm::Model { - using Model::Model; using Base = Model; public: + ModelWrapper(size_t num_agegroups, int id, + std::vector mobility_rules = + std::vector{&get_buried, &return_home_when_recovered, &go_to_hospital, + &go_to_icu, &go_to_school, &go_to_work, &go_to_shop, + &go_to_event, &go_to_quarantine}) + : Base(num_agegroups, id) + { + Base::m_mobility_rules = mobility_rules; + } + /** * @brief Get person buffer. */ @@ -112,20 +123,12 @@ class ModelWrapper : public abm::Model return false; }; - //run mobility rules one after the other if the corresponding location type exists - //shortcutting of bool operators ensures the rules stop after the first rule is applied - if (Base::m_use_mobility_rules) { - try_mobility_rule(&get_buried) || try_mobility_rule(&return_home_when_recovered) || - try_mobility_rule(&go_to_hospital) || try_mobility_rule(&go_to_icu) || - try_mobility_rule(&go_to_school) || try_mobility_rule(&go_to_work) || - try_mobility_rule(&go_to_shop) || try_mobility_rule(&go_to_event) || - try_mobility_rule(&go_to_quarantine); - } - else { - //no daily routine mobility, just infection related - try_mobility_rule(&get_buried) || try_mobility_rule(&return_home_when_recovered) || - try_mobility_rule(&go_to_hospital) || try_mobility_rule(&go_to_icu) || - try_mobility_rule(&go_to_quarantine); + for (auto rule : Base::m_mobility_rules) { + bool applied = try_mobility_rule(rule); + //only use one mobility rule per person + if (applied) { + break; + } } } } diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 51373029ae..bc7f0f0513 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -413,18 +413,18 @@ TEST(TestModel, evolveMobility) // Check the dead person got burried and the severely infected person starts in Hospital model.evolve(t, dt); - EXPECT_EQ(model.get_location_by_person(model.get_person_index(p_dead.get_id())).get_type(), + EXPECT_EQ(model.get_location(model.get_person_index(p_dead.get_id())).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(model.get_location_by_person(model.get_person_index(p_severe.get_id())).get_type(), + EXPECT_EQ(model.get_location(model.get_person_index(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 model.evolve(t + dt, dt); - EXPECT_EQ(model.get_location_by_person(model.get_person_index(p_dead.get_id())).get_type(), + EXPECT_EQ(model.get_location(model.get_person_index(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(model.get_location_by_person(model.get_person_index(p_severe.get_id())).get_type(), + EXPECT_EQ(model.get_location(model.get_person_index(p_severe.get_id())).get_type(), mio::abm::LocationType::Cemetery); } } diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 3ec3002aa5..d6ef2f59d3 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -53,7 +53,7 @@ TEST(TestGraphAbm, test_evolve_node) model.parameters.get()[mio::AgeGroup(0)] = true; auto home_id = model.add_location(mio::abm::LocationType::Home); auto& home = model.get_location(home_id); - auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0), size_t(1), 1); + auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0), size_t(1), 2); auto pid = model.add_person(home_id, mio::AgeGroup(0)); auto index = model.get_person_index(pid); auto& p = model.get_person(pid); @@ -71,17 +71,12 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { - auto model1 = mio::ModelWrapper(size_t(2), 1); - auto model2 = mio::ModelWrapper(size_t(2), 2); - model1.parameters.get()[mio::AgeGroup(0)] = true; - model2.parameters.get()[mio::AgeGroup(0)] = true; - model1.parameters.get()[mio::AgeGroup(0)] = 0.0; - model2.parameters.get()[mio::AgeGroup(0)] = 0.0; - auto no_social_events = Eigen::VectorXd::Constant(2, 0.0); - model1.parameters.get().add_damping(no_social_events, - mio::SimulationTime(mio::abm::TimePoint(0).days())); - model2.parameters.get().add_damping(no_social_events, - mio::SimulationTime(mio::abm::TimePoint(0).days())); + auto model1 = + mio::ModelWrapper(size_t(2), 1, std::vector{&mio::abm::go_to_work}); + auto model2 = + mio::ModelWrapper(size_t(2), 2, std::vector{&mio::abm::go_to_work}); + model1.parameters.get()[mio::AgeGroup(0)] = true; + model2.parameters.get()[mio::AgeGroup(0)] = true; auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); auto home_id = model1.add_location(mio::abm::LocationType::Home); auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); @@ -101,11 +96,11 @@ TEST(TestGraphAbm, test_apply_mobility) auto p2_index = model1.get_person_index(p2_id); auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - //auto p4_index = model1.get_person_index(p4_id); - auto& p1 = model1.get_person(p1_id); - auto& p2 = model1.get_person(p2_id); - auto& p3 = model1.get_person(p3_id); - auto& p4 = model1.get_person(p4_id); + auto p4_index = model1.get_person_index(p4_id); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); @@ -115,18 +110,14 @@ TEST(TestGraphAbm, test_apply_mobility) p3.set_assigned_location(event_1.get_type(), event_1.get_id(), event_1.get_model_id()); p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); - // mio::abm::TripList& trips = model1.get_trip_list(); - // mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, - // model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); - // mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, - // model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + mio::abm::TripList& trips = model1.get_trip_list(); + mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, + model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, + model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); - // trips.add_trip(trip1); - // trips.add_trip(trip2); - - ScopedMockDistribution>>> - mock_exponential_dist; - EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillRepeatedly(testing::Return(1.)); //no random transitions + trips.add_trip(trip1); + trips.add_trip(trip2); auto t = mio::abm::TimePoint(0); auto dt = mio::abm::hours(12); @@ -142,15 +133,15 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - //EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); mio::ABMMobilityEdge edge; mio::log_warning("Apply mobility"); edge.apply_mobility(node1, node2, t); mio::log_warning("End apply mobility"); - EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 3); - EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 1); + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); } From c285c385b73883baf78f2196470f8b6f53f95d0d Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 13 Aug 2024 09:20:28 +0200 Subject: [PATCH 065/111] remove logs --- cpp/models/abm/model.h | 14 -------------- cpp/models/graph_abm/model_wrapper.h | 9 --------- cpp/tests/test_graph_abm.cpp | 5 ----- 3 files changed, 28 deletions(-) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 3a71e97136..9bd2bcde44 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -432,21 +432,15 @@ class Model inline void change_location(uint32_t person_index, LocationId destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { - //mio::log_warning("Get origin"); LocationId origin = get_location(person_index).get_id(); - //mio::log_warning("has_changed_location"); const bool has_changed_location = mio::abm::change_location(get_person(person_index), get_location(destination), mode, cells); - //mio::log_warning("has_changed_location end"); // if the person has changed location, invalidate exposure caches but keep population caches valid if (has_changed_location) { m_are_exposure_caches_valid = false; if (m_is_local_population_cache_valid) { - //mio::log_warning("Adjust cache 1"); --m_local_population_cache[origin.get()]; - //mio::log_warning("Adjust cache 2"); ++m_local_population_cache[destination.get()]; - //mio::log_warning("Adjust cache 2 end"); } } } @@ -480,7 +474,6 @@ class Model */ const Location& get_location(LocationId id) const { - //log_error("here"); assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model."); return m_locations[id.get()]; @@ -488,11 +481,8 @@ class Model Location& get_location(LocationId id) { - //log_error("here 1"); assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model."); - // log_warning("id get is {}", id.get()); - // log_warning("m_locations size is {}", m_locations.size()); return m_locations[id.get()]; } /** @} */ @@ -505,10 +495,6 @@ class Model */ inline Location& get_location(uint32_t index) { - // log_warning("Person index is {}", index); - // log_warning("Person location type is {}", int(get_person(index).get_location_type())); - // log_warning("Person model id is {}", get_person(index).get_location_model_id()); - // log_warning("Location Id is {}", get_person(index).get_location().get()); return get_location(get_person(index).get_location()); } diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 1727e83e74..413b186689 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -102,22 +102,18 @@ class ModelWrapper : public abm::Model target_location.get_capacity().persons) { bool wears_mask = person.apply_mask_intervention(personal_rng, target_location); if (wears_mask) { - mio::log_warning("Change loc rules"); Base::change_location(person_index, target_location.get_id()); - mio::log_warning("End change loc rules"); } return true; } } } else { //person moves to other world - mio::log_warning("Inactive person rules"); Base::m_activeness_statuses[person_index] = false; person.set_location(target_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; - mio::log_warning("End inactive person rules"); return true; } return false; @@ -140,7 +136,6 @@ class ModelWrapper : public abm::Model if (num_trips != 0) { while (Base::m_trip_list.get_current_index() < num_trips && Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { - mio::log_warning("Start trips"); auto& trip = Base::m_trip_list.get_next_trip(weekend); auto person_index = Base::get_person_index(trip.person_id); auto& person = Base::get_person(person_index); @@ -150,13 +145,10 @@ class ModelWrapper : public abm::Model auto& target_location = Base::get_location(trip.destination); if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { person.apply_mask_intervention(personal_rng, target_location); - mio::log_warning("Change loc trips"); Base::change_location(person_index, target_location.get_id(), trip.trip_mode); - mio::log_warning("End change loc trip"); } } else { - mio::log_warning("Inactive persons trip"); //person moves to other world Base::m_activeness_statuses[person_index] = false; person.set_location(trip.destination_type, abm::LocationId::invalid_id(), @@ -164,7 +156,6 @@ class ModelWrapper : public abm::Model m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; - mio::log_warning("End inactive persons trip"); } } Base::m_trip_list.increase_index(); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index d6ef2f59d3..af04831d39 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -124,11 +124,8 @@ TEST(TestGraphAbm, test_apply_mobility) mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); - mio::log_warning("Evolve node 1"); node1.evolve(t, dt); - mio::log_warning("Evolve node 2"); node2.evolve(t, dt); - mio::log_warning("End evolve"); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); @@ -136,9 +133,7 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); mio::ABMMobilityEdge edge; - mio::log_warning("Apply mobility"); edge.apply_mobility(node1, node2, t); - mio::log_warning("End apply mobility"); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); From b6593114852a69afddd8210895cd5ad80bebfe32 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 21 Nov 2024 12:53:46 +0100 Subject: [PATCH 066/111] rework add_person such that person id can be kept --- cpp/models/abm/model.cpp | 15 ++++++++++----- cpp/models/graph_abm/model_wrapper.h | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index b181a12cde..045b88b5cf 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -59,17 +59,22 @@ PersonId Model::add_person(Person&& person) assert(person.get_location() < LocationId((uint32_t)m_locations.size()) && "Added Person's location is not in Model."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); - uint64_t id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - PersonId new_id{id}; - m_persons.emplace_back(person, new_id); + if (person.get_id() == PersonId::invalid_id()) { + uint64_t id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); + PersonId new_id{id}; + person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); + m_persons.emplace_back(person, new_id); + } + else { + m_persons.emplace_back(person); + } m_activeness_statuses.push_back(true); auto& new_person = m_persons.back(); - new_person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); if (m_is_local_population_cache_valid) { ++m_local_population_cache[new_person.get_location().get()]; } - return new_id; + return new_person.get_id(); } void Model::evolve(TimePoint t, TimeSpan dt) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 413b186689..cfc7c38492 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -166,7 +166,7 @@ class ModelWrapper : public abm::Model } } - std::vector m_person_buffer; ///< List with indices of persons that are deactivated per target world. + std::vector m_person_buffer; ///< List with indices of persons that are deactivated. }; } // namespace mio From bece66be0a78423937041caf743a163b4ef9465a Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 21 Nov 2024 15:58:13 +0100 Subject: [PATCH 067/111] bug fix after merge --- cpp/models/abm/location.cpp | 2 +- cpp/models/abm/model.cpp | 378 +++++++++++++-------------- cpp/models/abm/person.h | 2 +- cpp/models/abm/trip_list.h | 8 +- cpp/models/graph_abm/model_wrapper.h | 97 ++++--- cpp/tests/test_abm_model.cpp | 54 ++-- cpp/tests/test_json_serializer.cpp | 96 ------- 7 files changed, 289 insertions(+), 348 deletions(-) diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index d9eb4056aa..55c6da5d60 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -32,7 +32,7 @@ Location::Location(LocationType loc_type, LocationId loc_id, size_t num_agegroup , m_id(loc_id) , m_parameters(num_agegroups) , m_cells(num_cells) - , m_required_mask(MaskType::Community) + , m_required_mask(MaskType::None) , m_model_id(model_id) { assert(num_cells > 0 && "Number of cells has to be larger than 0."); diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index d68e85813e..6937a69fbb 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -143,6 +143,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) change_location(person_index, target_location.get_id()); return true; } + return false; }; // run mobility rules one after the other if the corresponding location type exists @@ -168,224 +169,223 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) (has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine)); } } + } - // check if a person makes a trip - bool weekend = t.is_weekend(); - size_t num_trips = m_trip_list.num_trips(weekend); - - for (; m_trip_list.get_current_index() < num_trips && - m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); - m_trip_list.increase_index()) { - auto& trip = m_trip_list.get_next_trip(weekend); - auto& person = get_person(static_cast(trip.person_id.get())); - auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); - // skip the trip if the person is in quarantine or is dead - if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { - continue; - } - auto& target_location = get_location(trip.destination); - // skip the trip if the Person wears mask as required at targeted location - if (target_location.is_mask_required() && !person.is_compliant(personal_rng, InterventionType::Mask)) { - continue; - } - // skip the trip if the performed TestingStrategy is positive - if (!m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { - continue; - } - // all requirements are met, move to target location - change_location(static_cast(trip.person_id.get()), target_location.get_id(), trip.trip_mode); - // update worn mask to target location's requirements - if (target_location.is_mask_required()) { - // if the current MaskProtection level is lower than required, the Person changes mask - if (parameters.get()[person.get_mask().get_type()] < - parameters.get()[target_location.get_required_mask()]) { - person.set_mask(target_location.get_required_mask(), t); - } - } - else { - person.set_mask(MaskType::None, t); + // check if a person makes a trip + bool weekend = t.is_weekend(); + size_t num_trips = m_trip_list.num_trips(weekend); + + for (; m_trip_list.get_current_index() < num_trips && + m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); + m_trip_list.increase_index()) { + auto& trip = m_trip_list.get_next_trip(weekend); + auto& person = get_person(static_cast(trip.person_id.get())); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + // skip the trip if the person is in quarantine or is dead + if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { + continue; + } + auto& target_location = get_location(trip.destination); + // skip the trip if the Person wears mask as required at targeted location + if (target_location.is_mask_required() && !person.is_compliant(personal_rng, InterventionType::Mask)) { + continue; + } + // skip the trip if the performed TestingStrategy is positive + if (!m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + continue; + } + // all requirements are met, move to target location + change_location(static_cast(trip.person_id.get()), target_location.get_id(), trip.trip_mode); + // update worn mask to target location's requirements + if (target_location.is_mask_required()) { + // if the current MaskProtection level is lower than required, the Person changes mask + if (parameters.get()[person.get_mask().get_type()] < + parameters.get()[target_location.get_required_mask()]) { + person.set_mask(target_location.get_required_mask(), t); } } - if (((t).days() < std::floor((t + dt).days()))) { - m_trip_list.reset_index(); + else { + person.set_mask(MaskType::None, t); } } + if (((t).days() < std::floor((t + dt).days()))) { + m_trip_list.reset_index(); + } +} - void Model::build_compute_local_population_cache() const +void Model::build_compute_local_population_cache() const +{ + PRAGMA_OMP(single) { - PRAGMA_OMP(single) - { - 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; - } // implicit taskloop barrier - PRAGMA_OMP(taskloop) - for (size_t i = 0; i < num_persons; i++) { - if (m_persons[i].get_location_model_id() == m_id) { - ++m_local_population_cache[m_persons[i].get_location().get()]; - } - } // implicit taskloop barrier - } // implicit single barrier - } + 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; + } // implicit taskloop barrier + PRAGMA_OMP(taskloop) + for (size_t i = 0; i < num_persons; i++) { + if (m_persons[i].get_location_model_id() == m_id) { + ++m_local_population_cache[m_persons[i].get_location().get()]; + } + } // implicit taskloop barrier + } // implicit single barrier +} - void Model::build_exposure_caches() +void Model::build_exposure_caches() +{ + PRAGMA_OMP(single) { - PRAGMA_OMP(single) - { - const size_t num_locations = m_locations.size(); - 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[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_are_exposure_caches_valid = false; - m_exposure_caches_need_rebuild = false; - } // implicit single barrier - } + const size_t num_locations = m_locations.size(); + 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[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_are_exposure_caches_valid = false; + m_exposure_caches_need_rebuild = false; + } // implicit single barrier +} - void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) +void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) +{ + PRAGMA_OMP(single) { - PRAGMA_OMP(single) - { - // if cache shape was changed (e.g. by add_location), rebuild it - if (m_exposure_caches_need_rebuild) { - build_exposure_caches(); + // if cache shape was changed (e.g. by add_location), rebuild it + if (m_exposure_caches_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[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[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().get(); + if (person.get_location_model_id() == m_id) { + mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], + m_contact_exposure_rates_cache[location], person, + get_location(uint32_t(i)), t, dt); } - // 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[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[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().get(); - if (person.get_location_model_id() == m_id) { - mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], - m_contact_exposure_rates_cache[location], person, - get_location(uint32_t(i)), t, dt); - } - } // implicit taskloop barrier - } // implicit single barrier - } + } // implicit taskloop barrier + } // implicit single barrier +} - void Model::begin_step(TimePoint t, TimeSpan dt) - { - m_testing_strategy.update_activity_status(t); +void Model::begin_step(TimePoint t, TimeSpan dt) +{ + m_testing_strategy.update_activity_status(t); - if (!m_is_local_population_cache_valid) { - build_compute_local_population_cache(); - m_is_local_population_cache_valid = true; - } - compute_exposure_caches(t, dt); - m_are_exposure_caches_valid = true; + if (!m_is_local_population_cache_valid) { + build_compute_local_population_cache(); + m_is_local_population_cache_valid = true; } + compute_exposure_caches(t, dt); + m_are_exposure_caches_valid = true; +} - auto Model::get_locations() const->Range> - { - return std::make_pair(m_locations.cbegin(), m_locations.cend()); - } - auto Model::get_locations()->Range> - { - return std::make_pair(m_locations.begin(), m_locations.end()); - } +auto Model::get_locations() const -> Range> +{ + return std::make_pair(m_locations.cbegin(), m_locations.cend()); +} +auto Model::get_locations() -> Range> +{ + return std::make_pair(m_locations.begin(), m_locations.end()); +} - auto Model::get_persons() const->Range> - { - return std::make_pair(m_persons.cbegin(), m_persons.cend()); - } +auto Model::get_persons() const -> Range> +{ + return std::make_pair(m_persons.cbegin(), m_persons.cend()); +} - auto Model::get_persons()->Range> - { - return std::make_pair(m_persons.begin(), m_persons.end()); - } +auto Model::get_persons() -> Range> +{ + return std::make_pair(m_persons.begin(), m_persons.end()); +} - auto Model::get_activeness_statuses() const->Range> - { - return std::make_pair(m_activeness_statuses.cbegin(), m_activeness_statuses.cend()); - } +auto Model::get_activeness_statuses() const -> Range> +{ + return std::make_pair(m_activeness_statuses.cbegin(), m_activeness_statuses.cend()); +} - auto Model::get_activeness_statuses()->Range> - { - return std::make_pair(m_activeness_statuses.begin(), m_activeness_statuses.end()); - } +auto Model::get_activeness_statuses() -> Range> +{ + return std::make_pair(m_activeness_statuses.begin(), m_activeness_statuses.end()); +} - LocationId Model::find_location(LocationType type, const uint32_t person_index) const - { - auto location_id = get_person(person_index).get_assigned_location(type); - assert(location_id != LocationId::invalid_id() && "The person has no assigned location of that type."); - return location_id; - } +LocationId Model::find_location(LocationType type, const uint32_t person_index) const +{ + auto location_id = get_person(person_index).get_assigned_location(type); + assert(location_id != LocationId::invalid_id() && "The person has no assigned location of that type."); + return location_id; +} - size_t Model::get_subpopulation_combined(TimePoint t, InfectionState s) const - { - return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, - [t, s, this](size_t running_sum, const Location& loc) { - return running_sum + get_subpopulation(loc.get_id(), t, s); - }); - } +size_t Model::get_subpopulation_combined(TimePoint t, InfectionState s) const +{ + return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, + [t, s, this](size_t running_sum, const Location& loc) { + return running_sum + get_subpopulation(loc.get_id(), t, s); + }); +} - size_t Model::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 Location& loc) { - return loc.get_type() == type ? running_sum + get_subpopulation(loc.get_id(), t, s) - : running_sum; - }); - } +size_t Model::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 Location& loc) { + return loc.get_type() == type ? running_sum + get_subpopulation(loc.get_id(), t, s) : running_sum; + }); +} - TripList& Model::get_trip_list() - { - return m_trip_list; - } +TripList& Model::get_trip_list() +{ + return m_trip_list; +} - const TripList& Model::get_trip_list() const - { - return m_trip_list; - } +const TripList& Model::get_trip_list() const +{ + return m_trip_list; +} - void Model::use_mobility_rules(bool param) - { - m_use_mobility_rules = param; - } +void Model::use_mobility_rules(bool param) +{ + m_use_mobility_rules = param; +} - bool Model::use_mobility_rules() const - { - return m_use_mobility_rules; - } +bool Model::use_mobility_rules() const +{ + return m_use_mobility_rules; +} - TestingStrategy& Model::get_testing_strategy() - { - return m_testing_strategy; - } +TestingStrategy& Model::get_testing_strategy() +{ + return m_testing_strategy; +} - const TestingStrategy& Model::get_testing_strategy() const - { - return m_testing_strategy; - } +const TestingStrategy& Model::get_testing_strategy() const +{ + return m_testing_strategy; +} } // namespace abm } // namespace mio diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 93a858ebfe..80d2d22b86 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -485,7 +485,7 @@ template <> struct DefaultFactory { static abm::Person create() { - return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::LocationId(), AgeGroup(0), + return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::LocationId(), 0, AgeGroup(0), abm::PersonId()); } }; diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index cbbf019b94..ffde7bbd80 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -90,6 +90,12 @@ struct Trip { { } + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationType type_of_activity, + const std::vector& input_cells = {}) + : Trip(id, time_new, dest, dest, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) + { + } + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, const std::vector& input_cells = {}) : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) @@ -205,7 +211,7 @@ template <> struct DefaultFactory { static abm::Trip create() { - return abm::Trip{abm::PersonId{}, abm::TimePoint{}, abm::LocationId{}}; + return abm::Trip{abm::PersonId{}, abm::TimePoint{}, abm::LocationId{}, abm::LocationType{}}; } }; diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index cfc7c38492..7e78a94d80 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -96,17 +96,33 @@ class ModelWrapper : public abm::Model const Location& target_location = Base::get_location(Base::find_location(target_type, person_index)); const LocationId current_location = person.get_location(); - if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { - if (target_location.get_id() != current_location && - Base::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) { - Base::change_location(person_index, target_location.get_id()); - } - return true; + // the Person cannot move if they do not wear mask as required at targeted location + if (target_location.is_mask_required() && + !person.is_compliant(personal_rng, InterventionType::Mask)) { + return false; + } + // the Person cannot move if the capacity of targeted Location is reached + if (target_location.get_id() == current_location || + get_number_persons(target_location.get_id()) >= target_location.get_capacity().persons) { + return false; + } + // the Person cannot move if the performed TestingStrategy is positive + if (!m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + return false; + } + // update worn mask to target location's requirements + if (target_location.is_mask_required()) { + // if the current MaskProtection level is lower than required, the Person changes mask + if (parameters.get()[person.get_mask().get_type()] < + parameters.get()[target_location.get_required_mask()]) { + person.set_mask(target_location.get_required_mask(), t); } } + else { + person.set_mask(MaskType::None, t); + } + Base::change_location(person_index, target_location.get_id()); + return true; } else { //person moves to other world Base::m_activeness_statuses[person_index] = false; @@ -133,32 +149,47 @@ class ModelWrapper : public abm::Model bool weekend = t.is_weekend(); size_t num_trips = Base::m_trip_list.num_trips(weekend); - if (num_trips != 0) { - while (Base::m_trip_list.get_current_index() < num_trips && - Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds()) { - auto& trip = Base::m_trip_list.get_next_trip(weekend); - auto person_index = Base::get_person_index(trip.person_id); - auto& person = Base::get_person(person_index); - auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); - if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) { - if (trip.destination_model_id == Base::m_id) { - auto& target_location = Base::get_location(trip.destination); - if (Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { - person.apply_mask_intervention(personal_rng, target_location); - Base::change_location(person_index, target_location.get_id(), trip.trip_mode); - } - } - else { - //person moves to other world - Base::m_activeness_statuses[person_index] = false; - person.set_location(trip.destination_type, abm::LocationId::invalid_id(), - std::numeric_limits::max()); - m_person_buffer.push_back(person_index); - m_are_exposure_caches_valid = false; - m_is_local_population_cache_valid = false; + for (; Base::m_trip_list.get_current_index() < num_trips && + Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); + Base::m_trip_list.increase_index()) { + auto& trip = Base::m_trip_list.get_next_trip(weekend); + auto& person = get_person(static_cast(trip.person_id.get())); + auto person_index = Base::get_person_index(trip.person_id); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + // skip the trip if the person is in quarantine or is dead + if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { + continue; + } + if (trip.destination_model_id == Base::m_id) { + auto& target_location = get_location(trip.destination); + // skip the trip if the Person wears mask as required at targeted location + if (target_location.is_mask_required() && !person.is_compliant(personal_rng, InterventionType::Mask)) { + continue; + } + // skip the trip if the performed TestingStrategy is positive + if (!Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + continue; + } + // all requirements are met, move to target location + change_location(person_index, target_location.get_id(), trip.trip_mode); + // update worn mask to target location's requirements + if (target_location.is_mask_required()) { + // if the current MaskProtection level is lower than required, the Person changes mask + if (parameters.get()[person.get_mask().get_type()] < + parameters.get()[target_location.get_required_mask()]) { + person.set_mask(target_location.get_required_mask(), t); } } - Base::m_trip_list.increase_index(); + else { + person.set_mask(MaskType::None, t); + } + } + else { //person moves to other world + Base::m_activeness_statuses[person_index] = false; + person.set_location(trip.destination_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); + m_person_buffer.push_back(person_index); + m_are_exposure_caches_valid = false; + m_is_local_population_cache_valid = false; } } if (((t).days() < std::floor((t + dt).days()))) { diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 07ca319278..bd832cde8e 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -634,17 +634,17 @@ TEST(TestModel, mobilityRulesWithAppliedNPIs) auto& p_no_test = model.get_person(p_id_no_test); auto& p_no_isolation = model.get_person(p_id_no_isolation); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::School, school_id); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_no_mask.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_no_mask.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_no_test.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_no_test.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Home, home_id); + p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); auto testing_criteria = mio::abm::TestingCriteria( {}, {mio::abm::InfectionState::InfectedSymptoms, mio::abm::InfectionState::InfectedNoSymptoms}); @@ -746,17 +746,17 @@ TEST(TestModel, mobilityTripWithAppliedNPIs) auto& p_no_test = model.get_person(p_id_no_test); auto& p_no_isolation = model.get_person(p_id_no_isolation); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::School, school_id); - p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_no_mask.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_no_mask.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_no_test.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_no_test.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Work, work_id); - p_no_isolation.set_assigned_location(mio::abm::LocationType::Home, home_id); + p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_compliant_go_to_work.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); + p_compliant_go_to_school.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_no_mask.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_no_test.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_no_isolation.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); auto testing_criteria = mio::abm::TestingCriteria( {}, {mio::abm::InfectionState::InfectedSymptoms, mio::abm::InfectionState::InfectedNoSymptoms}); @@ -781,11 +781,11 @@ TEST(TestModel, mobilityTripWithAppliedNPIs) // Using trip list mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_compliant_go_to_work.get_id(), t, work_id, home_id); - mio::abm::Trip trip2(p_compliant_go_to_school.get_id(), t, school_id, home_id); - mio::abm::Trip trip3(p_no_mask.get_id(), t, work_id, home_id); - mio::abm::Trip trip4(p_no_test.get_id(), t, work_id, home_id); - mio::abm::Trip trip5(p_no_isolation.get_id(), t, work_id, home_id); + mio::abm::Trip trip1(p_compliant_go_to_work.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip2(p_compliant_go_to_school.get_id(), t, school_id, home_id, mio::abm::LocationType::School); + mio::abm::Trip trip3(p_no_mask.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip4(p_no_test.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip5(p_no_isolation.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); trip_list.add_trip(trip1); trip_list.add_trip(trip2); trip_list.add_trip(trip3); diff --git a/cpp/tests/test_json_serializer.cpp b/cpp/tests/test_json_serializer.cpp index 52fe0a1df8..33729af67b 100644 --- a/cpp/tests/test_json_serializer.cpp +++ b/cpp/tests/test_json_serializer.cpp @@ -472,99 +472,3 @@ TEST(TestJsonSerializer, container_of_objects) ASSERT_THAT(print_wrap(r), IsSuccess()); EXPECT_THAT(r.value(), testing::UnorderedElementsAre(jsontest::Foo{1}, jsontest::Foo{2})); } - -TEST(TestJsonSerializer, abmLocation) -{ - auto location = mio::abm::Location(mio::abm::LocationType::Home, 0, num_age_groups); - auto js = mio::serialize_json(location); - Json::Value expected_json; - expected_json["index"] = Json::UInt64(0); - expected_json["type"] = Json::UInt64(mio::abm::LocationType::Home); - 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(), location); -} - -// 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) -{ - 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, - mio::abm::LocationType::Work); - auto js = mio::serialize_json(trip, true); - Json::Value expected_json; - expected_json["person_id"] = Json::UInt64(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 - expected_json["destination_type"] = Json::UInt64(mio::abm::LocationType::Work); - 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(), trip); -} - -// TEST(TestJsonSerializer, abmModel) // FIXME: (de)serialize is only partially implemented -// { -// auto model = mio::abm::Model(num_age_groups); -// auto home_id = model.add_location(mio::abm::LocationType::Home); -// auto work_id = model.add_location(mio::abm::LocationType::Work); -// auto person = model.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); -// model.get_trip_list().add_trip(trip1, false); -// model.get_trip_list().add_trip(trip2, true); -// auto js = mio::serialize_json(model); -// 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_mobility_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(), model); -// } From 8237f368e99640ecc3846b65f3956811f8b71450 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 21 Nov 2024 16:07:25 +0100 Subject: [PATCH 068/111] finx abm python bindings --- .../memilio/simulation/bindings/models/abm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp index 66a75f9b66..f9d366fbc2 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp @@ -193,6 +193,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def_readwrite("time", &mio::abm::Trip::time) .def_readwrite("destination", &mio::abm::Trip::destination) .def_readwrite("origin", &mio::abm::Trip::origin) + .def_readwrite("type_of_activity", &mio::abm::LocationType) .def_readwrite("cells", &mio::abm::Trip::cells); pymio::bind_class(m, "TripList") From a6e9c825d0d530db44b0aaa14b5f5161be68c0f9 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 21 Nov 2024 16:16:01 +0100 Subject: [PATCH 069/111] python test abm --- 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 1a4661c4a3..90703b731d 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -106,9 +106,9 @@ def test_simulation(self): # trips trip_list = abm.TripList() trip_list.add_trip(abm.Trip(0, abm.TimePoint( - 0) + abm.hours(8), social_event_id, 0, home_id, 0, abm.TransportMode.Unknown, abm.LocationType.SocialEvent)) + 0) + abm.hours(8), social_event_id, home_id, abm.LocationType.SocialEvent)) trip_list.add_trip(abm.Trip(1, abm.TimePoint(0) + - abm.hours(8), work_id, 0, home_id, 0, abm.TransportMode.Unknown, abm.LocationType.Work)) + abm.hours(8), work_id, home_id, abm.LocationType.Work)) model.trip_list = trip_list model.use_mobility_rules = False self.assertEqual(model.trip_list.num_trips(), 2) From cb977f1adf8f60e6efbefb1a6c6909b2db0e5cfb Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 21 Nov 2024 16:18:59 +0100 Subject: [PATCH 070/111] python bindings template abm simulation --- .../memilio/simulation/bindings/models/abm.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp index f9d366fbc2..5b60b50686 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp @@ -228,12 +228,12 @@ PYBIND11_MODULE(_simulation_abm, m) }, py::return_value_policy::reference_internal); - pymio::bind_class(m, "Simulation") + pymio::bind_class, pymio::EnablePickling::Never>(m, "Simulation") .def(py::init()) .def("advance", - static_cast(&mio::abm::Simulation::advance), + static_cast::*)(mio::abm::TimePoint)>(&mio::abm::Simulation<>::advance), py::arg("tmax")) - .def_property_readonly("model", py::overload_cast<>(&mio::abm::Simulation::get_model)); + .def_property_readonly("model", py::overload_cast<>(&mio::abm::Simulation<>::get_model)); m.attr("__version__") = "dev"; } From b2023afb47a639d3b88e1b1e7d7adbfeea3e1574 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 22 Nov 2024 09:27:24 +0100 Subject: [PATCH 071/111] abm python bindings --- cpp/models/abm/trip_list.h | 8 ++++---- cpp/models/graph_abm/model_wrapper.h | 1 - .../memilio/simulation/bindings/models/abm.cpp | 12 ++++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index ffde7bbd80..57602bbcc3 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -90,15 +90,15 @@ struct Trip { { } - Trip(PersonId id, TimePoint time_new, LocationId dest, LocationType type_of_activity, + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, const std::vector& input_cells = {}) - : Trip(id, time_new, dest, dest, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) + : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { } - Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationType type_of_activity, const std::vector& input_cells = {}) - : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) + : Trip(id, time_new, dest, dest, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { } diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 7e78a94d80..fb3e2a9fd0 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -132,7 +132,6 @@ class ModelWrapper : public abm::Model m_is_local_population_cache_valid = false; return true; } - return false; }; for (auto rule : Base::m_mobility_rules) { diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp index 5b60b50686..2055c635cf 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp @@ -142,12 +142,12 @@ PYBIND11_MODULE(_simulation_abm, m) .def("index", &mio::abm::LocationId::get); pymio::bind_class(m, "PersonId") - .def(py::init(), py::arg("id")) + .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); @@ -185,15 +185,15 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_Range().get_persons())>(m, "_ModelPersonsRange"); pymio::bind_class(m, "Trip") - .def(py::init>(), py::arg("person_id"), py::arg("time"), py::arg("destination"), py::arg("origin"), - py::arg("cells") = std::vector()) + py::arg("type_of_activity"), py::arg("cells") = std::vector()) .def_readwrite("person_id", &mio::abm::Trip::person_id) .def_readwrite("time", &mio::abm::Trip::time) .def_readwrite("destination", &mio::abm::Trip::destination) .def_readwrite("origin", &mio::abm::Trip::origin) - .def_readwrite("type_of_activity", &mio::abm::LocationType) + .def_readwrite("destination_type", &mio::abm::Trip::destination_type) .def_readwrite("cells", &mio::abm::Trip::cells); pymio::bind_class(m, "TripList") From 1e69b4dbdbbf5f8f9db62d324b10ab8dfb9890ca Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 6 Dec 2024 10:46:33 +0100 Subject: [PATCH 072/111] Debug logs --- cpp/models/abm/model.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 34b49f973b..a1de36963e 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -379,6 +379,7 @@ class Model */ Person& get_person(PersonId id) { + mio::log_debug("get_person is accessed by PersonID instead of person index. Therefore m_persons is searched."); auto it = std::find_if(m_persons.begin(), m_persons.end(), [id](auto& person) { return person.get_id() == id; }); @@ -390,6 +391,7 @@ class Model const Person& get_person(PersonId id) const { + mio::log_debug("get_person is accessed by PersonID instead of person index. Therefore m_persons is searched."); auto it = std::find_if(m_persons.begin(), m_persons.end(), [id](auto& person) { return person.get_id() == id; }); @@ -519,6 +521,7 @@ class Model */ uint32_t get_person_index(PersonId id) const { + mio::log_debug("get_person_index is used leading to a search in m_persons."); auto it = std::find_if(m_persons.begin(), m_persons.end(), [id](auto& person) { return person.get_id() == id; }); From 1eabac26c91fbeec5ac150bcfad0db25e34a776a Mon Sep 17 00:00:00 2001 From: jubicker <113909589+jubicker@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:36:45 +0100 Subject: [PATCH 073/111] Update cpp/examples/graph_abm.cpp Co-authored-by: David Kerkmann <44698825+DavidKerkmann@users.noreply.github.com> --- cpp/examples/graph_abm.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 7a143e3336..1294282b25 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -192,7 +192,6 @@ int main() auto start_date = mio::abm::TimePoint(0); auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - mio::unused(end_date); //Assign infection states and locations to persons from model 1 std::vector infection_distribution_m1{0.5, 0.3, 0.05, 0.05, 0.05, 0.05, 0.0, 0.0}; From 944736f902de7c2b5aa6f2970f4bf99d8d8cf71e Mon Sep 17 00:00:00 2001 From: jubicker <113909589+jubicker@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:46:45 +0100 Subject: [PATCH 074/111] Apply suggestions from code review Co-authored-by: David Kerkmann <44698825+DavidKerkmann@users.noreply.github.com> --- cpp/models/abm/model.h | 2 +- cpp/models/abm/person.cpp | 2 +- cpp/models/graph_abm/graph_abm_mobility.h | 2 +- cpp/models/graph_abm/model_wrapper.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index a1de36963e..fde65d55ee 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -574,7 +574,7 @@ class Model int m_id; ///< Model id. Is only used for abm graph model or hybrid model. std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. - std::vector m_activeness_statuses; ///< Vector with activeness status for every person + std::vector m_activeness_statuses; ///< Vector with activeness status for every person. Is only used for abm graph model or hybrid model. 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. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 7650c15cad..6f854a6f18 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -111,7 +111,7 @@ Infection& Person::get_infection() return m_infections.back(); } -void Person::set_assigned_location(LocationType type, LocationId id, int model_id) +void Person::set_assigned_location(LocationType type, LocationId id, int model_id = 0) { m_assigned_locations[static_cast(type)] = id; m_assigned_location_model_ids[static_cast(type)] = model_id; diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 9fb8dcb44c..263ca987ec 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -39,7 +39,7 @@ namespace mio { /** -* @brief Represents the ABM simulation in one node of the abm graph model. +* @brief Represents the ABM simulation in one node of the ABM graph model. */ template class ABMSimulationNode diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index fb3e2a9fd0..93285ed691 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -196,7 +196,7 @@ class ModelWrapper : public abm::Model } } - std::vector m_person_buffer; ///< List with indices of persons that are deactivated. + std::vector m_person_buffer; ///< List with indices of persons that are subject to move to another node. }; } // namespace mio From 26fe99f64a4a7aa5b2d77738f22402ea6e0d8391 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 10 Jan 2025 13:03:29 +0100 Subject: [PATCH 075/111] add unique person id --- cpp/examples/abm_history_object.cpp | 12 ++-- cpp/examples/abm_minimal.cpp | 2 +- cpp/examples/graph_abm.cpp | 22 +++--- cpp/models/abm/common_abm_loggers.h | 8 +-- cpp/models/abm/household.cpp | 2 +- cpp/models/abm/model.cpp | 39 +++++----- cpp/models/abm/model.h | 104 +++++++++++++++------------ cpp/models/abm/person.cpp | 24 +++++-- cpp/models/abm/person.h | 20 +++++- cpp/models/abm/person_id.h | 8 +-- cpp/models/abm/trip_list.h | 14 ++-- cpp/models/graph_abm/model_wrapper.h | 3 +- cpp/simulations/abm.cpp | 2 +- cpp/simulations/abm_braunschweig.cpp | 19 ++--- cpp/tests/test_abm_model.cpp | 83 +++++++++++---------- cpp/tests/test_abm_simulation.cpp | 65 ++++++++--------- cpp/tests/test_graph_abm.cpp | 34 ++++----- 17 files changed, 254 insertions(+), 207 deletions(-) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 7566d73193..02cf6808f9 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -152,17 +152,17 @@ int main() for (auto& person : model.get_persons()) { const auto pid = person.get_id(); //assign shop and event - model.assign_location(model.get_person_index(pid), event); - model.assign_location(model.get_person_index(pid), shop); + model.assign_location(pid, event); + model.assign_location(pid, shop); //assign hospital and ICU - model.assign_location(model.get_person_index(pid), hospital); - model.assign_location(model.get_person_index(pid), icu); + model.assign_location(pid, hospital); + model.assign_location(pid, icu); //assign work/school to people depending on their age if (person.get_age() == age_group_5_to_14) { - model.assign_location(model.get_person_index(pid), school); + model.assign_location(pid, school); } if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) { - model.assign_location(model.get_person_index(pid), work); + model.assign_location(pid, work); } } diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 74cbc4d303..7b7a21140e 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -130,7 +130,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto id = model.get_person_index(person.get_id()); + const auto id = person.get_id(); //assign shop and event model.assign_location(id, event); model.assign_location(id, shop); diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 1294282b25..b412185cc2 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -134,19 +134,19 @@ int main() auto model2 = mio::ModelWrapper(num_age_groups, 2); //Set infection parameters - model1.parameters.get() = 4.; - model1.parameters.get() = 2.; - model1.parameters.get() = 4.; - model1.parameters.get() = 5.; - model1.parameters.get() = 6.; - model1.parameters.get() = 8.; - model1.parameters.get() = 7.; - model1.parameters.get() = 10.; - model1.parameters.get() = 11.; + model2.parameters.get() = 4.; + model2.parameters.get() = 2.; + model2.parameters.get() = 4.; + model2.parameters.get() = 5.; + model2.parameters.get() = 6.; + model2.parameters.get() = 8.; + model2.parameters.get() = 7.; + model2.parameters.get() = 10.; + model2.parameters.get() = 11.; //Age group 0 goes to school and age group 1 goes to work - model1.parameters.get()[age_group_children] = true; - model1.parameters.get()[age_group_adults] = true; + model2.parameters.get()[age_group_children] = true; + model2.parameters.get()[age_group_adults] = true; //Household groups for model 2 auto single_hh_group_m2 = mio::abm::HouseholdGroup(); diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index aec3e2e202..2060cf1f9b 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -125,11 +125,9 @@ struct LogPersonInformation : mio::LogOnce { Type person_information{}; person_information.reserve(sim.get_model().get_persons().size()); for (auto& person : sim.get_model().get_persons()) { - person_information.push_back( - std::make_tuple(person.get_id(), - sim.get_model().find_location(mio::abm::LocationType::Home, - sim.get_model().get_person_index(person.get_id())), - person.get_age())); + person_information.push_back(std::make_tuple( + person.get_id(), sim.get_model().find_location(mio::abm::LocationType::Home, person.get_id()), + person.get_age())); } return person_information; } diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index 4a235168ed..db857b1f34 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -70,7 +70,7 @@ void add_household_to_model(Model& model, const Household& household) for (int j = 0; j < count; j++) { auto age_group = pick_age_group_from_age_distribution(model.get_rng(), member.get_age_weights()); auto person = model.add_person(home, age_group); - model.assign_location(model.get_person_index(person), home); + model.assign_location(person, home); } } } diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 6937a69fbb..c4340e33eb 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -24,6 +24,7 @@ #include "abm/person.h" #include "abm/location.h" #include "abm/mobility_rules.h" +#include "abm/person_id.h" #include "memilio/epidemiology/age_group.h" #include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" @@ -60,14 +61,16 @@ PersonId Model::add_person(Person&& person) assert(person.get_location() < LocationId((uint32_t)m_locations.size()) && "Added Person's location is not in Model."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); - if (person.get_id() == PersonId::invalid_id()) { - uint64_t id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - PersonId new_id{id}; + PersonId new_id{static_cast(m_persons.size())}; + if (person.get_unique_id() == INVALID_UNIQUE_ID) { + //if the person does not have a valid unique id yet, it gets one which is coumbound by model id and index in m_persons + uint64_t unique_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); - m_persons.emplace_back(person, new_id); + m_persons.emplace_back(person, new_id, unique_id); } else { - m_persons.emplace_back(person); + //if the person already has a valid unique id, it only gets a new PersonId + m_persons.emplace_back(person, new_id); } m_activeness_statuses.push_back(true); auto& new_person = m_persons.back(); @@ -91,9 +94,9 @@ void Model::interaction(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { - if (m_activeness_statuses[person_index]) { - interact(person_index, t, dt); + for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { + if (m_activeness_statuses[person_id]) { + interact(person_id, t, dt); } } } @@ -102,16 +105,16 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { - if (m_activeness_statuses[person_index]) { - Person& person = m_persons[person_index]; + for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { + if (m_activeness_statuses[person_id]) { + Person& person = m_persons[person_id]; auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); auto try_mobility_rule = [&](auto rule) -> bool { // run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); if (person.get_assigned_location_model_id(target_type) == m_id) { - const Location& target_location = get_location(find_location(target_type, person_index)); + const Location& target_location = get_location(find_location(target_type, person_id)); const LocationId current_location = person.get_location(); // the Person cannot move if they do not wear mask as required at targeted location @@ -140,7 +143,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) person.set_mask(MaskType::None, t); } // all requirements are met, move to target location - change_location(person_index, target_location.get_id()); + change_location(person_id, target_location.get_id()); return true; } return false; @@ -179,7 +182,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); m_trip_list.increase_index()) { auto& trip = m_trip_list.get_next_trip(weekend); - auto& person = get_person(static_cast(trip.person_id.get())); + auto& person = get_person(PersonId(static_cast(trip.person_id))); auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); // skip the trip if the person is in quarantine or is dead if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { @@ -195,7 +198,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) continue; } // all requirements are met, move to target location - change_location(static_cast(trip.person_id.get()), target_location.get_id(), trip.trip_mode); + change_location(person.get_id(), target_location.get_id(), trip.trip_mode); // update worn mask to target location's requirements if (target_location.is_mask_required()) { // if the current MaskProtection level is lower than required, the Person changes mask @@ -287,7 +290,7 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) if (person.get_location_model_id() == m_id) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, - get_location(uint32_t(i)), t, dt); + get_location(person.get_id()), t, dt); } } // implicit taskloop barrier } // implicit single barrier @@ -334,9 +337,9 @@ auto Model::get_activeness_statuses() -> Range& cells = {0}) + inline void change_location(PersonId person, LocationId destination, TransportMode mode = TransportMode::Unknown, + const std::vector& cells = {0}) { - LocationId origin = get_location(person_index).get_id(); + LocationId origin = get_location(person).get_id(); const bool has_changed_location = - mio::abm::change_location(get_person(person_index), get_location(destination), mode, cells); + mio::abm::change_location(get_person(person), get_location(destination), mode, cells); // if the person has changed location, invalidate exposure caches but keep population caches valid if (has_changed_location) { m_are_exposure_caches_valid = false; @@ -455,11 +469,11 @@ class Model /** * @brief Let a person interact with the population at its current location. - * @param[in] person_index Index of a person in m_persons vector of this Model. + * @param[in] person PersonId of a person from this Model. * @param[in] t Time step of the simulation. * @param[in] dt Step size of the simulation. */ - inline void interact(uint32_t person_index, TimePoint t, TimeSpan dt) + inline void interact(PersonId person, TimePoint t, TimeSpan dt) { if (!m_are_exposure_caches_valid) { // checking caches is only needed for external calls @@ -467,11 +481,10 @@ class Model compute_exposure_caches(t, dt); m_are_exposure_caches_valid = true; } - auto personal_rng = PersonalRandomNumberGenerator(m_rng, get_person(person_index)); - mio::abm::interact(personal_rng, get_person(person_index), get_location(person_index), - m_air_exposure_rates_cache[get_location(person_index).get_id().get()], - m_contact_exposure_rates_cache[get_location(person_index).get_id().get()], t, dt, - parameters); + 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_id().get()], + m_contact_exposure_rates_cache[get_location(person).get_id().get()], t, dt, parameters); } /** @@ -497,33 +510,33 @@ class Model /** * @brief Get a reference to the location of a person. - * @param[in] index Index of a person in m_persons. + * @param[in] id PersonId of a person. * @return Reference to the Location. * @{ */ - inline Location& get_location(uint32_t index) + inline Location& get_location(PersonId id) { - return get_location(get_person(index).get_location()); + return get_location(get_person(id).get_location()); } - inline const Location& get_location(uint32_t index) const + inline const Location& get_location(PersonId id) const { - return get_location(get_person(index).get_location()); + return get_location(get_person(id).get_location()); } /** @} */ /** * @brief Get index of person in m_persons. - * @param[in] id A person's PersonId. + * @param[in] id A person's unique id. * First 32 bit are the Person's individual id and second 32 bit the Persons's home model id. * @return Index of Person in m_persons vector. * @{ */ - uint32_t get_person_index(PersonId id) const + uint32_t get_person_index(uint64_t unique_id) const { mio::log_debug("get_person_index is used leading to a search in m_persons."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [id](auto& person) { - return person.get_id() == id; + auto it = std::find_if(m_persons.begin(), m_persons.end(), [unique_id](auto& person) { + return person.get_unique_id() == unique_id; }); if (it == m_persons.end()) { log_error("Given PersonId is not in this Model."); @@ -574,7 +587,8 @@ class Model int m_id; ///< Model id. Is only used for abm graph model or hybrid model. std::vector m_persons; ///< Vector of every Person. std::vector m_locations; ///< Vector of every Location. - std::vector m_activeness_statuses; ///< Vector with activeness status for every person. Is only used for abm graph model or hybrid model. + std::vector + m_activeness_statuses; ///< Vector with activeness status for every person. Is only used for abm graph model or hybrid model. 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. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 6f854a6f18..5d3257fbe2 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -24,6 +24,7 @@ #include "abm/infection.h" #include "abm/location.h" #include "memilio/utils/random_number_generator.h" +#include #include namespace mio @@ -32,7 +33,7 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id) + int location_model_id, AgeGroup age, PersonId person_id, uint64_t unique_id) : m_location(location_id) , m_location_type(location_type) , m_location_model_id(location_model_id) @@ -47,6 +48,7 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_last_transport_mode(TransportMode::Unknown) , m_test_results({TestType::Count}, TestResult()) , m_assigned_location_model_ids((int)LocationType::Count) + , m_unique_id(unique_id) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); @@ -60,6 +62,13 @@ Person::Person(const Person& other, PersonId id) m_person_id = id; } +Person::Person(const Person& other, PersonId id, uint64_t unique_id) + : Person(other) +{ + m_person_id = id; + m_unique_id = unique_id; +} + bool Person::is_infected(TimePoint t) const { if (m_infections.empty()) { @@ -199,6 +208,11 @@ PersonId Person::get_id() const return m_person_id; } +uint64_t Person::get_unique_id() const +{ + return m_unique_id; +} + std::vector& Person::get_cells() { return m_cells; @@ -223,14 +237,14 @@ bool Person::is_compliant(PersonalRandomNumberGenerator& rng, InterventionType i ProtectionEvent Person::get_latest_protection() const { ProtectionType latest_protection_type = ProtectionType::NoProtection; - TimePoint infection_time = TimePoint(0); + TimePoint infection_time = TimePoint(0); if (!m_infections.empty()) { latest_protection_type = ProtectionType::NaturalInfection; - infection_time = m_infections.back().get_start_date(); + infection_time = m_infections.back().get_start_date(); } if (!m_vaccinations.empty() && infection_time.days() <= m_vaccinations.back().time.days()) { latest_protection_type = m_vaccinations.back().type; - infection_time = m_vaccinations.back().time; + infection_time = m_vaccinations.back().time; } return ProtectionEvent{latest_protection_type, infection_time}; } @@ -249,7 +263,7 @@ ScalarType Person::get_protection_factor(TimePoint t, VirusVariant virus, const void Person::set_mask(MaskType type, TimePoint t) { m_mask.change_mask(type, t); -} +} void Person::add_test_result(TimePoint t, TestType type, bool result) { diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 80d2d22b86..622ca98cdf 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -37,6 +37,7 @@ #include "abm/mobility_data.h" #include "memilio/epidemiology/age_group.h" #include "memilio/utils/random_number_generator.h" +#include namespace mio { @@ -44,6 +45,7 @@ namespace abm { static constexpr uint32_t INVALID_PERSON_ID = std::numeric_limits::max(); +static constexpr uint64_t INVALID_UNIQUE_ID = std::numeric_limits::max(); /** * @brief Agents in the simulated Model that can carry and spread the Infection. @@ -59,16 +61,19 @@ class Person * @param[in] person_id Index of the Person. */ explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id = PersonId::invalid_id()); + int location_model_id, AgeGroup age, PersonId person_id = PersonId::invalid_id(), + uint64_t unique_id = INVALID_UNIQUE_ID); explicit Person(const Person& other, PersonId id); + explicit Person(const Person& other, PersonId id, uint64_t unique_id); + /** * @brief Compare two Person%s. */ bool operator==(const Person& other) const { - return (m_person_id == other.m_person_id); + return (m_unique_id == other.m_unique_id); } /** @@ -285,6 +290,13 @@ class Person */ PersonId get_id() const; + /** + * @brief Get the unique id of the Person. + * This id is only relevant for the graph abm and otherwise corresponds to the PersonId. + * @return The unique id. + */ + uint64_t get_unique_id() const; + /** * @brief Set the PersonId of the Person. * The PersonID should correspond to the index in m_persons in model. @@ -469,13 +481,15 @@ class Person Mask m_mask; ///< The Mask of the Person. std::vector m_compliance; ///< Vector of compliance values for all #InterventionType%s. Values from 0 to 1. - PersonId m_person_id; ///< Id of the Person. + PersonId m_person_id; ///< Id of the Person. Corresponds to the index in m_persons in Model. 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. CustomIndexArray m_test_results; ///< CustomIndexArray for TestResults. std::vector m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. + uint64_t + m_unique_id; ///< Unique identifier of a person. Is only relevant in graph abm, otherwise is equal to m_person_id. }; } // namespace abm diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index e6eaa019b9..9f65193535 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -30,16 +30,16 @@ namespace abm { /// Unique identifier for a Person within a Model. -struct MEMILIO_ENABLE_EBO PersonId : public mio::TypeSafe, public OperatorComparison { +struct MEMILIO_ENABLE_EBO PersonId : public mio::TypeSafe, public OperatorComparison { /// @brief Create an ID. - PersonId(uint64_t id) - : mio::TypeSafe(id) + PersonId(uint32_t id) + : mio::TypeSafe(id) { } /// @brief Create an invalid ID. PersonId() - : mio::TypeSafe(std::numeric_limits::max()) + : mio::TypeSafe(std::numeric_limits::max()) { } diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index 57602bbcc3..5ba4959a39 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -22,11 +22,13 @@ #include "abm/location_id.h" #include "abm/mobility_data.h" +#include "abm/person.h" #include "abm/person_id.h" #include "abm/time.h" #include "abm/location_type.h" #include "memilio/io/io.h" #include "memilio/io/default_serialize.h" +#include #include namespace mio @@ -39,7 +41,7 @@ namespace abm */ struct Trip { //TODO: Origin is currently not used for the trips. Should we delete it then? - PersonId person_id; /**< Person that makes the trip and corresponds to the index into the structure m_persons from + uint64_t person_id; /**< Person that makes the trip and corresponds to the index into the structure m_persons from Model, where all Person%s are saved.*/ TimePoint time; ///< Daytime at which a Person changes the Location. LocationId destination; ///< Location where the Person changes to. @@ -62,7 +64,7 @@ struct Trip { * @param[in] origin_model_id Model the Person starts the Trip. * @param[in] input_cells The index of the Cell%s the Person changes to. */ - Trip(PersonId id, TimePoint time_new, LocationId dest, int dest_model_id, LocationId orig, int orig_model_id, + Trip(uint64_t id, TimePoint time_new, LocationId dest, int dest_model_id, LocationId orig, int orig_model_id, TransportMode mode_of_transport, LocationType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) @@ -76,7 +78,7 @@ struct Trip { { } - Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, + Trip(uint64_t id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, LocationType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) @@ -90,13 +92,13 @@ struct Trip { { } - Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, + Trip(uint64_t id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, const std::vector& input_cells = {}) : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { } - Trip(PersonId id, TimePoint time_new, LocationId dest, LocationType type_of_activity, + Trip(uint64_t id, TimePoint time_new, LocationId dest, LocationType type_of_activity, const std::vector& input_cells = {}) : Trip(id, time_new, dest, dest, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { @@ -211,7 +213,7 @@ template <> struct DefaultFactory { static abm::Trip create() { - return abm::Trip{abm::PersonId{}, abm::TimePoint{}, abm::LocationId{}, abm::LocationType{}}; + return abm::Trip{abm::INVALID_UNIQUE_ID, abm::TimePoint{}, abm::LocationId{}, abm::LocationType{}}; } }; diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 93285ed691..46573c03e6 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -31,6 +31,7 @@ #include "abm/mobility_rules.h" #include "abm/mobility_rules.h" #include +#include #include #include @@ -152,7 +153,7 @@ class ModelWrapper : public abm::Model Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); Base::m_trip_list.increase_index()) { auto& trip = Base::m_trip_list.get_next_trip(weekend); - auto& person = get_person(static_cast(trip.person_id.get())); + auto& person = get_person(trip.person_id); auto person_index = Base::get_person_index(trip.person_id); auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); // skip the trip if the person is in quarantine or is dead diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 97167d5bee..ba25076ac1 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -379,7 +379,7 @@ void create_assign_locations(mio::abm::Model& model) //Assign locations to the people auto persons = model.get_persons(); for (auto& person : persons) { - const auto id = model.get_person_index(person.get_id()); + const auto id = person.get_id(); //assign shop and event model.assign_location(id, event); counter_event++; diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index e48556b00a..a0389f08f1 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -31,6 +31,7 @@ #include "boost/algorithm/string/split.hpp" #include "boost/algorithm/string/classification.hpp" +#include #include #include #include @@ -370,23 +371,23 @@ void create_model_from_data(mio::abm::Model& model, const std::string& filename, auto first_location = locations.find(first_location_id)->second; auto person_model_id = model.add_person(first_location, determine_age_group(age)); auto home = locations.find(home_id)->second; - model.assign_location(model.get_person_index(person_model_id), home); - model.assign_location(model.get_person_index(person_model_id), hospital); - model.assign_location(model.get_person_index(person_model_id), icu); + model.assign_location(person_model_id, home); + model.assign_location(person_model_id, hospital); + model.assign_location(person_model_id, icu); pid_itr = pids_data_to_model.insert_or_assign(person_data_id, person_model_id).first; } model.assign_location( - model.get_person_index(pid_itr->second), + pid_itr->second, target_location); //This assumes that we only have in each tripchain only one location type for each person if (locations.find(start_location_id) == locations.end()) { // For trips where the start location is not known use Home instead - start_location = model.get_person(model.get_person_index(pid_itr->second)) - .get_assigned_location(mio::abm::LocationType::Home); + start_location = model.get_person(pid_itr->second).get_assigned_location(mio::abm::LocationType::Home); } - model.get_trip_list().add_trip(mio::abm::Trip( - pid_itr->second, mio::abm::TimePoint(0) + mio::abm::minutes(trip_start), target_location, start_location, - mio::abm::TransportMode(transport_mode), mio::abm::LocationType(acticity_end))); + model.get_trip_list().add_trip( + mio::abm::Trip(static_cast(pid_itr->first), + mio::abm::TimePoint(0) + mio::abm::minutes(trip_start), target_location, start_location, + mio::abm::TransportMode(transport_mode), mio::abm::LocationType(acticity_end))); } model.get_trip_list().use_weekday_trips_on_weekend(); } diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index e54fe47732..661a2e0956 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -142,21 +142,21 @@ TEST_F(TestModel, findLocation) // Add a person to the model and assign them to multiple locations. auto person_id = add_test_person(model, home_id); - auto& person = model.get_person(model.get_person_index(person_id)); + auto& person = model.get_person(person_id); person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); person.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); // Verify that the find_location method correctly identifies each assigned location. - EXPECT_EQ(model.find_location(mio::abm::LocationType::Work, model.get_person_index(person_id)), work_id); - EXPECT_EQ(model.find_location(mio::abm::LocationType::School, model.get_person_index(person_id)), school_id); - EXPECT_EQ(model.find_location(mio::abm::LocationType::Home, model.get_person_index(person_id)), home_id); + EXPECT_EQ(model.find_location(mio::abm::LocationType::Work, person_id), work_id); + EXPECT_EQ(model.find_location(mio::abm::LocationType::School, person_id), school_id); + EXPECT_EQ(model.find_location(mio::abm::LocationType::Home, person_id), home_id); // Check that the method also works with a constant reference to the model. auto&& model_test = std::as_const(model); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Work, model.get_person_index(person_id)), work_id); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::School, model.get_person_index(person_id)), school_id); - EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Home, model.get_person_index(person_id)), home_id); + EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Work, person_id), work_id); + EXPECT_EQ(model_test.find_location(mio::abm::LocationType::School, person_id), school_id); + EXPECT_EQ(model_test.find_location(mio::abm::LocationType::Home, person_id), home_id); } /** @@ -257,8 +257,8 @@ TEST_F(TestModel, evolveMobilityRules) auto pid2 = add_test_person(model, home_id, age_group_5_to_14, mio::abm::InfectionState::Susceptible, t); auto pid1 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); - auto& p1 = model.get_person(model.get_person_index(pid1)); - auto& p2 = model.get_person(model.get_person_index(pid2)); + auto& p1 = model.get_person(pid1); + auto& p2 = model.get_person(pid2); p1.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); p2.set_assigned_location(mio::abm::LocationType::School, school_id, model.get_id()); @@ -334,11 +334,11 @@ TEST_F(TestModel, evolveMobilityTrips) auto pid5 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Susceptible, t); // Assign persons to locations for trips. - auto& p1 = model.get_person(model.get_person_index(pid1)); - auto& p2 = model.get_person(model.get_person_index(pid2)); - auto& p3 = model.get_person(model.get_person_index(pid3)); - auto& p4 = model.get_person(model.get_person_index(pid4)); - auto& p5 = model.get_person(model.get_person_index(pid5)); + auto& p1 = model.get_person(pid1); + auto& p2 = model.get_person(pid2); + auto& p3 = model.get_person(pid3); + auto& p4 = model.get_person(pid4); + auto& p5 = model.get_person(pid5); p1.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); p2.set_assigned_location(mio::abm::LocationType::SocialEvent, event_id, model.get_id()); @@ -356,11 +356,11 @@ TEST_F(TestModel, evolveMobilityTrips) // Set trips for persons between assigned locations. mio::abm::TripList& data = model.get_trip_list(); - mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id, + mio::abm::Trip trip1(p1.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, + mio::abm::Trip trip2(p2.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, + mio::abm::Trip trip3(p5.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, mio::abm::LocationType::SocialEvent); data.add_trip(trip1); data.add_trip(trip2); @@ -388,10 +388,10 @@ TEST_F(TestModel, evolveMobilityTrips) EXPECT_EQ(model.get_number_persons(hospital_id), 1); // Move all persons back to their home location to prepare for weekend trips. - model.change_location(model.get_person_index(p1.get_id()), home_id); - model.change_location(model.get_person_index(p1.get_id()), home_id); - model.change_location(model.get_person_index(p2.get_id()), home_id); - model.change_location(model.get_person_index(p5.get_id()), home_id); + model.change_location(p1.get_id(), home_id); + model.change_location(p1.get_id(), home_id); + model.change_location(p2.get_id(), home_id); + model.change_location(p5.get_id(), home_id); // Update the time to the weekend and reset the trip index. t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); @@ -411,12 +411,12 @@ TEST_F(TestModel, evolveMobilityTrips) // Add additional weekend trips for further verification. bool weekend = true; - mio::abm::Trip tripweekend1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), event_id, - work_id, mio::abm::LocationType::SocialEvent); - mio::abm::Trip tripweekend2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), home_id, - event_id, mio::abm::LocationType::Home); - mio::abm::Trip tripweekend3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), work_id, - event_id, mio::abm::LocationType::Work); + mio::abm::Trip tripweekend1(p1.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + event_id, work_id, mio::abm::LocationType::SocialEvent); + mio::abm::Trip tripweekend2(p2.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + home_id, event_id, mio::abm::LocationType::Home); + mio::abm::Trip tripweekend3(p5.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + work_id, event_id, mio::abm::LocationType::Work); data.add_trip(tripweekend1, weekend); data.add_trip(tripweekend2, weekend); data.add_trip(tripweekend3, weekend); @@ -526,11 +526,11 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) // Add trip to see if a dead person can change location outside of cemetery by scheduled trips mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, + mio::abm::Trip trip1(p_dead.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::Trip trip2(p_dead.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, mio::abm::LocationType::Home); - mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::Trip trip3(p_severe.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, mio::abm::LocationType::Home); trip_list.add_trip(trip1); trip_list.add_trip(trip2); @@ -538,17 +538,15 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) // Check the dead person got burried and the severely infected person starts in Hospital model.evolve(t, dt); - EXPECT_EQ(model.get_location(model.get_person_index(p_dead.get_id())).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.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(model.get_location(model.get_person_index(p_severe.get_id())).get_type(), - mio::abm::LocationType::Hospital); + EXPECT_EQ(model.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 model.evolve(t + dt, dt); - EXPECT_EQ(model.get_location(model.get_person_index(p_dead.get_id())).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.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(model.get_location(model.get_person_index(p_severe.get_id())).get_type(), - mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); } using TestModelTestingCriteria = RandomNumberTest; @@ -576,7 +574,7 @@ TEST_F(TestModelTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) // Since tests are performed before current_time, the InfectionState of the Person has to take into account test_time auto pid = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time - test_time); - auto& person = model.get_person(model.get_person_index(pid)); + auto& person = model.get_person(pid); auto rng_person = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); @@ -928,11 +926,12 @@ TEST_F(TestModel, mobilityTripWithAppliedNPIs) // Using trip list mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_compliant_go_to_work.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p_compliant_go_to_school.get_id(), t, school_id, home_id, mio::abm::LocationType::School); - mio::abm::Trip trip3(p_no_mask.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip4(p_no_test.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip5(p_no_isolation.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip1(p_compliant_go_to_work.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip2(p_compliant_go_to_school.get_unique_id(), t, school_id, home_id, + mio::abm::LocationType::School); + mio::abm::Trip trip3(p_no_mask.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip4(p_no_test.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip5(p_no_isolation.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); trip_list.add_trip(trip1); trip_list.add_trip(trip2); trip_list.add_trip(trip3); diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index c82e295024..7798bbf50a 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -22,6 +22,7 @@ #include "abm/common_abm_loggers.h" #include "matchers.h" #include "memilio/io/history.h" +#include TEST(TestSimulation, advance_random) { @@ -33,10 +34,10 @@ TEST(TestSimulation, advance_random) auto p3 = model.add_person(location2, age_group_5_to_14); auto p4 = model.add_person(location2, age_group_5_to_14); - model.assign_location(model.get_person_index(p1), location1); - model.assign_location(model.get_person_index(p2), location1); - model.assign_location(model.get_person_index(p3), location2); - model.assign_location(model.get_person_index(p4), location2); + model.assign_location(p1, location1); + model.assign_location(p2, location1); + model.assign_location(p3, location2); + model.assign_location(p4, location2); auto sim = mio::abm::Simulation(mio::abm::TimePoint(0), std::move(model)); @@ -81,38 +82,38 @@ TEST(TestSimulation, advanceWithCommonHistory) auto person2 = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::Exposed); auto person3 = add_test_person(model, home_id, age_group_35_to_59, mio::abm::InfectionState::Dead); - model.assign_location(model.get_person_index(person1), home_id); - model.assign_location(model.get_person_index(person2), home_id); - model.assign_location(model.get_person_index(person3), home_id); - model.assign_location(model.get_person_index(person1), school_id); - model.assign_location(model.get_person_index(person2), work_id); - model.assign_location(model.get_person_index(person2), icu_id); - model.assign_location(model.get_person_index(person2), hospital_id); - model.assign_location(model.get_person_index(person1), social_id); - model.assign_location(model.get_person_index(person2), social_id); - model.assign_location(model.get_person_index(person3), social_id); - model.assign_location(model.get_person_index(person1), basics_id); - model.assign_location(model.get_person_index(person2), basics_id); - model.assign_location(model.get_person_index(person3), basics_id); - model.assign_location(model.get_person_index(person2), public_id); + model.assign_location(person1, home_id); + model.assign_location(person2, home_id); + model.assign_location(person3, home_id); + model.assign_location(person1, school_id); + model.assign_location(person2, work_id); + model.assign_location(person2, icu_id); + model.assign_location(person2, hospital_id); + model.assign_location(person1, social_id); + model.assign_location(person2, social_id); + model.assign_location(person3, social_id); + model.assign_location(person1, basics_id); + model.assign_location(person2, basics_id); + model.assign_location(person3, basics_id); + model.assign_location(person2, public_id); mio::abm::TripList& trip_list = model.get_trip_list(); // We add trips for person two to test the history and if it is working correctly - mio::abm::Trip trip1(person2, mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, - mio::abm::LocationType::Work); - mio::abm::Trip trip2(person2, mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id, home_id, - mio::abm::LocationType::ICU); - mio::abm::Trip trip3(person2, mio::abm::TimePoint(0) + mio::abm::hours(4), hospital_id, home_id, - mio::abm::LocationType::Hospital); - mio::abm::Trip trip4(person2, mio::abm::TimePoint(0) + mio::abm::hours(5), social_id, home_id, - mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip5(person2, mio::abm::TimePoint(0) + mio::abm::hours(6), basics_id, home_id, - mio::abm::LocationType::BasicsShop); - mio::abm::Trip trip6(person2, mio::abm::TimePoint(0) + mio::abm::hours(7), public_id, home_id, - mio::abm::LocationType::PublicTransport); - mio::abm::Trip trip7(person2, mio::abm::TimePoint(0) + mio::abm::hours(8), home_id, home_id, - mio::abm::LocationType::Home); + mio::abm::Trip trip1(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, + home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip2(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(3), icu_id, + home_id, mio::abm::LocationType::ICU); + mio::abm::Trip trip3(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(4), hospital_id, + home_id, mio::abm::LocationType::Hospital); + mio::abm::Trip trip4(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(5), social_id, + home_id, mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip5(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(6), basics_id, + home_id, mio::abm::LocationType::BasicsShop); + mio::abm::Trip trip6(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(7), public_id, + home_id, mio::abm::LocationType::PublicTransport); + mio::abm::Trip trip7(static_cast(person2.get()), mio::abm::TimePoint(0) + mio::abm::hours(8), home_id, + home_id, mio::abm::LocationType::Home); trip_list.add_trip(trip1); trip_list.add_trip(trip2); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index af04831d39..e97b67b382 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -55,7 +55,7 @@ TEST(TestGraphAbm, test_evolve_node) auto& home = model.get_location(home_id); auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0), size_t(1), 2); auto pid = model.add_person(home_id, mio::AgeGroup(0)); - auto index = model.get_person_index(pid); + auto index = pid.get(); auto& p = model.get_person(pid); p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p.set_assigned_location(work.get_type(), work.get_id(), 2); @@ -91,16 +91,14 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); - auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); - auto p2_index = model1.get_person_index(p2_id); - auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); - auto p4_index = model1.get_person_index(p4_id); - auto& p1 = model1.get_person(p1_id); - auto& p2 = model1.get_person(p2_id); - auto& p3 = model1.get_person(p3_id); - auto& p4 = model1.get_person(p4_id); + auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); + auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto& p1 = model1.get_person(p1_id); + auto& p2 = model1.get_person(p2_id); + auto& p3 = model1.get_person(p3_id); + auto& p4 = model1.get_person(p4_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); @@ -111,10 +109,12 @@ TEST(TestGraphAbm, test_apply_mobility) p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); mio::abm::TripList& trips = model1.get_trip_list(); - mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, - model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, - model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip1(p3.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), + home_id, model1.get_id(), mio::abm::TransportMode::Unknown, + mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip2(p4.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), + home_id, model1.get_id(), mio::abm::TransportMode::Unknown, + mio::abm::LocationType::SocialEvent); trips.add_trip(trip1); trips.add_trip(trip2); @@ -129,8 +129,8 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_index], false); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_index], false); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_id.get()], false); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); From 66896aa8e5acb5a46581aac1acfe0dc5c96ed68c Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 10 Jan 2025 15:28:10 +0100 Subject: [PATCH 076/111] review comment --- cpp/benchmarks/abm.cpp | 3 --- cpp/examples/graph_abm.cpp | 7 ++++--- cpp/models/abm/model.cpp | 4 ++-- cpp/models/graph_abm/graph_abm_mobility.h | 8 +++++--- cpp/tests/test_json_serializer.cpp | 1 - .../memilio/simulation/bindings/models/abm.cpp | 4 +++- .../memilio/simulation_test/test_abm.py | 4 ++-- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 1c26661cce..249796f8df 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -1,9 +1,6 @@ #include "abm/simulation.h" #include "benchmark/benchmark.h" -#include -#include -#include mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list seeds) { diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index b412185cc2..5dac912502 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -257,10 +257,11 @@ int main() mio::Graph, mio::ABMMobilityEdge> graph; graph.add_node(model1.get_id(), HistoryType{}, start_date, std::move(model1)); graph.add_node(model2.get_id(), HistoryType{}, start_date, std::move(model2)); - graph.add_edge(0, 1); - graph.add_edge(1, 0); + graph.add_edge(model1.get_id(), model2.get_id()); + graph.add_edge(model2.get_id(), model1.get_id()); - auto sim = mio::make_abm_graph_sim(start_date, mio::abm::hours(12), std::move(graph)); + auto exchange_time_span = mio::abm::hours(12); + auto sim = mio::make_abm_graph_sim(start_date, exchange_time_span, std::move(graph)); sim.advance(end_date); return 0; diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index c4340e33eb..a724103176 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -229,7 +229,7 @@ void Model::build_compute_local_population_cache() const } // implicit taskloop barrier PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_persons; i++) { - if (m_persons[i].get_location_model_id() == m_id) { + if (m_persons[i].get_location_model_id() == m_id && m_activeness_statuses[i]) { ++m_local_population_cache[m_persons[i].get_location().get()]; } } // implicit taskloop barrier @@ -287,7 +287,7 @@ void Model::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().get(); - if (person.get_location_model_id() == m_id) { + if (person.get_location_model_id() == m_id && m_activeness_statuses[i]) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, get_location(person.get_id()), t, dt); diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 263ca987ec..000080af47 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -106,7 +106,7 @@ class ABMMobilityEdge public: /** * @brief Exchanges persons via the edge. - * Commuters are given by the ABMMobilityParameters and exchanged via mobility rules also given ABMMobilityParameters. + * Commuters are given by the person buffer of node_from. * @param[in] node_from Commuters home node * @param[in] node_to Node commuters (temporarily) move to * @param[in] t Echange time point @@ -144,6 +144,8 @@ class ABMMobilityEdge /** * @brief Edge functor for abm graph simulation. * @see ABMMobilityEdge::apply_mobility + * The attribute dt is required by the GraphSimulation class and therefore an input argument of the function. + * However it is not used in ABMMobilityEdge::apply_mobility. * @param[in] t Time point the functor is applied. * @param[in] edge ABMMobilityEdge for which the functor is applied. * @param[in] node_from Edge start node. @@ -171,8 +173,8 @@ void evolve_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode(&mio::abm::Model::add_person), py::arg("location_id"), py::arg("age_group")) - .def("assign_location", &mio::abm::Model::assign_location, py::arg("person_id"), py::arg("location_id")) + .def("assign_location", + py::overload_cast(&mio::abm::Model::assign_location), + py::arg("person_id"), py::arg("location_id")) .def_property_readonly("locations", py::overload_cast<>(&mio::abm::Model::get_locations, py::const_), py::keep_alive<1, 0>{}) //keep this model alive while contents are referenced in ranges .def_property_readonly("persons", py::overload_cast<>(&mio::abm::Model::get_persons, py::const_), diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index 90703b731d..bf7c1d2555 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -95,8 +95,8 @@ def test_simulation(self): p2_id = model.add_person(home_id, mio.AgeGroup(2)) for loc_id in [home_id, social_event_id, work_id]: - model.assign_location(0, loc_id) - model.assign_location(1, loc_id) + model.assign_location(p1_id, loc_id) + model.assign_location(p2_id, loc_id) model.parameters.InfectedSymptomsToSevere[abm.VirusVariant.Wildtype, mio.AgeGroup( 0)] = 0.0 From 8a97b5cacc8c9da666826984bd9152d3310c5918 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 10 Jan 2025 15:50:19 +0100 Subject: [PATCH 077/111] fix CI --- cpp/examples/graph_abm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 5dac912502..ef94488041 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -73,7 +73,7 @@ int main() const auto age_group_adults = mio::AgeGroup(1); const auto age_group_seniors = mio::AgeGroup(2); - auto model1 = mio::ModelWrapper(num_age_groups, 1); + auto model1 = mio::ModelWrapper(num_age_groups, 0); //Set infection parameters model1.parameters.get() = 4.; @@ -131,7 +131,7 @@ int main() add_household_group_to_model(model1, single_hh_group_m1); add_household_group_to_model(model1, family_hh_group_m1); - auto model2 = mio::ModelWrapper(num_age_groups, 2); + auto model2 = mio::ModelWrapper(num_age_groups, 1); //Set infection parameters model2.parameters.get() = 4.; From a106505bd6105c576813eb9227ae066c4775bcfb Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 13 Jan 2025 10:34:56 +0100 Subject: [PATCH 078/111] bug fix --- cpp/models/abm/person.cpp | 7 ++++++- cpp/models/abm/person.h | 2 +- cpp/models/graph_abm/graph_abm_mobility.h | 2 ++ cpp/models/graph_abm/model_wrapper.h | 10 ++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 5d3257fbe2..5f20ed5e8f 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -208,11 +208,16 @@ PersonId Person::get_id() const return m_person_id; } -uint64_t Person::get_unique_id() const +const uint64_t Person::get_unique_id() const { return m_unique_id; } +void Person::set_id(PersonId id) +{ + m_person_id = id; +} + std::vector& Person::get_cells() { return m_cells; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 622ca98cdf..a9a17ec05a 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -295,7 +295,7 @@ class Person * This id is only relevant for the graph abm and otherwise corresponds to the PersonId. * @return The unique id. */ - uint64_t get_unique_id() const; + const uint64_t get_unique_id() const; /** * @brief Set the PersonId of the Person. diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 000080af47..bd56931e5a 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -138,6 +138,8 @@ class ABMMobilityEdge persons_to_change.erase(persons_to_change.begin() + i); } } + //update person ids in model_from + model_from.update_person_ids(); } }; diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index 46573c03e6..ed4a370176 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -80,6 +80,16 @@ class ModelWrapper : public abm::Model perform_mobility(t, dt); } + void update_person_ids() + { + for (uint32_t i = 0; i < static_cast(m_persons.size()); ++i) { + auto& person = m_persons[i]; + if (person.get_id().get() != i) { + person.set_id(abm::PersonId{i}); + } + } + } + private: void perform_mobility(TimePoint t, TimeSpan dt) { From 34ebe3bd7213e305701df1678026040d70a9b2d6 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 13 Jan 2025 10:41:00 +0100 Subject: [PATCH 079/111] remove const from id getter --- cpp/models/abm/person.cpp | 2 +- cpp/models/abm/person.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 5f20ed5e8f..87cc2a838f 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -208,7 +208,7 @@ PersonId Person::get_id() const return m_person_id; } -const uint64_t Person::get_unique_id() const +uint64_t Person::get_unique_id() const { return m_unique_id; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index a9a17ec05a..622ca98cdf 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -295,7 +295,7 @@ class Person * This id is only relevant for the graph abm and otherwise corresponds to the PersonId. * @return The unique id. */ - const uint64_t get_unique_id() const; + uint64_t get_unique_id() const; /** * @brief Set the PersonId of the Person. From f21f37f70fadf7d317aedc7c20380a9dea2104e1 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 09:26:37 +0100 Subject: [PATCH 080/111] sort m_persons vector before moving persons between nodes --- cpp/models/graph_abm/graph_abm_mobility.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index bd56931e5a..d1bfb1f2f5 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -32,7 +32,9 @@ #include "memilio/mobility/graph_simulation.h" #include "memilio/mobility/graph.h" #include "memilio/utils/compiler_diagnostics.h" +#include #include +#include #include #include @@ -117,6 +119,8 @@ class ABMMobilityEdge auto& model_from = node_from.get_simulation().get_model(); auto& model_to = node_to.get_simulation().get_model(); auto& persons_to_change = model_from.get_person_buffer(); + //sort vector such that we start removing the persons from the bottom + std::sort(persons_to_change.begin(), persons_to_change.end()); //iterate over all persons that change from node_from for (int i = int(persons_to_change.size()) - 1; i >= 0; --i) { auto& person = model_from.get_persons()[persons_to_change[i]]; @@ -130,7 +134,8 @@ class ABMMobilityEdge model_to.add_person(std::move(person)); //remove person from model_from model_from.remove_person(persons_to_change[i]); - // correct indices in persons buffer from node_from + //correct indices in persons buffer from node_from + //here it is required that the vector is sorted for (size_t j = i + 1; j < persons_to_change.size(); ++j) { persons_to_change[j]--; } From 6a76553cc967b97947c1a2bdfb0184961093e29a Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 09:59:05 +0100 Subject: [PATCH 081/111] unique id --- cpp/models/abm/model.cpp | 19 +++++++------------ cpp/tests/abm_helpers.cpp | 10 ++++++---- cpp/tests/abm_helpers.h | 5 +++-- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index a724103176..759d76e01c 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -52,7 +52,8 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) PersonId Model::add_person(const LocationId id, AgeGroup age) { - return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age)); + uint64_t unique_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); + return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, PersonId::invalid_id(), unique_id)); } PersonId Model::add_person(Person&& person) @@ -60,21 +61,15 @@ PersonId Model::add_person(Person&& person) 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 Model."); + assert(person.get_unique_id() != INVALID_UNIQUE_ID && "Added Person's unique id must be valid."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); PersonId new_id{static_cast(m_persons.size())}; - if (person.get_unique_id() == INVALID_UNIQUE_ID) { - //if the person does not have a valid unique id yet, it gets one which is coumbound by model id and index in m_persons - uint64_t unique_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); - m_persons.emplace_back(person, new_id, unique_id); - } - else { - //if the person already has a valid unique id, it only gets a new PersonId - m_persons.emplace_back(person, new_id); - } + //set correct person id aka index in m_persons vector + person.set_id(new_id); + person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); + m_persons.emplace_back(person); m_activeness_statuses.push_back(true); auto& new_person = m_persons.back(); - if (m_is_local_population_cache_valid) { ++m_local_population_cache[new_person.get_location().get()]; } diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 71acb422af..b1238c3ecc 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -19,14 +19,16 @@ */ #include "abm_helpers.h" #include "abm/person.h" +#include "abm/person_id.h" #include "memilio/utils/random_number_generator.h" mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t, - mio::abm::Parameters params) + mio::abm::Parameters params, uint64_t id) { assert(age.get() < params.get_num_groups()); - mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age); + mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, + mio::abm::PersonId::invalid_id(), id); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( @@ -38,8 +40,8 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t) { - return model.add_person( - make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, model.parameters)); + return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, + model.parameters, static_cast(model.get_persons().size()))); } void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index f4488ff4e0..050e8ca112 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -92,10 +92,11 @@ struct ScopedMockDistribution { /** * @brief Create a Person without a Model object. Intended for simple use in tests. */ -mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age = age_group_15_to_34, +mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, 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), - mio::abm::Parameters params = mio::abm::Parameters(num_age_groups)); + mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), uint64_t id = 0); /** * @brief Add a Person to the Model. Intended for simple use in tests. From e0f3be12834b8fe954723c898d5aa0827eeb1474 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 11:29:28 +0100 Subject: [PATCH 082/111] extend tests --- cpp/models/graph_abm/graph_abm_mobility.h | 2 +- cpp/tests/test_abm_person.cpp | 8 ++++++++ cpp/tests/test_graph_abm.cpp | 23 ++++++++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index d1bfb1f2f5..9af1247e83 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -51,7 +51,7 @@ class ABMSimulationNode using Sim = abm::Simulation; template ::value, void>> - ABMSimulationNode(std::tuple history, Args&&... args) + ABMSimulationNode(std::tuple&& history, Args&&... args) : m_simulation(std::forward(args)...) , m_history(history) { diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 040841915e..a4ef99f7e1 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/mobility_rules.h" #include "abm/person.h" +#include "abm/person_id.h" #include "abm/time.h" #include "abm_helpers.h" #include "random_number_test.h" @@ -44,6 +45,13 @@ TEST_F(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_id(), mio::abm::PersonId::invalid_id()); + + // Verify copy constructor + auto copied_person = mio::abm::Person(person, mio::abm::PersonId(0), 0); + EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); + EXPECT_EQ(copied_person.get_location(), location.get_id()); + EXPECT_EQ(copied_person.get_id(), mio::abm::PersonId(0)); + EXPECT_EQ(copied_person.get_unique_id(), 0); } /** diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index e97b67b382..44c8653e06 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -75,36 +75,51 @@ TEST(TestGraphAbm, test_apply_mobility) mio::ModelWrapper(size_t(2), 1, std::vector{&mio::abm::go_to_work}); auto model2 = mio::ModelWrapper(size_t(2), 2, std::vector{&mio::abm::go_to_work}); + auto model3 = + mio::ModelWrapper(size_t(2), 3, std::vector{&mio::abm::go_to_work}); model1.parameters.get()[mio::AgeGroup(0)] = true; model2.parameters.get()[mio::AgeGroup(0)] = true; + model3.parameters.get()[mio::AgeGroup(0)] = true; + + //all persons go to work at 7am + model1.parameters.get()[mio::AgeGroup(0)] = mio::abm::hours(8); + model1.parameters.get()[mio::AgeGroup(0)] = mio::abm::hours(8); + auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); auto home_id = model1.add_location(mio::abm::LocationType::Home); auto work_id_2 = model2.add_location(mio::abm::LocationType::Work); + auto work_id_3 = model3.add_location(mio::abm::LocationType::Work); auto event_id_1 = model1.add_location(mio::abm::LocationType::SocialEvent); auto event_id_2 = model2.add_location(mio::abm::LocationType::SocialEvent); auto& work_1 = model1.get_location(work_id_1); auto& work_2 = model2.get_location(work_id_2); + auto& work_3 = model3.get_location(work_id_3); auto& home = model1.get_location(home_id); auto& event_1 = model1.get_location(event_id_1); auto& event_2 = model2.get_location(event_id_2); EXPECT_EQ(work_1.get_model_id(), 1); EXPECT_EQ(work_2.get_model_id(), 2); + EXPECT_EQ(work_3.get_model_id(), 3); auto p1_id = model1.add_person(home_id, mio::AgeGroup(0)); auto p2_id = model1.add_person(home_id, mio::AgeGroup(0)); auto p3_id = model1.add_person(home_id, mio::AgeGroup(1)); auto p4_id = model1.add_person(home_id, mio::AgeGroup(1)); + auto p5_id = model1.add_person(home_id, mio::AgeGroup(0)); auto& p1 = model1.get_person(p1_id); auto& p2 = model1.get_person(p2_id); auto& p3 = model1.get_person(p3_id); auto& p4 = model1.get_person(p4_id); + auto& p5 = model1.get_person(p5_id); p1.set_assigned_location(work_1.get_type(), work_1.get_id(), work_1.get_model_id()); p2.set_assigned_location(work_2.get_type(), work_2.get_id(), work_2.get_model_id()); + p5.set_assigned_location(work_3.get_type(), work_3.get_id(), work_3.get_model_id()); p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p3.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p4.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p5.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p3.set_assigned_location(event_1.get_type(), event_1.get_id(), event_1.get_model_id()); p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); @@ -123,20 +138,26 @@ TEST(TestGraphAbm, test_apply_mobility) auto dt = mio::abm::hours(12); mio::ABMSimulationNode node1(MockHistory{}, t, std::move(model1)); mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); + mio::ABMSimulationNode node3(MockHistory{}, t, std::move(model3)); node1.evolve(t, dt); node2.evolve(t, dt); + node3.evolve(t, dt); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); - EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 4); + EXPECT_EQ(node3.get_simulation().get_model().get_persons().size(), 0); + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 5); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_id.get()], false); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); + EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p5_id.get()], false); mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); + edge.apply_mobility(node1, node3, t); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); + EXPECT_EQ(node3.get_simulation().get_model().get_persons().size(), 1); EXPECT_EQ(node1.get_simulation().get_model().get_person_buffer().size(), 0); } From 788724a2a2f4caa57a11c53aec7677b6eecedb51 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 11:49:46 +0100 Subject: [PATCH 083/111] fix test --- cpp/tests/test_graph_abm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 44c8653e06..fa47aab9b8 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -82,8 +82,8 @@ TEST(TestGraphAbm, test_apply_mobility) model3.parameters.get()[mio::AgeGroup(0)] = true; //all persons go to work at 7am - model1.parameters.get()[mio::AgeGroup(0)] = mio::abm::hours(8); - model1.parameters.get()[mio::AgeGroup(0)] = mio::abm::hours(8); + model1.parameters.get()[mio::AgeGroup(0)] = mio::abm::hours(7); + model1.parameters.get()[mio::AgeGroup(0)] = mio::abm::hours(7); auto work_id_1 = model1.add_location(mio::abm::LocationType::Work); auto home_id = model1.add_location(mio::abm::LocationType::Home); From a807dd0ac480da3543db5bdec40d3b6c46c8e40b Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 12:47:22 +0100 Subject: [PATCH 084/111] [CI Skip] test --- cpp/tests/test_graph_abm.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index fa47aab9b8..995f4b9843 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -35,6 +35,7 @@ #include #include #include +#include struct MockHistory { @@ -151,10 +152,19 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p5_id.get()], false); + auto& persons_buffer = node1.get_simulation().get_model().get_person_buffer(); + for (auto i = size_t(0); i < persons_buffer.size(); ++i) { + std::cout << persons_buffer[i] << "\n"; + } + mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); edge.apply_mobility(node1, node3, t); + for (auto i = size_t(0); i < persons_buffer.size(); ++i) { + std::cout << persons_buffer[i] << "\n"; + } + EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node3.get_simulation().get_model().get_persons().size(), 1); From 7a4d6d64ad72f0e75fd842d18397d3eb6f2b7a60 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 13:24:15 +0100 Subject: [PATCH 085/111] [ci skip] --- cpp/tests/test_graph_abm.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 995f4b9843..912126edd0 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -152,17 +152,18 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p5_id.get()], false); - auto& persons_buffer = node1.get_simulation().get_model().get_person_buffer(); - for (auto i = size_t(0); i < persons_buffer.size(); ++i) { - std::cout << persons_buffer[i] << "\n"; + std::cout << "Size First: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; + for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { + std::cout << "First: " << node1.get_simulation().get_model().get_person_buffer()[i] << "\n"; } mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); edge.apply_mobility(node1, node3, t); - for (auto i = size_t(0); i < persons_buffer.size(); ++i) { - std::cout << persons_buffer[i] << "\n"; + std::cout << "Size Second: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; + for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { + std::cout << "Second: " << node1.get_simulation().get_model().get_person_buffer()[i] << "\n"; } EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); From 17ff52821b1f3e61332a5f294794a47785071405 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 13:29:09 +0100 Subject: [PATCH 086/111] test --- cpp/tests/test_graph_abm.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 912126edd0..0d5b7e4fb2 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -152,6 +152,8 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p5_id.get()], false); + std::cout << "here\n"; + std::cout << "Size First: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { std::cout << "First: " << node1.get_simulation().get_model().get_person_buffer()[i] << "\n"; From c3c92c53a6c3d9f0054089ed273b70d732e5c97c Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 14:25:16 +0100 Subject: [PATCH 087/111] test --- cpp/tests/test_graph_abm.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 0d5b7e4fb2..0923f04fd8 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -152,7 +152,11 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p5_id.get()], false); - std::cout << "here\n"; + std::cout << "p2 loc type " << static_cast(p2.get_location_type()) << "\n"; + std::cout << "p2 loc model id " << static_cast(p2.get_location_model_id()) << "\n"; + std::cout << "p2 assigned work model id " + << static_cast(p2.get_assigned_location_model_id(p2.get_location_type())) << "\n"; + std::cout << "p2 get go to work time " << p2.get_go_to_work_time(model1.parameters).hours() << "\n"; std::cout << "Size First: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { From c7e60a8aec07df964c631364fb8f7a229cb77704 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 14:41:02 +0100 Subject: [PATCH 088/111] ci --- cpp/tests/test_graph_abm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 0923f04fd8..be3e297b79 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -156,7 +156,8 @@ TEST(TestGraphAbm, test_apply_mobility) std::cout << "p2 loc model id " << static_cast(p2.get_location_model_id()) << "\n"; std::cout << "p2 assigned work model id " << static_cast(p2.get_assigned_location_model_id(p2.get_location_type())) << "\n"; - std::cout << "p2 get go to work time " << p2.get_go_to_work_time(model1.parameters).hours() << "\n"; + std::cout << "p2 get go to work time " + << p2.get_go_to_work_time(node1.get_simulation().get_model().parameters).hours() << "\n"; std::cout << "Size First: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { From f221fb374bc0acd54fa4a549ac449973f0d67a43 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 14:59:25 +0100 Subject: [PATCH 089/111] ci --- cpp/tests/test_graph_abm.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index be3e297b79..79f4bbe46b 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -158,6 +158,8 @@ TEST(TestGraphAbm, test_apply_mobility) << static_cast(p2.get_assigned_location_model_id(p2.get_location_type())) << "\n"; std::cout << "p2 get go to work time " << p2.get_go_to_work_time(node1.get_simulation().get_model().parameters).hours() << "\n"; + std::cout << "p2 unique id " << p2.get_unique_id() << "\n"; + std::cout << "p2 index " << p2.get_id().get() << "\n"; std::cout << "Size First: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { From bfb7b725f57c878a88ac0ba65eeac84d6fc8f922 Mon Sep 17 00:00:00 2001 From: jubicker Date: Tue, 14 Jan 2025 15:50:07 +0100 Subject: [PATCH 090/111] fix CI --- cpp/models/graph_abm/model_wrapper.h | 189 ++++++++++++++------------- cpp/tests/test_graph_abm.cpp | 19 --- 2 files changed, 95 insertions(+), 113 deletions(-) diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/model_wrapper.h index ed4a370176..c5ff537a6e 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/model_wrapper.h @@ -94,118 +94,119 @@ class ModelWrapper : public abm::Model void perform_mobility(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(Base::m_persons.size()); - PRAGMA_OMP(parallel for) - for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { - if (Base::m_activeness_statuses[person_index]) { - Person& person = Base::m_persons[person_index]; - auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); + for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { + if (Base::m_activeness_statuses[person_index]) { + Person& person = Base::m_persons[person_index]; + auto personal_rng = PersonalRandomNumberGenerator(Base::m_rng, person); - auto try_mobility_rule = [&](auto rule) -> bool { - //run mobility rule and check if change of location can actually happen - auto target_type = rule(personal_rng, person, t, dt, parameters); - if (person.get_assigned_location_model_id(target_type) == Base::m_id) { - const Location& target_location = - Base::get_location(Base::find_location(target_type, person_index)); - const LocationId current_location = person.get_location(); - // the Person cannot move if they do not wear mask as required at targeted location - if (target_location.is_mask_required() && - !person.is_compliant(personal_rng, InterventionType::Mask)) { - return false; - } - // the Person cannot move if the capacity of targeted Location is reached - if (target_location.get_id() == current_location || - get_number_persons(target_location.get_id()) >= target_location.get_capacity().persons) { - return false; - } - // the Person cannot move if the performed TestingStrategy is positive - if (!m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { - return false; - } - // update worn mask to target location's requirements - if (target_location.is_mask_required()) { - // if the current MaskProtection level is lower than required, the Person changes mask - if (parameters.get()[person.get_mask().get_type()] < - parameters.get()[target_location.get_required_mask()]) { - person.set_mask(target_location.get_required_mask(), t); + auto try_mobility_rule = [&](auto rule) -> bool { + //run mobility rule and check if change of location can actually happen + auto target_type = rule(personal_rng, person, t, dt, parameters); + if (person.get_assigned_location_model_id(target_type) == Base::m_id) { + const Location& target_location = + Base::get_location(Base::find_location(target_type, person_index)); + const LocationId current_location = person.get_location(); + // the Person cannot move if they do not wear mask as required at targeted location + if (target_location.is_mask_required() && + !person.is_compliant(personal_rng, InterventionType::Mask)) { + return false; + } + // the Person cannot move if the capacity of targeted Location is reached + if (target_location.get_id() == current_location || + get_number_persons(target_location.get_id()) >= target_location.get_capacity().persons) { + return false; + } + // the Person cannot move if the performed TestingStrategy is positive + if (!m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + return false; } + // update worn mask to target location's requirements + if (target_location.is_mask_required()) { + // if the current MaskProtection level is lower than required, the Person changes mask + if (parameters.get()[person.get_mask().get_type()] < + parameters.get()[target_location.get_required_mask()]) { + person.set_mask(target_location.get_required_mask(), t); + } + } + else { + person.set_mask(MaskType::None, t); + } + Base::change_location(person_index, target_location.get_id()); + return true; } - else { - person.set_mask(MaskType::None, t); + else { //person moves to other world + Base::m_activeness_statuses[person_index] = false; + person.set_location(target_type, abm::LocationId::invalid_id(), + std::numeric_limits::max()); + m_person_buffer.push_back(person_index); + m_are_exposure_caches_valid = false; + m_is_local_population_cache_valid = false; + return true; } - Base::change_location(person_index, target_location.get_id()); - return true; - } - else { //person moves to other world - Base::m_activeness_statuses[person_index] = false; - person.set_location(target_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); - m_person_buffer.push_back(person_index); - m_are_exposure_caches_valid = false; - m_is_local_population_cache_valid = false; - return true; - } - }; + }; - for (auto rule : Base::m_mobility_rules) { - bool applied = try_mobility_rule(rule); - //only use one mobility rule per person - if (applied) { - break; + for (auto rule : Base::m_mobility_rules) { + bool applied = try_mobility_rule(rule); + //only use one mobility rule per person + if (applied) { + break; + } } } } - } - // check if a person makes a trip - bool weekend = t.is_weekend(); - size_t num_trips = Base::m_trip_list.num_trips(weekend); + // check if a person makes a trip + bool weekend = t.is_weekend(); + size_t num_trips = Base::m_trip_list.num_trips(weekend); - for (; Base::m_trip_list.get_current_index() < num_trips && - Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); - Base::m_trip_list.increase_index()) { - auto& trip = Base::m_trip_list.get_next_trip(weekend); - auto& person = get_person(trip.person_id); - auto person_index = Base::get_person_index(trip.person_id); - auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); - // skip the trip if the person is in quarantine or is dead - if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { - continue; - } - if (trip.destination_model_id == Base::m_id) { - auto& target_location = get_location(trip.destination); - // skip the trip if the Person wears mask as required at targeted location - if (target_location.is_mask_required() && !person.is_compliant(personal_rng, InterventionType::Mask)) { + for (; Base::m_trip_list.get_current_index() < num_trips && + Base::m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); + Base::m_trip_list.increase_index()) { + auto& trip = Base::m_trip_list.get_next_trip(weekend); + auto& person = get_person(trip.person_id); + auto person_index = Base::get_person_index(trip.person_id); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + // skip the trip if the person is in quarantine or is dead + if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { continue; } - // skip the trip if the performed TestingStrategy is positive - if (!Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { - continue; - } - // all requirements are met, move to target location - change_location(person_index, target_location.get_id(), trip.trip_mode); - // update worn mask to target location's requirements - if (target_location.is_mask_required()) { - // if the current MaskProtection level is lower than required, the Person changes mask - if (parameters.get()[person.get_mask().get_type()] < - parameters.get()[target_location.get_required_mask()]) { - person.set_mask(target_location.get_required_mask(), t); + if (trip.destination_model_id == Base::m_id) { + auto& target_location = get_location(trip.destination); + // skip the trip if the Person wears mask as required at targeted location + if (target_location.is_mask_required() && !person.is_compliant(personal_rng, InterventionType::Mask)) { + continue; + } + // skip the trip if the performed TestingStrategy is positive + if (!Base::m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) { + continue; + } + // all requirements are met, move to target location + change_location(person_index, target_location.get_id(), trip.trip_mode); + // update worn mask to target location's requirements + if (target_location.is_mask_required()) { + // if the current MaskProtection level is lower than required, the Person changes mask + if (parameters.get()[person.get_mask().get_type()] < + parameters.get()[target_location.get_required_mask()]) { + person.set_mask(target_location.get_required_mask(), t); + } + } + else { + person.set_mask(MaskType::None, t); } } - else { - person.set_mask(MaskType::None, t); + else { //person moves to other world + Base::m_activeness_statuses[person_index] = false; + person.set_location(trip.destination_type, abm::LocationId::invalid_id(), + std::numeric_limits::max()); + m_person_buffer.push_back(person_index); + m_are_exposure_caches_valid = false; + m_is_local_population_cache_valid = false; } } - else { //person moves to other world - Base::m_activeness_statuses[person_index] = false; - person.set_location(trip.destination_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); - m_person_buffer.push_back(person_index); - m_are_exposure_caches_valid = false; - m_is_local_population_cache_valid = false; + if (((t).days() < std::floor((t + dt).days()))) { + Base::m_trip_list.reset_index(); } } - if (((t).days() < std::floor((t + dt).days()))) { - Base::m_trip_list.reset_index(); - } - } std::vector m_person_buffer; ///< List with indices of persons that are subject to move to another node. }; diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 79f4bbe46b..a4202104e0 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -152,29 +152,10 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p5_id.get()], false); - std::cout << "p2 loc type " << static_cast(p2.get_location_type()) << "\n"; - std::cout << "p2 loc model id " << static_cast(p2.get_location_model_id()) << "\n"; - std::cout << "p2 assigned work model id " - << static_cast(p2.get_assigned_location_model_id(p2.get_location_type())) << "\n"; - std::cout << "p2 get go to work time " - << p2.get_go_to_work_time(node1.get_simulation().get_model().parameters).hours() << "\n"; - std::cout << "p2 unique id " << p2.get_unique_id() << "\n"; - std::cout << "p2 index " << p2.get_id().get() << "\n"; - - std::cout << "Size First: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; - for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { - std::cout << "First: " << node1.get_simulation().get_model().get_person_buffer()[i] << "\n"; - } - mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); edge.apply_mobility(node1, node3, t); - std::cout << "Size Second: " << node1.get_simulation().get_model().get_person_buffer().size() << "\n"; - for (auto i = size_t(0); i < node1.get_simulation().get_model().get_person_buffer().size(); ++i) { - std::cout << "Second: " << node1.get_simulation().get_model().get_person_buffer()[i] << "\n"; - } - EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 2); EXPECT_EQ(node3.get_simulation().get_model().get_persons().size(), 1); From ad4a5fd4d56c3747d0c1c563f7623f26731cce7d Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 15 Jan 2025 07:58:47 +0100 Subject: [PATCH 091/111] Rename model_wrapper to graph_abmodel --- cpp/examples/graph_abm.cpp | 8 ++++---- cpp/models/graph_abm/CMakeLists.txt | 4 ++-- cpp/models/graph_abm/graph_abm_mobility.h | 4 ++-- .../{model_wrapper.cpp => graph_abmodel.cpp} | 4 ++-- .../graph_abm/{model_wrapper.h => graph_abmodel.h} | 10 +++++----- cpp/tests/test_graph_abm.cpp | 14 +++++++------- 6 files changed, 22 insertions(+), 22 deletions(-) rename cpp/models/graph_abm/{model_wrapper.cpp => graph_abmodel.cpp} (92%) rename cpp/models/graph_abm/{model_wrapper.h => graph_abmodel.h} (98%) diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index ef94488041..3083e5107d 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -25,7 +25,7 @@ #include "abm/time.h" #include "abm/person_id.h" #include "graph_abm/graph_abm_mobility.h" -#include "graph_abm/model_wrapper.h" +#include "graph_abm/graph_abmodel.h" #include "memilio/io/history.h" #include "memilio/mobility/graph.h" #include @@ -45,7 +45,7 @@ struct Logger : mio::LogAlways { */ using Type = std::vector>>; - static Type log(const mio::abm::Simulation& sim) + static Type log(const mio::abm::Simulation& sim) { Type location_information{}; location_information.reserve(size_t(mio::abm::LocationType::Count)); @@ -73,7 +73,7 @@ int main() const auto age_group_adults = mio::AgeGroup(1); const auto age_group_seniors = mio::AgeGroup(2); - auto model1 = mio::ModelWrapper(num_age_groups, 0); + auto model1 = mio::GraphABModel(num_age_groups, 0); //Set infection parameters model1.parameters.get() = 4.; @@ -131,7 +131,7 @@ int main() add_household_group_to_model(model1, single_hh_group_m1); add_household_group_to_model(model1, family_hh_group_m1); - auto model2 = mio::ModelWrapper(num_age_groups, 1); + auto model2 = mio::GraphABModel(num_age_groups, 1); //Set infection parameters model2.parameters.get() = 4.; diff --git a/cpp/models/graph_abm/CMakeLists.txt b/cpp/models/graph_abm/CMakeLists.txt index 571b774afb..982b3512c3 100644 --- a/cpp/models/graph_abm/CMakeLists.txt +++ b/cpp/models/graph_abm/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(graph_abm - model_wrapper.h - model_wrapper.cpp + graph_abmodel.h + graph_abmodel.cpp graph_abm_mobility.cpp graph_abm_mobility.h ) diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 9af1247e83..e2bf3ce541 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -28,7 +28,7 @@ #include "abm/person.h" #include "abm/person_id.h" #include "abm/model_functions.h" -#include "graph_abm/model_wrapper.h" +#include "graph_abm/graph_abmodel.h" #include "memilio/mobility/graph_simulation.h" #include "memilio/mobility/graph.h" #include "memilio/utils/compiler_diagnostics.h" @@ -48,7 +48,7 @@ class ABMSimulationNode { public: - using Sim = abm::Simulation; + using Sim = abm::Simulation; template ::value, void>> ABMSimulationNode(std::tuple&& history, Args&&... args) diff --git a/cpp/models/graph_abm/model_wrapper.cpp b/cpp/models/graph_abm/graph_abmodel.cpp similarity index 92% rename from cpp/models/graph_abm/model_wrapper.cpp rename to cpp/models/graph_abm/graph_abmodel.cpp index 65858c2f63..1f610d94c4 100644 --- a/cpp/models/graph_abm/model_wrapper.cpp +++ b/cpp/models/graph_abm/graph_abmodel.cpp @@ -18,9 +18,9 @@ * limitations under the License. */ -#include "graph_abm/model_wrapper.h" +#include "graph_abm/graph_abmodel.h" namespace mio { -} //namespace mio \ No newline at end of file +} //namespace mio diff --git a/cpp/models/graph_abm/model_wrapper.h b/cpp/models/graph_abm/graph_abmodel.h similarity index 98% rename from cpp/models/graph_abm/model_wrapper.h rename to cpp/models/graph_abm/graph_abmodel.h index c5ff537a6e..682894e21d 100644 --- a/cpp/models/graph_abm/model_wrapper.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -18,8 +18,8 @@ * limitations under the License. */ -#ifndef MIO_ABM_MODEL_WRAPPER_H -#define MIO_ABM_MODEL_WRAPPER_H +#ifndef MIO_ABM_GRAPH_ABMODEL_H +#define MIO_ABM_GRAPH_ABMODEL_H #include "abm/location_type.h" #include "abm/model.h" @@ -38,12 +38,12 @@ namespace mio { using namespace abm; -class ModelWrapper : public abm::Model +class GraphABModel : public abm::Model { using Base = Model; public: - ModelWrapper(size_t num_agegroups, int id, + GraphABModel(size_t num_agegroups, int id, std::vector mobility_rules = std::vector{&get_buried, &return_home_when_recovered, &go_to_hospital, &go_to_icu, &go_to_school, &go_to_work, &go_to_shop, @@ -212,4 +212,4 @@ class ModelWrapper : public abm::Model }; } // namespace mio -#endif //MIO_ABM_MODEL_WRAPPER_H +#endif //MIO_ABM_GRAPH_ABMODEL_H diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index a4202104e0..0a3b7df8ef 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -23,7 +23,7 @@ #include "abm/mobility_data.h" #include "abm/model.h" #include "abm/parameters.h" -#include "graph_abm/model_wrapper.h" +#include "graph_abm/graph_abmodel.h" #include "abm/location_type.h" #include "abm/time.h" #include "graph_abm/graph_abm_mobility.h" @@ -50,7 +50,7 @@ TEST(TestGraphAbm, test_evolve_node) { auto t = mio::abm::TimePoint(0); auto dt = mio::abm::hours(10); - auto model = mio::ModelWrapper(size_t(1), 1); + auto model = mio::GraphABModel(size_t(1), 1); model.parameters.get()[mio::AgeGroup(0)] = true; auto home_id = model.add_location(mio::abm::LocationType::Home); auto& home = model.get_location(home_id); @@ -73,11 +73,11 @@ TEST(TestGraphAbm, test_evolve_node) TEST(TestGraphAbm, test_apply_mobility) { auto model1 = - mio::ModelWrapper(size_t(2), 1, std::vector{&mio::abm::go_to_work}); + mio::GraphABModel(size_t(2), 1, std::vector{&mio::abm::go_to_work}); auto model2 = - mio::ModelWrapper(size_t(2), 2, std::vector{&mio::abm::go_to_work}); + mio::GraphABModel(size_t(2), 2, std::vector{&mio::abm::go_to_work}); auto model3 = - mio::ModelWrapper(size_t(2), 3, std::vector{&mio::abm::go_to_work}); + mio::GraphABModel(size_t(2), 3, std::vector{&mio::abm::go_to_work}); model1.parameters.get()[mio::AgeGroup(0)] = true; model2.parameters.get()[mio::AgeGroup(0)] = true; model3.parameters.get()[mio::AgeGroup(0)] = true; @@ -164,8 +164,8 @@ TEST(TestGraphAbm, test_apply_mobility) TEST(TestGraphABM, test_graph_simulation) { - auto model1 = mio::ModelWrapper(size_t(1), 1); - auto model2 = mio::ModelWrapper(size_t(1), 2); + auto model1 = mio::GraphABModel(size_t(1), 1); + auto model2 = mio::GraphABModel(size_t(1), 2); mio::abm::TimePoint t0 = mio::abm::TimePoint(0); mio::abm::TimePoint tmax = t0 + mio::abm::days(5); From dd58d6807a148c07cc3f85ee962a35dc6aa17f1a Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 15 Jan 2025 08:12:04 +0100 Subject: [PATCH 092/111] rename evolve to advance in graph --- cpp/memilio/mobility/README.md | 2 +- .../mobility/metapopulation_mobility_instant.h | 16 ++++++++-------- .../metapopulation_mobility_stochastic.h | 4 ++-- cpp/models/graph_abm/graph_abm_mobility.h | 12 ++++++------ cpp/tests/test_analyze_result.cpp | 2 +- cpp/tests/test_dynamic_npis.cpp | 12 ++++++------ cpp/tests/test_graph_abm.cpp | 10 +++++----- cpp/tests/test_mobility.cpp | 14 +++++++------- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/cpp/memilio/mobility/README.md b/cpp/memilio/mobility/README.md index 043854e4fd..9878cc60b5 100644 --- a/cpp/memilio/mobility/README.md +++ b/cpp/memilio/mobility/README.md @@ -3,7 +3,7 @@ This directory contains a module to loosely couple multiple simulation instances and model the mobility between them. Each instance can e.g. represent a different geographical region. The regions are stored as nodes in a graph, with edges between them representing the mobility between the regions. Currently, only compartment models are supported as nodes, but it will be extended for any model. At each time step, the simulation executes two following phases: -1. Evolve the simulation for each node independently +1. Advance the simulation for each node independently 2. Exchange people between nodes along the edges. The number of people exchanged depends on coefficients. The coefficient `a_i` of edge `e_xy` represents the percentage of people in compartment `i` moving from node `x` to node `y`. Like the contact matrices used in compartment models, the coefficients may contain dampings that change their value over time. During the next time step, the exchanged population will stay at their destination and participate in the evolution of that simulation. Afterwards, the people return. The total number of people returning is the same as the number that left. But the number of people in each compartment is adjusted according to the epidemiological situation in the destination node, e.g. some susceptible people that went from one node to another will have been exposed, so they return in a different compartment. See the [mobility header](metapopulation_mobility_instant.h) and the `MobilityEdge` and `SimulationNode` classes for technical details of the two phases. diff --git a/cpp/memilio/mobility/metapopulation_mobility_instant.h b/cpp/memilio/mobility/metapopulation_mobility_instant.h index d03be6c168..9e938723e9 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_instant.h +++ b/cpp/memilio/mobility/metapopulation_mobility_instant.h @@ -92,7 +92,7 @@ class SimulationNode return m_t0; } - void evolve(double t, double dt) + void advance(double t, double dt) { m_simulation.advance(t + dt); m_last_state = m_simulation.get_result().get_last_value(); @@ -535,8 +535,8 @@ auto get_mobility_factors(const SimulationNode& node, double t, const Eigen * detect a get_mobility_factors function for the Model type. */ template -using test_commuters_expr_t = decltype( - test_commuters(std::declval(), std::declval&>(), std::declval())); +using test_commuters_expr_t = decltype(test_commuters( + std::declval(), std::declval&>(), std::declval())); /** * Test persons when moving from their source node. @@ -642,12 +642,12 @@ void MobilityEdge::apply_mobility(FP t, FP dt, SimulationNode& node_fro /** * edge functor for mobility-based simulation. - * @see SimulationNode::evolve + * @see SimulationNode::advance */ template -void evolve_model(double t, double dt, SimulationNode& node) +void advance_model(double t, double dt, SimulationNode& node) { - node.evolve(t, dt); + node.advance(t, dt); } /** @@ -677,7 +677,7 @@ GraphSimulation, MobilityEdge>, FP, FP, void (*)(double, double, mio::SimulationNode&)> make_mobility_sim(FP t0, FP dt, const Graph, MobilityEdge>& graph) { - return make_graph_sim(t0, dt, graph, static_cast&)>(&evolve_model), + return make_graph_sim(t0, dt, graph, static_cast&)>(&advance_model), static_cast&, SimulationNode&, SimulationNode&)>( &apply_mobility)); } @@ -689,7 +689,7 @@ GraphSimulation, MobilityEdge>, FP, FP, make_mobility_sim(FP t0, FP dt, Graph, MobilityEdge>&& graph) { return make_graph_sim(t0, dt, std::move(graph), - static_cast&)>(&evolve_model), + static_cast&)>(&advance_model), static_cast&, SimulationNode&, SimulationNode&)>( &apply_mobility)); } diff --git a/cpp/memilio/mobility/metapopulation_mobility_stochastic.h b/cpp/memilio/mobility/metapopulation_mobility_stochastic.h index 320a59ef15..14433f138a 100644 --- a/cpp/memilio/mobility/metapopulation_mobility_stochastic.h +++ b/cpp/memilio/mobility/metapopulation_mobility_stochastic.h @@ -227,7 +227,7 @@ GraphSimulationStochastic, MobilityEdgeStochastic>> make_mobility_sim(double t0, double dt, const Graph, MobilityEdgeStochastic>& graph) { return make_graph_sim_stochastic( - t0, dt, graph, &evolve_model, + t0, dt, graph, &advance_model, static_cast&, SimulationNode&)>( &apply_mobility)); } @@ -237,7 +237,7 @@ GraphSimulationStochastic, MobilityEdgeStochastic>> make_mobility_sim(double t0, double dt, Graph, MobilityEdgeStochastic>&& graph) { return make_graph_sim_stochastic( - t0, dt, std::move(graph), &evolve_model, + t0, dt, std::move(graph), &advance_model, static_cast&, SimulationNode&)>( &apply_mobility)); } diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index e2bf3ce541..3721297a30 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -88,7 +88,7 @@ class ABMSimulationNode * @param[in] dt Time span that shoulb be advanced * @param[in, out] history History object(s) storing simulation information */ - void evolve(mio::abm::TimePoint t, mio::abm::TimeSpan dt) + void advance(mio::abm::TimePoint t, mio::abm::TimeSpan dt) { m_simulation.advance(t + dt, std::get<0>(m_history)); } @@ -167,15 +167,15 @@ void apply_mobility(abm::TimePoint t, abm::TimeSpan /*dt*/, ABMMobilityEdge -void evolve_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode& node) +void advance_model(abm::TimePoint t, abm::TimeSpan dt, ABMSimulationNode& node) { - node.evolve(t, dt); + node.advance(t, dt); } /** @@ -194,7 +194,7 @@ GraphSimulation, ABMMobilityEdge make_abm_graph_sim(abm::TimePoint t0, abm::TimeSpan dt, Graph, ABMMobilityEdge>&& graph) { - return make_graph_sim(t0, dt, std::move(graph), &evolve_model, &apply_mobility); + return make_graph_sim(t0, dt, std::move(graph), &advance_model, &apply_mobility); } } // namespace mio diff --git a/cpp/tests/test_analyze_result.cpp b/cpp/tests/test_analyze_result.cpp index c51d87c6af..67efe811bd 100644 --- a/cpp/tests/test_analyze_result.cpp +++ b/cpp/tests/test_analyze_result.cpp @@ -179,7 +179,7 @@ TEST(TestInterpolateGraph, basic) g.add_node(0, Model(1), 0.5); g.add_node(1, Model(1), 0.5); for (auto& n : g.nodes()) { - n.property.evolve(0.5, 4.0); + n.property.advance(0.5, 4.0); } auto interpolated = mio::interpolate_simulation_result(g); diff --git a/cpp/tests/test_dynamic_npis.cpp b/cpp/tests/test_dynamic_npis.cpp index ae32853595..2e35562a71 100644 --- a/cpp/tests/test_dynamic_npis.cpp +++ b/cpp/tests/test_dynamic_npis.cpp @@ -297,8 +297,8 @@ TEST(DynamicNPIs, mobility) EXPECT_CALL(node_from.get_simulation(), advance).Times(1).WillOnce([&](auto t) { node_from.get_simulation().result.add_time_point(t, last_state_safe); }); - node_from.evolve(3.0, 2.5); - node_to.evolve(3.0, 2.5); + node_from.advance(3.0, 2.5); + node_to.advance(3.0, 2.5); edge.apply_mobility(3.0, 2.5, node_from, node_to); EXPECT_EQ(edge.get_parameters().get_coefficients()[0].get_dampings().size(), 0); //threshold not exceeded @@ -306,8 +306,8 @@ TEST(DynamicNPIs, mobility) EXPECT_CALL(node_from.get_simulation(), advance).Times(1).WillOnce([&](auto t) { node_from.get_simulation().result.add_time_point(t, last_state_crit); }); - node_from.evolve(4.5, 1.5); - node_to.evolve(4.5, 1.5); + node_from.advance(4.5, 1.5); + node_to.advance(4.5, 1.5); edge.apply_mobility(4.5, 1.5, node_from, node_to); EXPECT_EQ(edge.get_parameters().get_coefficients()[0].get_dampings().size(), @@ -316,8 +316,8 @@ TEST(DynamicNPIs, mobility) EXPECT_CALL(node_from.get_simulation(), advance).Times(1).WillOnce([&](auto t) { node_from.get_simulation().result.add_time_point(t, last_state_crit); }); - node_from.evolve(6.0, 1.5); - node_to.evolve(6.0, 1.5); + node_from.advance(6.0, 1.5); + node_to.advance(6.0, 1.5); edge.apply_mobility(6.0, 1.5, node_from, node_to); EXPECT_EQ(edge.get_parameters().get_coefficients()[0].get_dampings().size(), 2); //NPIs implemented diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 0a3b7df8ef..f8f54375e4 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -46,7 +46,7 @@ struct MockHistory { } }; -TEST(TestGraphAbm, test_evolve_node) +TEST(TestGraphAbm, test_advance_node) { auto t = mio::abm::TimePoint(0); auto dt = mio::abm::hours(10); @@ -62,7 +62,7 @@ TEST(TestGraphAbm, test_evolve_node) p.set_assigned_location(work.get_type(), work.get_id(), 2); mio::ABMSimulationNode node(MockHistory{}, t, std::move(model)); EXPECT_EQ(node.get_simulation().get_model().get_activeness_statuses()[index], true); - node.evolve(t, dt); + node.advance(t, dt); EXPECT_EQ(node.get_simulation().get_time(), mio::abm::TimePoint(dt.seconds())); EXPECT_EQ(node.get_simulation().get_model().get_activeness_statuses()[index], false); @@ -141,9 +141,9 @@ TEST(TestGraphAbm, test_apply_mobility) mio::ABMSimulationNode node2(MockHistory{}, t, std::move(model2)); mio::ABMSimulationNode node3(MockHistory{}, t, std::move(model3)); - node1.evolve(t, dt); - node2.evolve(t, dt); - node3.evolve(t, dt); + node1.advance(t, dt); + node2.advance(t, dt); + node3.advance(t, dt); EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node3.get_simulation().get_model().get_persons().size(), 0); diff --git a/cpp/tests/test_mobility.cpp b/cpp/tests/test_mobility.cpp index 8accf2fc7e..d7d86d249c 100644 --- a/cpp/tests/test_mobility.cpp +++ b/cpp/tests/test_mobility.cpp @@ -81,7 +81,7 @@ TEST(TestMobility, compareNoMobilityWithSingleIntegration) 1e-6); } -TEST(TestMobility, nodeEvolve) +TEST(TestMobility, nodeAdvance) { using Model = mio::osecir::Model; Model model(1); @@ -100,7 +100,7 @@ TEST(TestMobility, nodeEvolve) double dt = 0.5; mio::SimulationNode> node(model, t0); - node.evolve(t0, dt); + node.advance(t0, dt); ASSERT_DOUBLE_EQ(node.get_result().get_last_time(), t0 + dt); ASSERT_EQ(print_wrap(node.get_result().get_last_value()), print_wrap(node.get_last_state())); } @@ -141,8 +141,8 @@ TEST(TestMobility, edgeApplyMobility) print_wrap((Eigen::VectorXd(10) << 990 + 99, 0, 0, 0, 10 + 1, 0, 0, 0, 0, 0).finished())); //returns - node1.evolve(t, 0.5); - node2.evolve(t, 0.5); + node1.advance(t, 0.5); + node2.advance(t, 0.5); t += 0.5; edge.apply_mobility(t, 0.5, node1, node2); auto v = node1.get_result().get_last_value(); @@ -159,8 +159,8 @@ TEST(TestMobility, edgeApplyMobility) EXPECT_DOUBLE_EQ(node2.get_result().get_last_value().sum(), 1000); //change node again - node1.evolve(t, 0.5); - node2.evolve(t, 0.5); + node1.advance(t, 0.5); + node2.advance(t, 0.5); t += 0.5; edge.apply_mobility(t, 0.5, node1, node2); EXPECT_DOUBLE_EQ(node1.get_result().get_last_value().sum(), 900); @@ -233,4 +233,4 @@ TEST(TestMobility, add_mobility_result_time_point) EXPECT_NEAR(mobility[0], 11.0, 1e-12); EXPECT_NEAR(mobility[1], 5.0, 1e-12); EXPECT_NEAR(mobility[2], 113.0, 1e-12); -} \ No newline at end of file +} From 3eaa51ae32562070ed571490439634a96fa31f82 Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 15 Jan 2025 10:10:33 +0100 Subject: [PATCH 093/111] introduce local index and global id --- cpp/examples/abm_history_object.cpp | 2 +- cpp/examples/abm_minimal.cpp | 2 +- cpp/models/abm/common_abm_loggers.h | 8 +- cpp/models/abm/model.cpp | 25 +++--- cpp/models/abm/model.h | 82 +++++++++---------- cpp/models/abm/model_functions.cpp | 4 +- cpp/models/abm/person.cpp | 29 +++---- cpp/models/abm/person.h | 48 ++++++----- cpp/models/abm/person_id.h | 38 +++++++-- cpp/models/abm/personal_rng.cpp | 7 +- cpp/models/abm/personal_rng.h | 8 +- cpp/models/abm/trip_list.h | 12 +-- cpp/models/graph_abm/graph_abm_mobility.h | 2 +- cpp/models/graph_abm/graph_abmodel.h | 11 +-- cpp/simulations/abm.cpp | 2 +- cpp/simulations/abm_braunschweig.cpp | 8 +- cpp/tests/abm_helpers.cpp | 8 +- cpp/tests/abm_helpers.h | 12 +-- cpp/tests/test_abm_infection.cpp | 9 +- cpp/tests/test_abm_model.cpp | 52 ++++++------ cpp/tests/test_abm_person.cpp | 10 +-- cpp/tests/test_abm_serialization.cpp | 2 +- cpp/tests/test_graph_abm.cpp | 4 +- .../simulation/bindings/models/abm.cpp | 7 +- 24 files changed, 209 insertions(+), 183 deletions(-) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 02cf6808f9..4e66a76720 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -150,7 +150,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto pid = person.get_id(); + const auto pid = person.get_index(); //assign shop and event model.assign_location(pid, event); model.assign_location(pid, shop); diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 7b7a21140e..b19cab087d 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -130,7 +130,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto id = person.get_id(); + const auto id = person.get_index(); //assign shop and event model.assign_location(id, event); model.assign_location(id, shop); diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 2060cf1f9b..7bb227eb88 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -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,7 +126,7 @@ struct LogPersonInformation : mio::LogOnce { person_information.reserve(sim.get_model().get_persons().size()); for (auto& person : sim.get_model().get_persons()) { person_information.push_back(std::make_tuple( - person.get_id(), sim.get_model().find_location(mio::abm::LocationType::Home, person.get_id()), + person.get_global_id(), sim.get_model().find_location(mio::abm::LocationType::Home, person.get_index()), person.get_age())); } return person_information; @@ -137,7 +137,7 @@ struct LogPersonInformation : mio::LogOnce { * @brief Logger to log mobility data of the agents in the simulation. */ struct LogDataForMobility : mio::LogAlways { - using Type = std::vector>; /** * @brief Log the mobility data of the agents in the simulation. @@ -155,7 +155,7 @@ struct LogDataForMobility : mio::LogAlways { Type mobility_data{}; for (Person p : sim.get_model().get_persons()) { mobility_data.push_back( - std::make_tuple(p.get_id(), p.get_location(), sim.get_time(), p.get_last_transport_mode(), + std::make_tuple(p.get_global_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 mobility_data; diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 759d76e01c..05ba682d89 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -50,22 +50,23 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) return id; } -PersonId Model::add_person(const LocationId id, AgeGroup age) +LocalIndex Model::add_person(const LocationId id, AgeGroup age) { - uint64_t unique_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, PersonId::invalid_id(), unique_id)); + GlobalID global_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); + return add_person( + Person(m_rng, get_location(id).get_type(), id, m_id, age, LocalIndex::invalid_index(), global_id)); } -PersonId Model::add_person(Person&& person) +LocalIndex Model::add_person(Person&& person) { 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 Model."); - assert(person.get_unique_id() != INVALID_UNIQUE_ID && "Added Person's unique id must be valid."); + assert(person.get_global_id() != GlobalID::invalid_ID() && "Added Person's unique id must be valid."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); - PersonId new_id{static_cast(m_persons.size())}; + LocalIndex new_index{static_cast(m_persons.size())}; //set correct person id aka index in m_persons vector - person.set_id(new_id); + person.set_index(new_index); person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); m_persons.emplace_back(person); m_activeness_statuses.push_back(true); @@ -73,7 +74,7 @@ PersonId Model::add_person(Person&& person) if (m_is_local_population_cache_valid) { ++m_local_population_cache[new_person.get_location().get()]; } - return new_person.get_id(); + return new_person.get_index(); } void Model::evolve(TimePoint t, TimeSpan dt) @@ -177,7 +178,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); m_trip_list.increase_index()) { auto& trip = m_trip_list.get_next_trip(weekend); - auto& person = get_person(PersonId(static_cast(trip.person_id))); + auto& person = get_person(LocalIndex(static_cast(trip.person_id.get()))); auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); // skip the trip if the person is in quarantine or is dead if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { @@ -193,7 +194,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) continue; } // all requirements are met, move to target location - change_location(person.get_id(), target_location.get_id(), trip.trip_mode); + change_location(person.get_index(), target_location.get_id(), trip.trip_mode); // update worn mask to target location's requirements if (target_location.is_mask_required()) { // if the current MaskProtection level is lower than required, the Person changes mask @@ -285,7 +286,7 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) if (person.get_location_model_id() == m_id && m_activeness_statuses[i]) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, - get_location(person.get_id()), t, dt); + get_location(person.get_index()), t, dt); } } // implicit taskloop barrier } // implicit single barrier @@ -332,7 +333,7 @@ auto Model::get_activeness_statuses() -> Range& cells = {0}) { LocationId origin = get_location(person).get_id(); @@ -469,11 +467,11 @@ class Model /** * @brief Let a person interact with the population at its current location. - * @param[in] person PersonId of a person from this Model. + * @param[in] person LocalIndex of a person from this Model. * @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) + inline void interact(LocalIndex person, TimePoint t, TimeSpan dt) { if (!m_are_exposure_caches_valid) { // checking caches is only needed for external calls @@ -510,36 +508,36 @@ class Model /** * @brief Get a reference to the location of a person. - * @param[in] id PersonId of a person. + * @param[in] index LocalIndex of a person. * @return Reference to the Location. * @{ */ - inline Location& get_location(PersonId id) + inline Location& get_location(LocalIndex index) { - return get_location(get_person(id).get_location()); + return get_location(get_person(index).get_location()); } - inline const Location& get_location(PersonId id) const + inline const Location& get_location(LocalIndex index) const { - return get_location(get_person(id).get_location()); + return get_location(get_person(index).get_location()); } /** @} */ /** * @brief Get index of person in m_persons. - * @param[in] id A person's unique id. + * @param[in] id A person's unique GlobalID. * First 32 bit are the Person's individual id and second 32 bit the Persons's home model id. * @return Index of Person in m_persons vector. * @{ */ - uint32_t get_person_index(uint64_t unique_id) const + LocalIndex get_person_index(GlobalID global_id) const { mio::log_debug("get_person_index is used leading to a search in m_persons."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [unique_id](auto& person) { - return person.get_unique_id() == unique_id; + auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { + return person.get_global_id() == global_id; }); if (it == m_persons.end()) { - log_error("Given PersonId is not in this Model."); + log_error("Given LocalIndex is not in this Model."); return std::numeric_limits::max(); } else { diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index 77338b8046..cda9c5d9f6 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -102,7 +102,7 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo const Person& person, const Location& location, const TimePoint t, const TimeSpan dt) { if (person.get_location() != location.get_id()) { - mio::log_debug("In add_exposure_contribution: Person {} is not at Location {}", person.get_id().get(), + mio::log_debug("In add_exposure_contribution: Person {} is not at Location {}", person.get_global_id().get(), location.get_id().get()); } @@ -140,7 +140,7 @@ bool change_location(Person& person, const Location& destination, const Transpor return true; } else { - mio::log_debug("In change_location: Person {} already is at Location {}", person.get_id().get(), + mio::log_debug("In change_location: Person {} already is at Location {}", person.get_global_id().get(), destination.get_id().get()); return false; } diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 87cc2a838f..6d04200ddf 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -23,6 +23,7 @@ #include "abm/parameters.h" #include "abm/infection.h" #include "abm/location.h" +#include "abm/person_id.h" #include "memilio/utils/random_number_generator.h" #include #include @@ -33,7 +34,7 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id, uint64_t unique_id) + int location_model_id, AgeGroup age, LocalIndex person_index, GlobalID global_id) : m_location(location_id) , m_location_type(location_type) , m_location_model_id(location_model_id) @@ -43,12 +44,12 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_time_at_location(0) , m_mask(Mask(MaskType::None, TimePoint(-(std::numeric_limits::max() / 2)))) , m_compliance((uint32_t)InterventionType::Count, 1.) - , m_person_id(person_id) + , m_person_index(person_index) , m_cells{0} , m_last_transport_mode(TransportMode::Unknown) , m_test_results({TestType::Count}, TestResult()) , m_assigned_location_model_ids((int)LocationType::Count) - , m_unique_id(unique_id) + , m_global_id(global_id) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); @@ -56,17 +57,17 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } -Person::Person(const Person& other, PersonId id) +Person::Person(const Person& other, LocalIndex index) : Person(other) { - m_person_id = id; + m_person_index = index; } -Person::Person(const Person& other, PersonId id, uint64_t unique_id) +Person::Person(const Person& other, LocalIndex index, GlobalID global_id) : Person(other) { - m_person_id = id; - m_unique_id = unique_id; + m_person_index = index; + m_global_id = global_id; } bool Person::is_infected(TimePoint t) const @@ -203,19 +204,19 @@ bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const T } } -PersonId Person::get_id() const +LocalIndex Person::get_index() const { - return m_person_id; + return m_person_index; } -uint64_t Person::get_unique_id() const +GlobalID Person::get_global_id() const { - return m_unique_id; + return m_global_id; } -void Person::set_id(PersonId id) +void Person::set_index(LocalIndex index) { - m_person_id = id; + m_person_index = index; } std::vector& Person::get_cells() diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 622ca98cdf..0f99e285f7 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -44,9 +44,6 @@ namespace mio namespace abm { -static constexpr uint32_t INVALID_PERSON_ID = std::numeric_limits::max(); -static constexpr uint64_t INVALID_UNIQUE_ID = std::numeric_limits::max(); - /** * @brief Agents in the simulated Model that can carry and spread the Infection. */ @@ -58,22 +55,23 @@ class Person * @param[in, out] rng RandomNumberGenerator. * @param[in, out] location Initial Location of the Person. * @param[in] age The AgeGroup of the Person. - * @param[in] person_id Index of the Person. + * @param[in] person_index Index of the Person. + * */ explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId person_id = PersonId::invalid_id(), - uint64_t unique_id = INVALID_UNIQUE_ID); + int location_model_id, AgeGroup age, LocalIndex person_index = LocalIndex::invalid_index(), + GlobalID global_id = GlobalID::invalid_ID()); - explicit Person(const Person& other, PersonId id); + explicit Person(const Person& other, LocalIndex index); - explicit Person(const Person& other, PersonId id, uint64_t unique_id); + explicit Person(const Person& other, LocalIndex index, GlobalID global_id); /** * @brief Compare two Person%s. */ bool operator==(const Person& other) const { - return (m_unique_id == other.m_unique_id); + return (m_global_id == other.m_global_id); } /** @@ -284,24 +282,24 @@ class Person bool get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const TestParameters& params); /** - * @brief Get the PersonId of the Person. - * The PersonId should correspond to the index in m_persons in the Model. - * @return The PersonId. + * @brief Get the LocalIndex of the Person. + * The LocalIndex should correspond to the index in m_persons in the Model. + * @return The LocalIndex. */ - PersonId get_id() const; + LocalIndex get_index() const; /** - * @brief Get the unique id of the Person. - * This id is only relevant for the graph abm and otherwise corresponds to the PersonId. - * @return The unique id. + * @brief Get the GlobalID of the Person. + * This ID is only relevant for the graph abm and otherwise corresponds to the LocalIndex. + * @return The GlobalID. */ - uint64_t get_unique_id() const; + GlobalID get_global_id() const; /** - * @brief Set the PersonId of the Person. - * The PersonID should correspond to the index in m_persons in model. + * @brief Set the LocalIndex of the Person. + * The LocalIndex should correspond to the index in m_persons in model. */ - void set_id(PersonId id); + void set_index(LocalIndex index); /** * @brief Get index of Cell%s of the Person. @@ -440,7 +438,7 @@ class Person .add("rnd_go_to_school_hour", m_random_goto_school_hour) .add("mask", m_mask) .add("compliance", m_compliance) - .add("id", m_person_id) + .add("index", m_person_index) .add("cells", m_cells) .add("last_transport_mode", m_last_transport_mode) .add("rng_counter", m_rng_counter) @@ -481,15 +479,15 @@ class Person Mask m_mask; ///< The Mask of the Person. std::vector m_compliance; ///< Vector of compliance values for all #InterventionType%s. Values from 0 to 1. - PersonId m_person_id; ///< Id of the Person. Corresponds to the index in m_persons in Model. + LocalIndex m_person_index; ///< LocalIndex of the Person. Corresponds to the index in m_persons in Model. 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. CustomIndexArray m_test_results; ///< CustomIndexArray for TestResults. std::vector m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. - uint64_t - m_unique_id; ///< Unique identifier of a person. Is only relevant in graph abm, otherwise is equal to m_person_id. + GlobalID + m_global_id; ///< Unique identifier of a person. Is only relevant in graph abm, otherwise is equal to m_person_index. }; } // namespace abm @@ -500,7 +498,7 @@ struct DefaultFactory { static abm::Person create() { return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::LocationId(), 0, AgeGroup(0), - abm::PersonId()); + abm::LocalIndex(), abm::GlobalID()); } }; diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index 9f65193535..af2ccab02c 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -29,24 +29,46 @@ namespace mio namespace abm { -/// Unique identifier for a Person within a Model. -struct MEMILIO_ENABLE_EBO PersonId : public mio::TypeSafe, public OperatorComparison { +/// Index for a Person within a Model. +struct MEMILIO_ENABLE_EBO LocalIndex : public mio::TypeSafe, + public OperatorComparison { + /// @brief Create an Index. + LocalIndex(uint32_t index) + : mio::TypeSafe(index) + { + } + + /// @brief Create an invalid Index. + LocalIndex() + : mio::TypeSafe(std::numeric_limits::max()) + { + } + + /// @brief Value for invalid Indices. + const static LocalIndex invalid_index() + { + return LocalIndex(); + } +}; + +/// Unique ID for a Person within a Model. +struct MEMILIO_ENABLE_EBO GlobalID : public mio::TypeSafe, public OperatorComparison { /// @brief Create an ID. - PersonId(uint32_t id) - : mio::TypeSafe(id) + GlobalID(uint64_t id) + : mio::TypeSafe(id) { } /// @brief Create an invalid ID. - PersonId() - : mio::TypeSafe(std::numeric_limits::max()) + GlobalID() + : mio::TypeSafe(std::numeric_limits::max()) { } /// @brief Value for invalid IDs. - const static PersonId invalid_id() + const static GlobalID invalid_ID() { - return PersonId(); + return GlobalID(); } }; diff --git a/cpp/models/abm/personal_rng.cpp b/cpp/models/abm/personal_rng.cpp index b6eb54f675..728a7e35f7 100644 --- a/cpp/models/abm/personal_rng.cpp +++ b/cpp/models/abm/personal_rng.cpp @@ -20,21 +20,22 @@ #include "abm/personal_rng.h" #include "abm/person.h" +#include "abm/person_id.h" namespace mio { namespace abm { -PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key key, PersonId id, +PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key key, LocalIndex index, mio::Counter& counter) : m_key(key) - , m_person_id(id) + , m_person_index(index) , m_counter(counter) { } PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) - : PersonalRandomNumberGenerator(rng.get_key(), person.get_id(), person.get_rng_counter()) + : PersonalRandomNumberGenerator(rng.get_key(), person.get_index(), person.get_rng_counter()) { } diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h index 99f1287bf0..730e5e95bf 100644 --- a/cpp/models/abm/personal_rng.h +++ b/cpp/models/abm/personal_rng.h @@ -52,10 +52,10 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase key, PersonId id, mio::Counter& counter); + PersonalRandomNumberGenerator(mio::Key key, LocalIndex index, mio::Counter& counter); /** * Creates a RandomNumberGenerator for a person. @@ -78,7 +78,7 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase get_counter() const { - return mio::rng_totalsequence_counter(static_cast(m_person_id.get()), m_counter); + return mio::rng_totalsequence_counter(static_cast(m_person_index.get()), m_counter); } /** @@ -91,7 +91,7 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase m_key; ///< Global RNG Key - PersonId m_person_id; ///< Id of the Person + LocalIndex m_person_index; ///< LocalIndex of the Person mio::Counter& m_counter; ///< Reference to the Person's rng counter }; diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index 5ba4959a39..b86a25f52f 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -41,7 +41,7 @@ namespace abm */ struct Trip { //TODO: Origin is currently not used for the trips. Should we delete it then? - uint64_t person_id; /**< Person that makes the trip and corresponds to the index into the structure m_persons from + GlobalID person_id; /**< Person that makes the trip and corresponds to the index into the structure m_persons from Model, where all Person%s are saved.*/ TimePoint time; ///< Daytime at which a Person changes the Location. LocationId destination; ///< Location where the Person changes to. @@ -64,7 +64,7 @@ struct Trip { * @param[in] origin_model_id Model the Person starts the Trip. * @param[in] input_cells The index of the Cell%s the Person changes to. */ - Trip(uint64_t id, TimePoint time_new, LocationId dest, int dest_model_id, LocationId orig, int orig_model_id, + Trip(GlobalID id, TimePoint time_new, LocationId dest, int dest_model_id, LocationId orig, int orig_model_id, TransportMode mode_of_transport, LocationType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) @@ -78,7 +78,7 @@ struct Trip { { } - Trip(uint64_t id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, + Trip(GlobalID id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, LocationType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) @@ -92,13 +92,13 @@ struct Trip { { } - Trip(uint64_t id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, + Trip(GlobalID id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, const std::vector& input_cells = {}) : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { } - Trip(uint64_t id, TimePoint time_new, LocationId dest, LocationType type_of_activity, + Trip(GlobalID id, TimePoint time_new, LocationId dest, LocationType type_of_activity, const std::vector& input_cells = {}) : Trip(id, time_new, dest, dest, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { @@ -213,7 +213,7 @@ template <> struct DefaultFactory { static abm::Trip create() { - return abm::Trip{abm::INVALID_UNIQUE_ID, abm::TimePoint{}, abm::LocationId{}, abm::LocationType{}}; + return abm::Trip{abm::GlobalID{}, abm::TimePoint{}, abm::LocationId{}, abm::LocationType{}}; } }; diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index 3721297a30..f556a4ada6 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -144,7 +144,7 @@ class ABMMobilityEdge } } //update person ids in model_from - model_from.update_person_ids(); + model_from.update_person_indices(); } }; diff --git a/cpp/models/graph_abm/graph_abmodel.h b/cpp/models/graph_abm/graph_abmodel.h index 682894e21d..1b13b9326f 100644 --- a/cpp/models/graph_abm/graph_abmodel.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -23,6 +23,7 @@ #include "abm/location_type.h" #include "abm/model.h" +#include "abm/person_id.h" #include "abm/time.h" #include "abm/location_id.h" #include "memilio/utils/compiler_diagnostics.h" @@ -80,12 +81,12 @@ class GraphABModel : public abm::Model perform_mobility(t, dt); } - void update_person_ids() + void update_person_indices() { for (uint32_t i = 0; i < static_cast(m_persons.size()); ++i) { auto& person = m_persons[i]; - if (person.get_id().get() != i) { - person.set_id(abm::PersonId{i}); + if (person.get_index().get() != i) { + person.set_index(abm::LocalIndex{i}); } } } @@ -195,10 +196,10 @@ class GraphABModel : public abm::Model } } else { //person moves to other world - Base::m_activeness_statuses[person_index] = false; + Base::m_activeness_statuses[person_index.get()] = false; person.set_location(trip.destination_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); - m_person_buffer.push_back(person_index); + m_person_buffer.push_back(person_index.get()); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; } diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index ba25076ac1..f08d592fc0 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -379,7 +379,7 @@ void create_assign_locations(mio::abm::Model& model) //Assign locations to the people auto persons = model.get_persons(); for (auto& person : persons) { - const auto id = person.get_id(); + const auto id = person.get_index(); //assign shop and event model.assign_location(id, event); counter_event++; diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index a0389f08f1..0a6330fbff 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -221,9 +221,9 @@ void create_model_from_data(mio::abm::Model& model, const std::string& filename, count_of_titles++; } - std::map locations = {}; - std::map pids_data_to_model = {}; - std::map person_ids = {}; + std::map locations = {}; + std::map pids_data_to_model = {}; + std::map person_ids = {}; std::map> locations_before; std::map> locations_after; @@ -900,7 +900,7 @@ void write_log_to_file_trip_data(const T& history) auto agent_id = std::get<0>(mobility_data[mobility_data_index][trip_index]); int start_index = mobility_data_index - 1; - using Type = std::tuple; while (!std::binary_search(std::begin(mobility_data[start_index]), std::end(mobility_data[start_index]), mobility_data[mobility_data_index][trip_index], diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index b1238c3ecc..9379c21fb8 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -24,11 +24,11 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t, - mio::abm::Parameters params, uint64_t id) + mio::abm::Parameters params, mio::abm::GlobalID id) { assert(age.get() < params.get_num_groups()); mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, - mio::abm::PersonId::invalid_id(), id); + mio::abm::LocalIndex::invalid_index(), id); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( @@ -37,8 +37,8 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc return p; } -mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, - mio::abm::InfectionState infection_state, mio::abm::TimePoint t) +mio::abm::LocalIndex add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, + mio::abm::InfectionState infection_state, mio::abm::TimePoint t) { return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, model.parameters, static_cast(model.get_persons().size()))); diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 050e8ca112..005c941b10 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -21,6 +21,7 @@ #define ABM_HELPERS_H #include "abm/model.h" +#include "abm/person_id.h" #include "gmock/gmock.h" @@ -96,15 +97,16 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc mio::AgeGroup age = age_group_15_to_34, mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0), - mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), uint64_t id = 0); + mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), + mio::abm::GlobalID id = mio::abm::GlobalID(0)); /** * @brief Add a Person to the Model. Intended for simple use in tests. */ -mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, - mio::AgeGroup age = age_group_15_to_34, - mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, - mio::abm::TimePoint t = mio::abm::TimePoint(0)); +mio::abm::LocalIndex add_test_person(mio::abm::Model& model, 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 Calls mio::abm::interact, but it computes the correct exposures for you. void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index 0c2b7a00af..39604d833d 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -20,6 +20,7 @@ #include "abm/location_type.h" #include "abm/person.h" +#include "abm/person_id.h" #include "abm_helpers.h" #include "random_number_test.h" @@ -38,7 +39,7 @@ TEST_F(TestInfection, init) //set up a personal RNG for infections //uses uniformdistribution but result doesn't matter, so init before the mock auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::PersonId(0), counter); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -112,7 +113,7 @@ TEST_F(TestInfection, init) TEST_F(TestInfection, getInfectionState) { auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::PersonId(0), counter); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); auto params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); @@ -132,7 +133,7 @@ TEST_F(TestInfection, getInfectionState) TEST_F(TestInfection, drawInfectionCourseForward) { auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::PersonId(0), counter); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); auto params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); @@ -156,7 +157,7 @@ TEST_F(TestInfection, drawInfectionCourseForward) TEST_F(TestInfection, drawInfectionCourseBackward) { auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::PersonId(0), counter); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); auto t = mio::abm::TimePoint(1); auto dt = mio::abm::days(1); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 661a2e0956..57921c033a 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -86,13 +86,13 @@ TEST_F(TestModel, addPerson) auto model = mio::abm::Model(num_age_groups); auto location = model.add_location(mio::abm::LocationType::School); - model.add_person(location, age_group_15_to_34); - model.add_person(location, age_group_35_to_59); + auto id1 = model.add_person(location, age_group_15_to_34); + auto id2 = model.add_person(location, age_group_35_to_59); // Verify the number of persons in the model and their respective age groups. EXPECT_EQ(model.get_persons().size(), 2); - EXPECT_EQ(model.get_person(0).get_age(), age_group_15_to_34); - EXPECT_EQ(model.get_person(1).get_age(), age_group_35_to_59); + EXPECT_EQ(model.get_person(id1).get_age(), age_group_15_to_34); + EXPECT_EQ(model.get_person(id2).get_age(), age_group_35_to_59); } /** @@ -356,11 +356,11 @@ TEST_F(TestModel, evolveMobilityTrips) // Set trips for persons between assigned locations. mio::abm::TripList& data = model.get_trip_list(); - mio::abm::Trip trip1(p1.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id, + mio::abm::Trip trip1(p1.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p2.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, + mio::abm::Trip trip2(p2.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip3(p5.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, + mio::abm::Trip trip3(p5.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, mio::abm::LocationType::SocialEvent); data.add_trip(trip1); data.add_trip(trip2); @@ -388,10 +388,10 @@ TEST_F(TestModel, evolveMobilityTrips) EXPECT_EQ(model.get_number_persons(hospital_id), 1); // Move all persons back to their home location to prepare for weekend trips. - model.change_location(p1.get_id(), home_id); - model.change_location(p1.get_id(), home_id); - model.change_location(p2.get_id(), home_id); - model.change_location(p5.get_id(), home_id); + model.change_location(p1.get_index(), home_id); + model.change_location(p1.get_index(), home_id); + model.change_location(p2.get_index(), home_id); + model.change_location(p5.get_index(), home_id); // Update the time to the weekend and reset the trip index. t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); @@ -411,11 +411,11 @@ TEST_F(TestModel, evolveMobilityTrips) // Add additional weekend trips for further verification. bool weekend = true; - mio::abm::Trip tripweekend1(p1.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + mio::abm::Trip tripweekend1(p1.get_global_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), event_id, work_id, mio::abm::LocationType::SocialEvent); - mio::abm::Trip tripweekend2(p2.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + mio::abm::Trip tripweekend2(p2.get_global_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), home_id, event_id, mio::abm::LocationType::Home); - mio::abm::Trip tripweekend3(p5.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), + mio::abm::Trip tripweekend3(p5.get_global_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), work_id, event_id, mio::abm::LocationType::Work); data.add_trip(tripweekend1, weekend); data.add_trip(tripweekend2, weekend); @@ -526,11 +526,11 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) // Add trip to see if a dead person can change location outside of cemetery by scheduled trips mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, + mio::abm::Trip trip1(p_dead.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p_dead.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::Trip trip2(p_dead.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, mio::abm::LocationType::Home); - mio::abm::Trip trip3(p_severe.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::Trip trip3(p_severe.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, mio::abm::LocationType::Home); trip_list.add_trip(trip1); trip_list.add_trip(trip2); @@ -538,15 +538,15 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) // Check the dead person got burried and the severely infected person starts in Hospital model.evolve(t, dt); - EXPECT_EQ(model.get_location(p_dead.get_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_dead.get_index()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Hospital); + EXPECT_EQ(model.get_location(p_severe.get_index()).get_type(), mio::abm::LocationType::Hospital); // Check the dead person is still in Cemetery and the severely infected person dies and got burried model.evolve(t + dt, dt); - EXPECT_EQ(model.get_location(p_dead.get_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_dead.get_index()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t + dt), mio::abm::InfectionState::Dead); - EXPECT_EQ(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_severe.get_index()).get_type(), mio::abm::LocationType::Cemetery); } using TestModelTestingCriteria = RandomNumberTest; @@ -926,12 +926,12 @@ TEST_F(TestModel, mobilityTripWithAppliedNPIs) // Using trip list mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_compliant_go_to_work.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p_compliant_go_to_school.get_unique_id(), t, school_id, home_id, + mio::abm::Trip trip1(p_compliant_go_to_work.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip2(p_compliant_go_to_school.get_global_id(), t, school_id, home_id, mio::abm::LocationType::School); - mio::abm::Trip trip3(p_no_mask.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip4(p_no_test.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip5(p_no_isolation.get_unique_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip3(p_no_mask.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip4(p_no_test.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip5(p_no_isolation.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); trip_list.add_trip(trip1); trip_list.add_trip(trip2); trip_list.add_trip(trip3); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index a4ef99f7e1..32225e8157 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -44,14 +44,14 @@ TEST_F(TestPerson, init) // Verify default state and location assignments. 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()); + EXPECT_EQ(person.get_index(), mio::abm::LocalIndex::invalid_index()); // Verify copy constructor - auto copied_person = mio::abm::Person(person, mio::abm::PersonId(0), 0); + auto copied_person = mio::abm::Person(person, mio::abm::LocalIndex(0), 0); EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(copied_person.get_location(), location.get_id()); - EXPECT_EQ(copied_person.get_id(), mio::abm::PersonId(0)); - EXPECT_EQ(copied_person.get_unique_id(), 0); + EXPECT_EQ(copied_person.get_index(), mio::abm::LocalIndex(0)); + EXPECT_EQ(copied_person.get_global_id(), 0); } /** @@ -359,7 +359,7 @@ TEST_F(TestPerson, getLatestProtection) TEST_F(TestPerson, rng) { auto p = mio::abm::Person(this->get_rng(), mio::abm::LocationType::Home, 0, 0, age_group_35_to_59, - mio::abm::PersonId(13)); + mio::abm::LocalIndex(13)); EXPECT_EQ(p.get_rng_counter(), mio::Counter(0)); diff --git a/cpp/tests/test_abm_serialization.cpp b/cpp/tests/test_abm_serialization.cpp index 834abc1059..0be9cf2697 100644 --- a/cpp/tests/test_abm_serialization.cpp +++ b/cpp/tests/test_abm_serialization.cpp @@ -198,7 +198,7 @@ TEST(TestAbmSerialization, Person) reference_json["assigned_locations"] = json_uint_array({i++, i++, i++, i++, i++, i++, i++, i++, i++, i++, i++}); reference_json["cells"] = json_uint_array({i++}); reference_json["compliance"] = json_double_array({(double)i++, (double)i++, (double)i++}); - reference_json["id"] = Json::UInt(i++); + reference_json["index"] = Json::UInt(i++); reference_json["infections"] = Json::Value(Json::arrayValue); reference_json["last_transport_mode"] = Json::UInt(i++); reference_json["location"] = Json::UInt(i++); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index f8f54375e4..1043f3ced7 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -125,10 +125,10 @@ TEST(TestGraphAbm, test_apply_mobility) p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); mio::abm::TripList& trips = model1.get_trip_list(); - mio::abm::Trip trip1(p3.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), + mio::abm::Trip trip1(p3.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip2(p4.get_unique_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), + mio::abm::Trip trip2(p4.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp index d14a8588e9..a46dd6ee3e 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp @@ -19,6 +19,7 @@ */ //Includes from pymio +#include "abm/person_id.h" #include "pybind_util.h" #include "utils/custom_index_array.h" #include "utils/parameter_set.h" @@ -141,9 +142,9 @@ PYBIND11_MODULE(_simulation_abm, m) .def(py::init(), py::arg("id")) .def("index", &mio::abm::LocationId::get); - pymio::bind_class(m, "PersonId") + pymio::bind_class(m, "LocalIndex") .def(py::init(), py::arg("id")) - .def("index", &mio::abm::PersonId::get); + .def("index", &mio::abm::LocalIndex::get); pymio::bind_class(m, "Person") .def("set_assigned_location", py::overload_cast( @@ -208,7 +209,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def("add_person", py::overload_cast(&mio::abm::Model::add_person), py::arg("location_id"), py::arg("age_group")) .def("assign_location", - py::overload_cast(&mio::abm::Model::assign_location), + py::overload_cast(&mio::abm::Model::assign_location), py::arg("person_id"), py::arg("location_id")) .def_property_readonly("locations", py::overload_cast<>(&mio::abm::Model::get_locations, py::const_), py::keep_alive<1, 0>{}) //keep this model alive while contents are referenced in ranges From 830aff3973ebf658cee762620d2ab575f800a454 Mon Sep 17 00:00:00 2001 From: jubicker Date: Wed, 15 Jan 2025 13:00:40 +0100 Subject: [PATCH 094/111] add tests --- cpp/tests/test_graph_abm.cpp | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 1043f3ced7..915d03e1db 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -181,3 +181,56 @@ TEST(TestGraphABM, test_graph_simulation) EXPECT_EQ(sim.get_t(), tmax); } + +TEST(TestGraphABM, mask_compliance) +{ + auto model = mio::GraphABModel(size_t(2), 0, std::vector{&mio::abm::go_to_work}); + model.parameters.get()[mio::AgeGroup(1)] = true; + model.parameters.get()[mio::AgeGroup(0)] = true; + //add home, work and school location + auto home_id = model.add_location(mio::abm::LocationType::Home); + auto work_id = model.add_location(mio::abm::LocationType::Work); + auto school_id = model.add_location(mio::abm::LocationType::School); + auto& work = model.get_location(work_id); + auto& home = model.get_location(home_id); + auto& school = model.get_location(school_id); + //school and work require FFP2 masks + school.set_required_mask(mio::abm::MaskType::FFP2); + work.set_required_mask(mio::abm::MaskType::FFP2); + auto p_id1 = model.add_person(home_id, mio::AgeGroup(1)); + auto p_id2 = model.add_person(home_id, mio::AgeGroup(0)); + auto& p1 = model.get_person(p_id1); + auto& p2 = model.get_person(p_id2); + p1.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); + p1.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + p2.set_assigned_location(school.get_type(), school.get_id(), school.get_model_id()); + p2.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); + //person is not compliant with mask + p1.set_compliance(mio::abm::InterventionType::Mask, 0.0); + p2.set_compliance(mio::abm::InterventionType::Mask, 0.0); + + //add trips for p2 + mio::abm::TripList& trips = model.get_trip_list(); + mio::abm::Trip trip1(p2.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), school_id, model.get_id(), + home_id, model.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::School); + + trips.add_trip(trip1); + + auto t0 = mio::abm::TimePoint(0); + auto dt = mio::abm::hours(12); + auto t = t0; + model.evolve(t, dt); + t += dt; + //persons should still be at home + EXPECT_EQ(p1.get_location_type(), mio::abm::LocationType::Home); + EXPECT_EQ(p2.get_location_type(), mio::abm::LocationType::Home); + //person is compliant with mask + p1.set_compliance(mio::abm::InterventionType::Mask, 1.0); + p2.set_compliance(mio::abm::InterventionType::Mask, 1.0); + model.evolve(t, dt); + t += dt; + model.evolve(t, dt); + //person should be at work and school + EXPECT_EQ(p1.get_location_type(), mio::abm::LocationType::Work); + EXPECT_EQ(p2.get_location_type(), mio::abm::LocationType::School); +} From ce3aad43f7f0da4d72064734f9ed5929ed308de8 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 16 Jan 2025 08:18:28 +0100 Subject: [PATCH 095/111] test person init --- cpp/tests/test_abm_person.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 32225e8157..2491662f03 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -46,12 +46,17 @@ TEST_F(TestPerson, init) EXPECT_EQ(person.get_location(), location.get_id()); EXPECT_EQ(person.get_index(), mio::abm::LocalIndex::invalid_index()); - // Verify copy constructor - auto copied_person = mio::abm::Person(person, mio::abm::LocalIndex(0), 0); - EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); - EXPECT_EQ(copied_person.get_location(), location.get_id()); - EXPECT_EQ(copied_person.get_index(), mio::abm::LocalIndex(0)); - EXPECT_EQ(copied_person.get_global_id(), 0); + // Verify copy constructors + auto copied_person1 = mio::abm::Person(person, mio::abm::LocalIndex(0), 0); + auto copied_person2 = mio::abm::Person(person, mio::abm::LocalIndex(1)); + EXPECT_EQ(copied_person1.get_infection_state(t), mio::abm::InfectionState::Susceptible); + EXPECT_EQ(copied_person1.get_location(), location.get_id()); + EXPECT_EQ(copied_person1.get_index(), mio::abm::LocalIndex(0)); + EXPECT_EQ(copied_person1.get_global_id(), 0); + EXPECT_EQ(copied_person2.get_infection_state(t), mio::abm::InfectionState::Susceptible); + EXPECT_EQ(copied_person2.get_location(), location.get_id()); + EXPECT_EQ(copied_person2.get_index(), mio::abm::LocalIndex(1)); + EXPECT_EQ(copied_person2.get_global_id(), mio::abm::GlobalID::invalid_ID()); } /** From ac8e4ec2138d5561dba81302aada628ed303c413 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 17 Jan 2025 18:34:01 +0100 Subject: [PATCH 096/111] remove local index from person --- cpp/examples/abm_history_object.cpp | 2 +- cpp/examples/abm_minimal.cpp | 2 +- cpp/models/abm/common_abm_loggers.h | 4 +- cpp/models/abm/model.cpp | 30 ++--- cpp/models/abm/model.h | 119 ++++++++---------- cpp/models/abm/person.cpp | 26 +--- cpp/models/abm/person.h | 38 +++--- cpp/models/abm/personal_rng.cpp | 5 +- cpp/models/abm/personal_rng.h | 8 +- cpp/models/graph_abm/graph_abm_mobility.h | 2 - cpp/models/graph_abm/graph_abmodel.h | 17 +-- cpp/simulations/abm.cpp | 2 +- cpp/simulations/abm_braunschweig.cpp | 9 +- cpp/tests/abm_helpers.cpp | 7 +- cpp/tests/abm_helpers.h | 8 +- cpp/tests/test_abm_infection.cpp | 14 +-- cpp/tests/test_abm_model.cpp | 16 +-- cpp/tests/test_abm_person.cpp | 18 +-- cpp/tests/test_abm_serialization.cpp | 1 - cpp/tests/test_graph_abm.cpp | 17 ++- .../simulation/bindings/models/abm.cpp | 6 +- 21 files changed, 150 insertions(+), 201 deletions(-) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 4e66a76720..45e9e76fa7 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -150,7 +150,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto pid = person.get_index(); + const auto pid = person.get_global_id(); //assign shop and event model.assign_location(pid, event); model.assign_location(pid, shop); diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index b19cab087d..6bf6d6cfdf 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -130,7 +130,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto id = person.get_index(); + const auto id = person.get_global_id(); //assign shop and event model.assign_location(id, event); model.assign_location(id, shop); diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 7bb227eb88..09e8fa3514 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -126,8 +126,8 @@ struct LogPersonInformation : mio::LogOnce { person_information.reserve(sim.get_model().get_persons().size()); for (auto& person : sim.get_model().get_persons()) { person_information.push_back(std::make_tuple( - person.get_global_id(), sim.get_model().find_location(mio::abm::LocationType::Home, person.get_index()), - person.get_age())); + person.get_global_id(), + sim.get_model().find_location(mio::abm::LocationType::Home, person.get_global_id()), person.get_age())); } return person_information; } diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 05ba682d89..f3e6a6cefe 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -50,23 +50,19 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) return id; } -LocalIndex Model::add_person(const LocationId id, AgeGroup age) +GlobalID Model::add_person(const LocationId id, AgeGroup age) { GlobalID global_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - return add_person( - Person(m_rng, get_location(id).get_type(), id, m_id, age, LocalIndex::invalid_index(), global_id)); + return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, global_id)); } -LocalIndex Model::add_person(Person&& person) +GlobalID Model::add_person(Person&& person) { 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 Model."); assert(person.get_global_id() != GlobalID::invalid_ID() && "Added Person's unique id must be valid."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); - LocalIndex new_index{static_cast(m_persons.size())}; - //set correct person id aka index in m_persons vector - person.set_index(new_index); person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); m_persons.emplace_back(person); m_activeness_statuses.push_back(true); @@ -74,7 +70,7 @@ LocalIndex Model::add_person(Person&& person) if (m_is_local_population_cache_valid) { ++m_local_population_cache[new_person.get_location().get()]; } - return new_person.get_index(); + return new_person.get_global_id(); } void Model::evolve(TimePoint t, TimeSpan dt) @@ -101,16 +97,16 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { - if (m_activeness_statuses[person_id]) { - Person& person = m_persons[person_id]; + for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { + if (m_activeness_statuses[person_index]) { + Person& person = m_persons[person_index]; auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); auto try_mobility_rule = [&](auto rule) -> bool { // run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); if (person.get_assigned_location_model_id(target_type) == m_id) { - const Location& target_location = get_location(find_location(target_type, person_id)); + const Location& target_location = get_location(find_location(target_type, person.get_global_id())); const LocationId current_location = person.get_location(); // the Person cannot move if they do not wear mask as required at targeted location @@ -139,7 +135,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) person.set_mask(MaskType::None, t); } // all requirements are met, move to target location - change_location(person_id, target_location.get_id()); + change_location(person.get_global_id(), target_location.get_id()); return true; } return false; @@ -178,7 +174,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) m_trip_list.get_next_trip_time(weekend).seconds() < (t + dt).time_since_midnight().seconds(); m_trip_list.increase_index()) { auto& trip = m_trip_list.get_next_trip(weekend); - auto& person = get_person(LocalIndex(static_cast(trip.person_id.get()))); + auto& person = get_person(trip.person_id); auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); // skip the trip if the person is in quarantine or is dead if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { @@ -194,7 +190,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) continue; } // all requirements are met, move to target location - change_location(person.get_index(), target_location.get_id(), trip.trip_mode); + change_location(person.get_global_id(), target_location.get_id(), trip.trip_mode); // update worn mask to target location's requirements if (target_location.is_mask_required()) { // if the current MaskProtection level is lower than required, the Person changes mask @@ -286,7 +282,7 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) if (person.get_location_model_id() == m_id && m_activeness_statuses[i]) { mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, - get_location(person.get_index()), t, dt); + get_location(person.get_location()), t, dt); } } // implicit taskloop barrier } // implicit single barrier @@ -333,7 +329,7 @@ auto Model::get_activeness_statuses() -> Range(global_id.get())]; + } + else { + mio::log_warning("get_person is accessed by GlobalID which does not align with the index of the person due " + "to former removal of persons. Therefore m_persons is searched."); + auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { + return person.get_global_id() == global_id; + }); + if (it == m_persons.end()) { + log_error("Given Person is not in this Model."); + } + return *it; } - return *it; } const Person& get_person(GlobalID global_id) const { - mio::log_warning("get_person is accessed by unique id instead of LocalIndex. Therefore m_persons is searched."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { - return person.get_global_id() == global_id; - }); - if (it == m_persons.end()) { - log_error("Given Person is not in this Model."); + if (m_person_ids_equal_index) { + return m_persons[static_cast(global_id.get())]; + } + else { + mio::log_warning("get_person is accessed by GlobalID which does not align with the index of the person due " + "to former removal of persons. Therefore m_persons is searched."); + auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { + return person.get_global_id() == global_id; + }); + if (it == m_persons.end()) { + log_error("Given Person is not in this Model."); + } + return *it; } - return *it; } /** @@ -444,12 +429,12 @@ class Model // Change the Location of a Person. this requires that Location is part of this Model. /** * @brief Let a Person change to another Location. - * @param[in] person LocalIndex of a person from this Model. + * @param[in] person Id of a person from this Model. * @param[in] destination LocationId of the Location in this Model, which the Person should change to. * @param[in] mode The transport mode the person uses to change the Location. * @param[in] cells The cells within the destination the person should be in. */ - inline void change_location(LocalIndex person, LocationId destination, TransportMode mode = TransportMode::Unknown, + inline void change_location(GlobalID person, LocationId destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { LocationId origin = get_location(person).get_id(); @@ -467,11 +452,11 @@ class Model /** * @brief Let a person interact with the population at its current location. - * @param[in] person LocalIndex of a person from this Model. + * @param[in] person Id of a person from this Model. * @param[in] t Time step of the simulation. * @param[in] dt Step size of the simulation. */ - inline void interact(LocalIndex person, TimePoint t, TimeSpan dt) + inline void interact(GlobalID person, TimePoint t, TimeSpan dt) { if (!m_are_exposure_caches_valid) { // checking caches is only needed for external calls @@ -479,10 +464,11 @@ class Model 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_id().get()], - m_contact_exposure_rates_cache[get_location(person).get_id().get()], t, dt, parameters); + auto& p = get_person(person); + auto personal_rng = PersonalRandomNumberGenerator(m_rng, p); + mio::abm::interact(personal_rng, p, get_location(p.get_location()), + m_air_exposure_rates_cache[p.get_location().get()], + m_contact_exposure_rates_cache[p.get_location().get()], t, dt, parameters); } /** @@ -508,18 +494,18 @@ class Model /** * @brief Get a reference to the location of a person. - * @param[in] index LocalIndex of a person. + * @param[in] id Id of a person. * @return Reference to the Location. * @{ */ - inline Location& get_location(LocalIndex index) + inline Location& get_location(GlobalID id) { - return get_location(get_person(index).get_location()); + return get_location(get_person(id).get_location()); } - inline const Location& get_location(LocalIndex index) const + inline const Location& get_location(GlobalID id) const { - return get_location(get_person(index).get_location()); + return get_location(get_person(id).get_location()); } /** @} */ @@ -530,14 +516,14 @@ class Model * @return Index of Person in m_persons vector. * @{ */ - LocalIndex get_person_index(GlobalID global_id) const + uint32_t get_person_index(GlobalID global_id) const { mio::log_debug("get_person_index is used leading to a search in m_persons."); auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { return person.get_global_id() == global_id; }); if (it == m_persons.end()) { - log_error("Given LocalIndex is not in this Model."); + log_error("Given GlobalID is not in this Model."); return std::numeric_limits::max(); } else { @@ -595,6 +581,7 @@ class Model std::vector m_mobility_rules; ///< Rules that govern the mobility between Location%s. LocationId m_cemetery_id; // Central cemetery for all dead persons. RandomNumberGenerator m_rng; ///< Global random number generator + bool m_person_ids_equal_index; }; } // namespace abm diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 6d04200ddf..c0bbc5005b 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -34,7 +34,7 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, LocalIndex person_index, GlobalID global_id) + int location_model_id, AgeGroup age, GlobalID global_id) : m_location(location_id) , m_location_type(location_type) , m_location_model_id(location_model_id) @@ -44,12 +44,12 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_time_at_location(0) , m_mask(Mask(MaskType::None, TimePoint(-(std::numeric_limits::max() / 2)))) , m_compliance((uint32_t)InterventionType::Count, 1.) - , m_person_index(person_index) , m_cells{0} , m_last_transport_mode(TransportMode::Unknown) , m_test_results({TestType::Count}, TestResult()) , m_assigned_location_model_ids((int)LocationType::Count) , m_global_id(global_id) + , m_rng_index(static_cast(global_id.get())) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); @@ -57,17 +57,11 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } -Person::Person(const Person& other, LocalIndex index) +Person::Person(const Person& other, GlobalID global_id) : Person(other) { - m_person_index = index; -} - -Person::Person(const Person& other, LocalIndex index, GlobalID global_id) - : Person(other) -{ - m_person_index = index; - m_global_id = global_id; + m_global_id = global_id; + m_rng_index = static_cast(global_id.get()); } bool Person::is_infected(TimePoint t) const @@ -204,21 +198,11 @@ bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const T } } -LocalIndex Person::get_index() const -{ - return m_person_index; -} - GlobalID Person::get_global_id() const { return m_global_id; } -void Person::set_index(LocalIndex index) -{ - m_person_index = index; -} - std::vector& Person::get_cells() { return m_cells; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 0f99e285f7..86cdced3a6 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -59,12 +59,9 @@ class Person * */ explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, LocalIndex person_index = LocalIndex::invalid_index(), - GlobalID global_id = GlobalID::invalid_ID()); + int location_model_id, AgeGroup age, GlobalID global_id = GlobalID::invalid_ID()); - explicit Person(const Person& other, LocalIndex index); - - explicit Person(const Person& other, LocalIndex index, GlobalID global_id); + explicit Person(const Person& other, GlobalID global_id); /** * @brief Compare two Person%s. @@ -281,26 +278,12 @@ class Person */ bool get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const TestParameters& params); - /** - * @brief Get the LocalIndex of the Person. - * The LocalIndex should correspond to the index in m_persons in the Model. - * @return The LocalIndex. - */ - LocalIndex get_index() const; - /** * @brief Get the GlobalID of the Person. - * This ID is only relevant for the graph abm and otherwise corresponds to the LocalIndex. * @return The GlobalID. */ GlobalID get_global_id() const; - /** - * @brief Set the LocalIndex of the Person. - * The LocalIndex should correspond to the index in m_persons in model. - */ - void set_index(LocalIndex index); - /** * @brief Get index of Cell%s of the Person. * @return A vector of all Cell indices the Person visits at the current Location. @@ -415,6 +398,15 @@ class Person return m_rng_counter; } + /** + * @brief Get this persons index that is used for the RandomNumberGenerator. + * @see mio::abm::PersonalRandomNumberGenerator. + */ + uint32_t get_rng_index() + { + return m_rng_index; + } + /** * @brief Get the latest #ProtectionType and its initial TimePoint of the Person. */ @@ -438,7 +430,6 @@ class Person .add("rnd_go_to_school_hour", m_random_goto_school_hour) .add("mask", m_mask) .add("compliance", m_compliance) - .add("index", m_person_index) .add("cells", m_cells) .add("last_transport_mode", m_last_transport_mode) .add("rng_counter", m_rng_counter) @@ -479,15 +470,14 @@ class Person Mask m_mask; ///< The Mask of the Person. std::vector m_compliance; ///< Vector of compliance values for all #InterventionType%s. Values from 0 to 1. - LocalIndex m_person_index; ///< LocalIndex of the Person. Corresponds to the index in m_persons in Model. 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. CustomIndexArray m_test_results; ///< CustomIndexArray for TestResults. std::vector m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. - GlobalID - m_global_id; ///< Unique identifier of a person. Is only relevant in graph abm, otherwise is equal to m_person_index. + GlobalID m_global_id; ///< Unique identifier of a person. + uint32_t m_rng_index; }; } // namespace abm @@ -498,7 +488,7 @@ struct DefaultFactory { static abm::Person create() { return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::LocationId(), 0, AgeGroup(0), - abm::LocalIndex(), abm::GlobalID()); + abm::GlobalID()); } }; diff --git a/cpp/models/abm/personal_rng.cpp b/cpp/models/abm/personal_rng.cpp index 728a7e35f7..34ab827992 100644 --- a/cpp/models/abm/personal_rng.cpp +++ b/cpp/models/abm/personal_rng.cpp @@ -21,12 +21,13 @@ #include "abm/personal_rng.h" #include "abm/person.h" #include "abm/person_id.h" +#include namespace mio { namespace abm { -PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key key, LocalIndex index, +PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key key, uint32_t index, mio::Counter& counter) : m_key(key) , m_person_index(index) @@ -35,7 +36,7 @@ PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key } PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) - : PersonalRandomNumberGenerator(rng.get_key(), person.get_index(), person.get_rng_counter()) + : PersonalRandomNumberGenerator(rng.get_key(), person.get_rng_index(), person.get_rng_counter()) { } diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h index 730e5e95bf..beae637f76 100644 --- a/cpp/models/abm/personal_rng.h +++ b/cpp/models/abm/personal_rng.h @@ -52,10 +52,10 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase key, LocalIndex index, mio::Counter& counter); + PersonalRandomNumberGenerator(mio::Key key, uint32_t index, mio::Counter& counter); /** * Creates a RandomNumberGenerator for a person. @@ -78,7 +78,7 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase get_counter() const { - return mio::rng_totalsequence_counter(static_cast(m_person_index.get()), m_counter); + return mio::rng_totalsequence_counter(m_person_index, m_counter); } /** @@ -91,7 +91,7 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase m_key; ///< Global RNG Key - LocalIndex m_person_index; ///< LocalIndex of the Person + uint32_t m_person_index; ///< Index of the Person mio::Counter& m_counter; ///< Reference to the Person's rng counter }; diff --git a/cpp/models/graph_abm/graph_abm_mobility.h b/cpp/models/graph_abm/graph_abm_mobility.h index f556a4ada6..686db87994 100644 --- a/cpp/models/graph_abm/graph_abm_mobility.h +++ b/cpp/models/graph_abm/graph_abm_mobility.h @@ -143,8 +143,6 @@ class ABMMobilityEdge persons_to_change.erase(persons_to_change.begin() + i); } } - //update person ids in model_from - model_from.update_person_indices(); } }; diff --git a/cpp/models/graph_abm/graph_abmodel.h b/cpp/models/graph_abm/graph_abmodel.h index 1b13b9326f..d9aaa24fd3 100644 --- a/cpp/models/graph_abm/graph_abmodel.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -70,6 +70,9 @@ class GraphABModel : public abm::Model { Base::m_persons.erase(Base::m_persons.begin() + pos); Base::m_activeness_statuses.erase(Base::m_activeness_statuses.begin() + pos); + if (Base::m_person_ids_equal_index) { + Base::m_person_ids_equal_index = false; + } } void evolve(TimePoint t, TimeSpan dt) @@ -81,16 +84,6 @@ class GraphABModel : public abm::Model perform_mobility(t, dt); } - void update_person_indices() - { - for (uint32_t i = 0; i < static_cast(m_persons.size()); ++i) { - auto& person = m_persons[i]; - if (person.get_index().get() != i) { - person.set_index(abm::LocalIndex{i}); - } - } - } - private: void perform_mobility(TimePoint t, TimeSpan dt) { @@ -196,10 +189,10 @@ class GraphABModel : public abm::Model } } else { //person moves to other world - Base::m_activeness_statuses[person_index.get()] = false; + Base::m_activeness_statuses[person_index] = false; person.set_location(trip.destination_type, abm::LocationId::invalid_id(), std::numeric_limits::max()); - m_person_buffer.push_back(person_index.get()); + m_person_buffer.push_back(person_index); m_are_exposure_caches_valid = false; m_is_local_population_cache_valid = false; } diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index f08d592fc0..2a016bf7ef 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -379,7 +379,7 @@ void create_assign_locations(mio::abm::Model& model) //Assign locations to the people auto persons = model.get_persons(); for (auto& person : persons) { - const auto id = person.get_index(); + const auto id = person.get_global_id(); //assign shop and event model.assign_location(id, event); counter_event++; diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index 0a6330fbff..eac3e91e5a 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -22,6 +22,7 @@ #include "abm/lockdown_rules.h" #include "abm/parameters.h" #include "abm/person.h" +#include "abm/person_id.h" #include "abm/simulation.h" #include "abm/model.h" #include "memilio/epidemiology/age_group.h" @@ -221,9 +222,9 @@ void create_model_from_data(mio::abm::Model& model, const std::string& filename, count_of_titles++; } - std::map locations = {}; - std::map pids_data_to_model = {}; - std::map person_ids = {}; + std::map locations = {}; + std::map pids_data_to_model = {}; + std::map person_ids = {}; std::map> locations_before; std::map> locations_after; @@ -344,7 +345,7 @@ void create_model_from_data(mio::abm::Model& model, const std::string& filename, split_line(line, &row); line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - uint32_t person_data_id = row[index["puid"]]; + uint64_t person_data_id = row[index["puid"]]; if (person_ids.find(person_data_id) == person_ids.end()) break; diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 9379c21fb8..5aa30ad6e4 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -27,8 +27,7 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc mio::abm::Parameters params, mio::abm::GlobalID id) { assert(age.get() < params.get_num_groups()); - mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, - mio::abm::LocalIndex::invalid_index(), id); + mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, id); if (infection_state != mio::abm::InfectionState::Susceptible) { auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); p.add_new_infection( @@ -37,8 +36,8 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc return p; } -mio::abm::LocalIndex add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, - mio::abm::InfectionState infection_state, mio::abm::TimePoint t) +mio::abm::GlobalID add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, + mio::abm::InfectionState infection_state, mio::abm::TimePoint t) { return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, model.parameters, static_cast(model.get_persons().size()))); diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 005c941b10..b3c9f09952 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -103,10 +103,10 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc /** * @brief Add a Person to the Model. Intended for simple use in tests. */ -mio::abm::LocalIndex add_test_person(mio::abm::Model& model, 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::GlobalID add_test_person(mio::abm::Model& model, 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 Calls mio::abm::interact, but it computes the correct exposures for you. void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index 39604d833d..efc02e8609 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -39,7 +39,7 @@ TEST_F(TestInfection, init) //set up a personal RNG for infections //uses uniformdistribution but result doesn't matter, so init before the mock auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), 0, counter); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -113,7 +113,7 @@ TEST_F(TestInfection, init) TEST_F(TestInfection, getInfectionState) { auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), 0, counter); auto params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); @@ -133,7 +133,7 @@ TEST_F(TestInfection, getInfectionState) TEST_F(TestInfection, drawInfectionCourseForward) { auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), 0, counter); auto params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); @@ -156,10 +156,10 @@ TEST_F(TestInfection, drawInfectionCourseForward) */ TEST_F(TestInfection, drawInfectionCourseBackward) { - auto counter = mio::Counter(0); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), mio::abm::LocalIndex(0), counter); - auto t = mio::abm::TimePoint(1); - auto dt = mio::abm::days(1); + auto counter = mio::Counter(0); + auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng().get_key(), 0, counter); + auto t = mio::abm::TimePoint(1); + auto dt = mio::abm::days(1); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); // Time to go from all infected states to recover is 1 day (dt). diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 57921c033a..660d6600e9 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -388,10 +388,10 @@ TEST_F(TestModel, evolveMobilityTrips) EXPECT_EQ(model.get_number_persons(hospital_id), 1); // Move all persons back to their home location to prepare for weekend trips. - model.change_location(p1.get_index(), home_id); - model.change_location(p1.get_index(), home_id); - model.change_location(p2.get_index(), home_id); - model.change_location(p5.get_index(), home_id); + model.change_location(p1.get_global_id(), home_id); + model.change_location(p1.get_global_id(), home_id); + model.change_location(p2.get_global_id(), home_id); + model.change_location(p5.get_global_id(), home_id); // Update the time to the weekend and reset the trip index. t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); @@ -538,15 +538,15 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) // Check the dead person got burried and the severely infected person starts in Hospital model.evolve(t, dt); - EXPECT_EQ(model.get_location(p_dead.get_index()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_dead.get_global_id()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(model.get_location(p_severe.get_index()).get_type(), mio::abm::LocationType::Hospital); + EXPECT_EQ(model.get_location(p_severe.get_global_id()).get_type(), mio::abm::LocationType::Hospital); // Check the dead person is still in Cemetery and the severely infected person dies and got burried model.evolve(t + dt, dt); - EXPECT_EQ(model.get_location(p_dead.get_index()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_dead.get_global_id()).get_type(), mio::abm::LocationType::Cemetery); EXPECT_EQ(p_severe.get_infection_state(t + dt), mio::abm::InfectionState::Dead); - EXPECT_EQ(model.get_location(p_severe.get_index()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_severe.get_global_id()).get_type(), mio::abm::LocationType::Cemetery); } using TestModelTestingCriteria = RandomNumberTest; diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 2491662f03..2bb9733eb3 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -44,19 +44,12 @@ TEST_F(TestPerson, init) // Verify default state and location assignments. EXPECT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(person.get_location(), location.get_id()); - EXPECT_EQ(person.get_index(), mio::abm::LocalIndex::invalid_index()); // Verify copy constructors - auto copied_person1 = mio::abm::Person(person, mio::abm::LocalIndex(0), 0); - auto copied_person2 = mio::abm::Person(person, mio::abm::LocalIndex(1)); - EXPECT_EQ(copied_person1.get_infection_state(t), mio::abm::InfectionState::Susceptible); - EXPECT_EQ(copied_person1.get_location(), location.get_id()); - EXPECT_EQ(copied_person1.get_index(), mio::abm::LocalIndex(0)); - EXPECT_EQ(copied_person1.get_global_id(), 0); - EXPECT_EQ(copied_person2.get_infection_state(t), mio::abm::InfectionState::Susceptible); - EXPECT_EQ(copied_person2.get_location(), location.get_id()); - EXPECT_EQ(copied_person2.get_index(), mio::abm::LocalIndex(1)); - EXPECT_EQ(copied_person2.get_global_id(), mio::abm::GlobalID::invalid_ID()); + auto copied_person = mio::abm::Person(person, 0); + EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); + EXPECT_EQ(copied_person.get_location(), location.get_id()); + EXPECT_EQ(copied_person.get_global_id(), 0); } /** @@ -363,8 +356,7 @@ TEST_F(TestPerson, getLatestProtection) */ TEST_F(TestPerson, rng) { - auto p = mio::abm::Person(this->get_rng(), mio::abm::LocationType::Home, 0, 0, age_group_35_to_59, - mio::abm::LocalIndex(13)); + auto p = mio::abm::Person(this->get_rng(), mio::abm::LocationType::Home, 0, 0, age_group_35_to_59, 13); EXPECT_EQ(p.get_rng_counter(), mio::Counter(0)); diff --git a/cpp/tests/test_abm_serialization.cpp b/cpp/tests/test_abm_serialization.cpp index 0be9cf2697..8435dbaa98 100644 --- a/cpp/tests/test_abm_serialization.cpp +++ b/cpp/tests/test_abm_serialization.cpp @@ -198,7 +198,6 @@ TEST(TestAbmSerialization, Person) reference_json["assigned_locations"] = json_uint_array({i++, i++, i++, i++, i++, i++, i++, i++, i++, i++, i++}); reference_json["cells"] = json_uint_array({i++}); reference_json["compliance"] = json_double_array({(double)i++, (double)i++, (double)i++}); - reference_json["index"] = Json::UInt(i++); reference_json["infections"] = Json::Value(Json::arrayValue); reference_json["last_transport_mode"] = Json::UInt(i++); reference_json["location"] = Json::UInt(i++); diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index 915d03e1db..de63b48cce 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -56,7 +56,7 @@ TEST(TestGraphAbm, test_advance_node) auto& home = model.get_location(home_id); auto work = mio::abm::Location(mio::abm::LocationType::Work, mio::abm::LocationId(0), size_t(1), 2); auto pid = model.add_person(home_id, mio::AgeGroup(0)); - auto index = pid.get(); + auto index = model.get_person_index(pid); auto& p = model.get_person(pid); p.set_assigned_location(home.get_type(), home.get_id(), home.get_model_id()); p.set_assigned_location(work.get_type(), work.get_id(), 2); @@ -148,9 +148,18 @@ TEST(TestGraphAbm, test_apply_mobility) EXPECT_EQ(node2.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node3.get_simulation().get_model().get_persons().size(), 0); EXPECT_EQ(node1.get_simulation().get_model().get_persons().size(), 5); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p2_id.get()], false); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p4_id.get()], false); - EXPECT_EQ(node1.get_simulation().get_model().get_activeness_statuses()[p5_id.get()], false); + EXPECT_EQ(node1.get_simulation() + .get_model() + .get_activeness_statuses()[node1.get_simulation().get_model().get_person_index(p2_id)], + false); + EXPECT_EQ(node1.get_simulation() + .get_model() + .get_activeness_statuses()[node1.get_simulation().get_model().get_person_index(p4_id)], + false); + EXPECT_EQ(node1.get_simulation() + .get_model() + .get_activeness_statuses()[node1.get_simulation().get_model().get_person_index(p5_id)], + false); mio::ABMMobilityEdge edge; edge.apply_mobility(node1, node2, t); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp index a46dd6ee3e..70cea07144 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp @@ -142,9 +142,9 @@ PYBIND11_MODULE(_simulation_abm, m) .def(py::init(), py::arg("id")) .def("index", &mio::abm::LocationId::get); - pymio::bind_class(m, "LocalIndex") + pymio::bind_class(m, "GlobalID") .def(py::init(), py::arg("id")) - .def("index", &mio::abm::LocalIndex::get); + .def("index", &mio::abm::GlobalID::get); pymio::bind_class(m, "Person") .def("set_assigned_location", py::overload_cast( @@ -209,7 +209,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def("add_person", py::overload_cast(&mio::abm::Model::add_person), py::arg("location_id"), py::arg("age_group")) .def("assign_location", - py::overload_cast(&mio::abm::Model::assign_location), + py::overload_cast(&mio::abm::Model::assign_location), py::arg("person_id"), py::arg("location_id")) .def_property_readonly("locations", py::overload_cast<>(&mio::abm::Model::get_locations, py::const_), py::keep_alive<1, 0>{}) //keep this model alive while contents are referenced in ranges From 5a57215132e6efb6d4d7ac7b6136065c9b9e488a Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 20 Jan 2025 08:11:58 +0100 Subject: [PATCH 097/111] fix msvc problems --- cpp/simulations/abm_braunschweig.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index eac3e91e5a..866dcae374 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -222,11 +222,11 @@ void create_model_from_data(mio::abm::Model& model, const std::string& filename, count_of_titles++; } - std::map locations = {}; - std::map pids_data_to_model = {}; - std::map person_ids = {}; - std::map> locations_before; - std::map> locations_after; + std::map locations = {}; + std::map pids_data_to_model = {}; + std::map person_ids = {}; + std::map> locations_before; + std::map> locations_after; // For the model we need: Hospitals, ICUs (for both we just create one for now), Homes for each unique householdID, One Person for each person_id with respective age and home_id. From ccb157d9cb2b68397d90e0093eb926308b2f99e1 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 20 Jan 2025 09:58:02 +0100 Subject: [PATCH 098/111] fix graph ABM example --- cpp/models/abm/model.cpp | 16 ++-- cpp/models/abm/model.h | 110 +++++++++++++++++++++------ cpp/models/graph_abm/graph_abmodel.h | 7 +- 3 files changed, 97 insertions(+), 36 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index f3e6a6cefe..6b234c085c 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -86,9 +86,9 @@ void Model::interaction(TimePoint t, TimeSpan dt) { const uint32_t num_persons = static_cast(m_persons.size()); PRAGMA_OMP(parallel for) - for (uint32_t person_id = 0; person_id < num_persons; ++person_id) { - if (m_activeness_statuses[person_id]) { - interact(person_id, t, dt); + for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { + if (m_activeness_statuses[person_index]) { + interact(m_persons[person_index], t, dt); } } } @@ -106,7 +106,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) // run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); if (person.get_assigned_location_model_id(target_type) == m_id) { - const Location& target_location = get_location(find_location(target_type, person.get_global_id())); + const Location& target_location = get_location(find_location(target_type, person)); const LocationId current_location = person.get_location(); // the Person cannot move if they do not wear mask as required at targeted location @@ -135,7 +135,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) person.set_mask(MaskType::None, t); } // all requirements are met, move to target location - change_location(person.get_global_id(), target_location.get_id()); + change_location(person, target_location.get_id()); return true; } return false; @@ -190,7 +190,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) continue; } // all requirements are met, move to target location - change_location(person.get_global_id(), target_location.get_id(), trip.trip_mode); + change_location(person, target_location.get_id(), trip.trip_mode); // update worn mask to target location's requirements if (target_location.is_mask_required()) { // if the current MaskProtection level is lower than required, the Person changes mask @@ -331,9 +331,7 @@ auto Model::get_activeness_statuses() -> Range& cells = {0}) { - LocationId origin = get_location(person).get_id(); - const bool has_changed_location = - mio::abm::change_location(get_person(person), get_location(destination), mode, cells); - // if the person has changed location, invalidate exposure caches but keep population caches valid - if (has_changed_location) { - m_are_exposure_caches_valid = false; - if (m_is_local_population_cache_valid) { - --m_local_population_cache[origin.get()]; - ++m_local_population_cache[destination.get()]; - } - } + change_location(get_person(person), destination, mode, cells); } /** @@ -458,17 +448,7 @@ class Model */ inline void interact(GlobalID person, TimePoint t, TimeSpan dt) { - 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_are_exposure_caches_valid = true; - } - auto& p = get_person(person); - auto personal_rng = PersonalRandomNumberGenerator(m_rng, p); - mio::abm::interact(personal_rng, p, get_location(p.get_location()), - m_air_exposure_rates_cache[p.get_location().get()], - m_contact_exposure_rates_cache[p.get_location().get()], t, dt, parameters); + interact(get_person(person), t, dt); } /** @@ -484,6 +464,18 @@ class Model return m_locations[id.get()]; } + /** + * @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 reference to the Person the location will be assigned to. + * @param[in] location The LocationId of the Location. + */ + void assign_location(Person& person, LocationId location) + { + person.set_assigned_location(get_location(location).get_type(), location, m_id); + } + Location& get_location(LocationId id) { assert(id != LocationId::invalid_id() && "Given LocationId must be valid."); @@ -558,6 +550,78 @@ class Model */ void compute_exposure_caches(TimePoint t, TimeSpan dt); + // Change the Location of a Person. this requires that Location is part of this Model. + /** + * @brief Let a Person change to another Location. + * @param[in] person Reference to Person. + * @param[in] destination LocationId of the Location in this Model, which the Person should change to. + * @param[in] mode The transport mode the person uses to change the Location. + * @param[in] cells The cells within the destination the person should be in. + */ + inline void change_location(Person& person, LocationId destination, TransportMode mode = TransportMode::Unknown, + const std::vector& cells = {0}) + { + LocationId origin = get_location(person).get_id(); + const bool has_changed_location = mio::abm::change_location(person, get_location(destination), mode, cells); + // if the person has changed location, invalidate exposure caches but keep population caches valid + if (has_changed_location) { + m_are_exposure_caches_valid = false; + if (m_is_local_population_cache_valid) { + --m_local_population_cache[origin.get()]; + ++m_local_population_cache[destination.get()]; + } + } + } + + /** + * @brief Get a reference to the location of a person. + * @param[in] person Reference to a Person. + * @return Reference to the Location. + * @{ + */ + inline Location& get_location(Person& person) + { + return get_location(person.get_location()); + } + + inline const Location& get_location(Person& person) const + { + return get_location(person.get_location()); + } + + /** + * @brief Find an assigned Location of a Person. + * @param[in] type The #LocationType that specifies the assigned Location. + * @param[in] person Reference to Person. + * @return ID of the Location of LocationType type assigend to person. + */ + LocationId find_location(LocationType type, const Person& person) const + { + auto location_id = person.get_assigned_location(type); + assert(location_id != LocationId::invalid_id() && "The person has no assigned location of that type."); + return location_id; + } + + /** + * @brief Let a person interact with the population at its current location. + * @param[in] person Reference to Person. + * @param[in] t Time step of the simulation. + * @param[in] dt Step size of the simulation. + */ + inline void interact(Person& person, TimePoint t, TimeSpan dt) + { + 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_are_exposure_caches_valid = true; + } + auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + mio::abm::interact(personal_rng, person, get_location(person.get_location()), + m_air_exposure_rates_cache[person.get_location().get()], + m_contact_exposure_rates_cache[person.get_location().get()], t, dt, parameters); + } + mutable Eigen::Matrix m_local_population_cache; ///< Current number of Persons in a given location. Eigen::Matrix diff --git a/cpp/models/graph_abm/graph_abmodel.h b/cpp/models/graph_abm/graph_abmodel.h index d9aaa24fd3..c22175a89b 100644 --- a/cpp/models/graph_abm/graph_abmodel.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -97,8 +97,7 @@ class GraphABModel : public abm::Model //run mobility rule and check if change of location can actually happen auto target_type = rule(personal_rng, person, t, dt, parameters); if (person.get_assigned_location_model_id(target_type) == Base::m_id) { - const Location& target_location = - Base::get_location(Base::find_location(target_type, person_index)); + const Location& target_location = Base::get_location(Base::find_location(target_type, person)); const LocationId current_location = person.get_location(); // the Person cannot move if they do not wear mask as required at targeted location if (target_location.is_mask_required() && @@ -125,7 +124,7 @@ class GraphABModel : public abm::Model else { person.set_mask(MaskType::None, t); } - Base::change_location(person_index, target_location.get_id()); + Base::change_location(person, target_location.get_id()); return true; } else { //person moves to other world @@ -175,7 +174,7 @@ class GraphABModel : public abm::Model continue; } // all requirements are met, move to target location - change_location(person_index, target_location.get_id(), trip.trip_mode); + change_location(person, target_location.get_id(), trip.trip_mode); // update worn mask to target location's requirements if (target_location.is_mask_required()) { // if the current MaskProtection level is lower than required, the Person changes mask From db66cac49b9272d35d14d462eed7b3170469a248 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 20 Jan 2025 13:26:55 +0100 Subject: [PATCH 099/111] rename GlobalID to PersonId --- cpp/examples/abm_history_object.cpp | 2 +- cpp/examples/abm_minimal.cpp | 2 +- cpp/models/abm/common_abm_loggers.h | 10 ++-- cpp/models/abm/model.cpp | 12 ++--- cpp/models/abm/model.h | 38 +++++++------- cpp/models/abm/model_functions.cpp | 4 +- cpp/models/abm/person.cpp | 6 +-- cpp/models/abm/person.h | 14 ++--- cpp/models/abm/person_id.h | 36 +++---------- cpp/models/abm/trip_list.h | 12 ++--- cpp/simulations/abm.cpp | 2 +- cpp/simulations/abm_braunschweig.cpp | 4 +- cpp/tests/abm_helpers.cpp | 4 +- cpp/tests/abm_helpers.h | 4 +- cpp/tests/test_abm_model.cpp | 51 +++++++++---------- cpp/tests/test_abm_person.cpp | 2 +- cpp/tests/test_graph_abm.cpp | 14 +++-- .../simulation/bindings/models/abm.cpp | 6 +-- 18 files changed, 99 insertions(+), 124 deletions(-) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 45e9e76fa7..02cf6808f9 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -150,7 +150,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto pid = person.get_global_id(); + const auto pid = person.get_id(); //assign shop and event model.assign_location(pid, event); model.assign_location(pid, shop); diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 6bf6d6cfdf..7b7a21140e 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -130,7 +130,7 @@ int main() // Assign locations to the people for (auto& person : model.get_persons()) { - const auto id = person.get_global_id(); + const auto id = person.get_id(); //assign shop and event model.assign_location(id, event); model.assign_location(id, shop); diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 09e8fa3514..2060cf1f9b 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -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,8 @@ struct LogPersonInformation : mio::LogOnce { person_information.reserve(sim.get_model().get_persons().size()); for (auto& person : sim.get_model().get_persons()) { person_information.push_back(std::make_tuple( - person.get_global_id(), - sim.get_model().find_location(mio::abm::LocationType::Home, person.get_global_id()), person.get_age())); + person.get_id(), sim.get_model().find_location(mio::abm::LocationType::Home, person.get_id()), + person.get_age())); } return person_information; } @@ -137,7 +137,7 @@ struct LogPersonInformation : mio::LogOnce { * @brief Logger to log mobility data of the agents in the simulation. */ struct LogDataForMobility : mio::LogAlways { - using Type = std::vector>; /** * @brief Log the mobility data of the agents in the simulation. @@ -155,7 +155,7 @@ struct LogDataForMobility : mio::LogAlways { Type mobility_data{}; for (Person p : sim.get_model().get_persons()) { mobility_data.push_back( - std::make_tuple(p.get_global_id(), p.get_location(), sim.get_time(), p.get_last_transport_mode(), + 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 mobility_data; diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 6b234c085c..7397478018 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -50,18 +50,18 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) return id; } -GlobalID Model::add_person(const LocationId id, AgeGroup age) +PersonId Model::add_person(const LocationId id, AgeGroup age) { - GlobalID global_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); + PersonId global_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, global_id)); } -GlobalID Model::add_person(Person&& person) +PersonId Model::add_person(Person&& person) { 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 Model."); - assert(person.get_global_id() != GlobalID::invalid_ID() && "Added Person's unique id must be valid."); + assert(person.get_id() != PersonId::invalid_ID() && "Added Person's unique id must be valid."); assert(person.get_age() < (AgeGroup)parameters.get_num_groups() && "Added Person's AgeGroup is too large."); person.set_assigned_location(LocationType::Cemetery, m_cemetery_id, m_id); m_persons.emplace_back(person); @@ -70,7 +70,7 @@ GlobalID Model::add_person(Person&& person) if (m_is_local_population_cache_valid) { ++m_local_population_cache[new_person.get_location().get()]; } - return new_person.get_global_id(); + return new_person.get_id(); } void Model::evolve(TimePoint t, TimeSpan dt) @@ -329,7 +329,7 @@ auto Model::get_activeness_statuses() -> Range(global_id.get())]; } else { - mio::log_warning("get_person is accessed by GlobalID which does not align with the index of the person due " + mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due " "to former removal of persons. Therefore m_persons is searched."); auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { - return person.get_global_id() == global_id; + return person.get_id() == global_id; }); if (it == m_persons.end()) { log_error("Given Person is not in this Model."); @@ -380,16 +380,16 @@ class Model } } - const Person& get_person(GlobalID global_id) const + const Person& get_person(PersonId global_id) const { if (m_person_ids_equal_index) { return m_persons[static_cast(global_id.get())]; } else { - mio::log_warning("get_person is accessed by GlobalID which does not align with the index of the person due " + mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due " "to former removal of persons. Therefore m_persons is searched."); auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { - return person.get_global_id() == global_id; + return person.get_id() == global_id; }); if (it == m_persons.end()) { log_error("Given Person is not in this Model."); @@ -434,7 +434,7 @@ class Model * @param[in] mode The transport mode the person uses to change the Location. * @param[in] cells The cells within the destination the person should be in. */ - inline void change_location(GlobalID person, LocationId destination, TransportMode mode = TransportMode::Unknown, + inline void change_location(PersonId person, LocationId destination, TransportMode mode = TransportMode::Unknown, const std::vector& cells = {0}) { change_location(get_person(person), destination, mode, cells); @@ -446,7 +446,7 @@ class Model * @param[in] t Time step of the simulation. * @param[in] dt Step size of the simulation. */ - inline void interact(GlobalID person, TimePoint t, TimeSpan dt) + inline void interact(PersonId person, TimePoint t, TimeSpan dt) { interact(get_person(person), t, dt); } @@ -490,12 +490,12 @@ class Model * @return Reference to the Location. * @{ */ - inline Location& get_location(GlobalID id) + inline Location& get_location(PersonId id) { return get_location(get_person(id).get_location()); } - inline const Location& get_location(GlobalID id) const + inline const Location& get_location(PersonId id) const { return get_location(get_person(id).get_location()); } @@ -503,19 +503,19 @@ class Model /** * @brief Get index of person in m_persons. - * @param[in] id A person's unique GlobalID. + * @param[in] id A person's unique PersonId. * First 32 bit are the Person's individual id and second 32 bit the Persons's home model id. * @return Index of Person in m_persons vector. * @{ */ - uint32_t get_person_index(GlobalID global_id) const + uint32_t get_person_index(PersonId global_id) const { mio::log_debug("get_person_index is used leading to a search in m_persons."); auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { - return person.get_global_id() == global_id; + return person.get_id() == global_id; }); if (it == m_persons.end()) { - log_error("Given GlobalID is not in this Model."); + log_error("Given PersonId is not in this Model."); return std::numeric_limits::max(); } else { diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index cda9c5d9f6..77338b8046 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -102,7 +102,7 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo const Person& person, const Location& location, const TimePoint t, const TimeSpan dt) { if (person.get_location() != location.get_id()) { - mio::log_debug("In add_exposure_contribution: Person {} is not at Location {}", person.get_global_id().get(), + mio::log_debug("In add_exposure_contribution: Person {} is not at Location {}", person.get_id().get(), location.get_id().get()); } @@ -140,7 +140,7 @@ bool change_location(Person& person, const Location& destination, const Transpor return true; } else { - mio::log_debug("In change_location: Person {} already is at Location {}", person.get_global_id().get(), + mio::log_debug("In change_location: Person {} already is at Location {}", person.get_id().get(), destination.get_id().get()); return false; } diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index c0bbc5005b..172820953f 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -34,7 +34,7 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, GlobalID global_id) + int location_model_id, AgeGroup age, PersonId global_id) : m_location(location_id) , m_location_type(location_type) , m_location_model_id(location_model_id) @@ -57,7 +57,7 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } -Person::Person(const Person& other, GlobalID global_id) +Person::Person(const Person& other, PersonId global_id) : Person(other) { m_global_id = global_id; @@ -198,7 +198,7 @@ bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const T } } -GlobalID Person::get_global_id() const +PersonId Person::get_id() const { return m_global_id; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 86cdced3a6..307f0ce9a2 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -59,9 +59,9 @@ class Person * */ explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, GlobalID global_id = GlobalID::invalid_ID()); + int location_model_id, AgeGroup age, PersonId global_id = PersonId::invalid_ID()); - explicit Person(const Person& other, GlobalID global_id); + explicit Person(const Person& other, PersonId global_id); /** * @brief Compare two Person%s. @@ -279,10 +279,10 @@ class Person bool get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const TestParameters& params); /** - * @brief Get the GlobalID of the Person. - * @return The GlobalID. + * @brief Get the PersonId of the Person. + * @return The PersonId. */ - GlobalID get_global_id() const; + PersonId get_id() const; /** * @brief Get index of Cell%s of the Person. @@ -476,7 +476,7 @@ class Person CustomIndexArray m_test_results; ///< CustomIndexArray for TestResults. std::vector m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. - GlobalID m_global_id; ///< Unique identifier of a person. + PersonId m_global_id; ///< Unique identifier of a person. uint32_t m_rng_index; }; @@ -488,7 +488,7 @@ struct DefaultFactory { static abm::Person create() { return abm::Person(thread_local_rng(), abm::LocationType::Count, abm::LocationId(), 0, AgeGroup(0), - abm::GlobalID()); + abm::PersonId()); } }; diff --git a/cpp/models/abm/person_id.h b/cpp/models/abm/person_id.h index af2ccab02c..0d9a46ad08 100644 --- a/cpp/models/abm/person_id.h +++ b/cpp/models/abm/person_id.h @@ -29,46 +29,24 @@ namespace mio namespace abm { -/// Index for a Person within a Model. -struct MEMILIO_ENABLE_EBO LocalIndex : public mio::TypeSafe, - public OperatorComparison { - /// @brief Create an Index. - LocalIndex(uint32_t index) - : mio::TypeSafe(index) - { - } - - /// @brief Create an invalid Index. - LocalIndex() - : mio::TypeSafe(std::numeric_limits::max()) - { - } - - /// @brief Value for invalid Indices. - const static LocalIndex invalid_index() - { - return LocalIndex(); - } -}; - /// Unique ID for a Person within a Model. -struct MEMILIO_ENABLE_EBO GlobalID : public mio::TypeSafe, public OperatorComparison { +struct MEMILIO_ENABLE_EBO PersonId : public mio::TypeSafe, public OperatorComparison { /// @brief Create an ID. - GlobalID(uint64_t id) - : mio::TypeSafe(id) + PersonId(uint64_t id) + : mio::TypeSafe(id) { } /// @brief Create an invalid ID. - GlobalID() - : mio::TypeSafe(std::numeric_limits::max()) + PersonId() + : mio::TypeSafe(std::numeric_limits::max()) { } /// @brief Value for invalid IDs. - const static GlobalID invalid_ID() + const static PersonId invalid_ID() { - return GlobalID(); + return PersonId(); } }; diff --git a/cpp/models/abm/trip_list.h b/cpp/models/abm/trip_list.h index b86a25f52f..57f35dc72e 100644 --- a/cpp/models/abm/trip_list.h +++ b/cpp/models/abm/trip_list.h @@ -41,7 +41,7 @@ namespace abm */ struct Trip { //TODO: Origin is currently not used for the trips. Should we delete it then? - GlobalID 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 Model, where all Person%s are saved.*/ TimePoint time; ///< Daytime at which a Person changes the Location. LocationId destination; ///< Location where the Person changes to. @@ -64,7 +64,7 @@ struct Trip { * @param[in] origin_model_id Model the Person starts the Trip. * @param[in] input_cells The index of the Cell%s the Person changes to. */ - Trip(GlobalID id, TimePoint time_new, LocationId dest, int dest_model_id, LocationId orig, int orig_model_id, + Trip(PersonId id, TimePoint time_new, LocationId dest, int dest_model_id, LocationId orig, int orig_model_id, TransportMode mode_of_transport, LocationType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) @@ -78,7 +78,7 @@ struct Trip { { } - Trip(GlobalID id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, TransportMode mode_of_transport, LocationType type_of_activity, const std::vector& input_cells = {}) : person_id(id) , time(mio::abm::TimePoint(time_new.time_since_midnight().seconds())) @@ -92,13 +92,13 @@ struct Trip { { } - Trip(GlobalID id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationId orig, LocationType type_of_activity, const std::vector& input_cells = {}) : Trip(id, time_new, dest, orig, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { } - Trip(GlobalID id, TimePoint time_new, LocationId dest, LocationType type_of_activity, + Trip(PersonId id, TimePoint time_new, LocationId dest, LocationType type_of_activity, const std::vector& input_cells = {}) : Trip(id, time_new, dest, dest, mio::abm::TransportMode::Unknown, type_of_activity, input_cells) { @@ -213,7 +213,7 @@ template <> struct DefaultFactory { static abm::Trip create() { - return abm::Trip{abm::GlobalID{}, abm::TimePoint{}, abm::LocationId{}, abm::LocationType{}}; + return abm::Trip{abm::PersonId{}, abm::TimePoint{}, abm::LocationId{}, abm::LocationType{}}; } }; diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 2a016bf7ef..ba25076ac1 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -379,7 +379,7 @@ void create_assign_locations(mio::abm::Model& model) //Assign locations to the people auto persons = model.get_persons(); for (auto& person : persons) { - const auto id = person.get_global_id(); + const auto id = person.get_id(); //assign shop and event model.assign_location(id, event); counter_event++; diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index 866dcae374..d60d2a5c4c 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -223,7 +223,7 @@ void create_model_from_data(mio::abm::Model& model, const std::string& filename, } std::map locations = {}; - std::map pids_data_to_model = {}; + std::map pids_data_to_model = {}; std::map person_ids = {}; std::map> locations_before; std::map> locations_after; @@ -901,7 +901,7 @@ void write_log_to_file_trip_data(const T& history) auto agent_id = std::get<0>(mobility_data[mobility_data_index][trip_index]); int start_index = mobility_data_index - 1; - using Type = std::tuple; while (!std::binary_search(std::begin(mobility_data[start_index]), std::end(mobility_data[start_index]), mobility_data[mobility_data_index][trip_index], diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 5aa30ad6e4..ef4bd4af1c 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -24,7 +24,7 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t, - mio::abm::Parameters params, mio::abm::GlobalID id) + mio::abm::Parameters params, mio::abm::PersonId id) { assert(age.get() < params.get_num_groups()); mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, id); @@ -36,7 +36,7 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc return p; } -mio::abm::GlobalID add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, +mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t) { return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index b3c9f09952..d1bbf700bd 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -98,12 +98,12 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0), mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), - mio::abm::GlobalID id = mio::abm::GlobalID(0)); + mio::abm::PersonId id = mio::abm::PersonId(0)); /** * @brief Add a Person to the Model. Intended for simple use in tests. */ -mio::abm::GlobalID add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, +mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age = age_group_15_to_34, mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, mio::abm::TimePoint t = mio::abm::TimePoint(0)); diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 660d6600e9..fd49b956a5 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -356,11 +356,11 @@ TEST_F(TestModel, evolveMobilityTrips) // Set trips for persons between assigned locations. mio::abm::TripList& data = model.get_trip_list(); - mio::abm::Trip trip1(p1.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id, + mio::abm::Trip trip1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p2.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, + mio::abm::Trip trip2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id, mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip3(p5.get_global_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, mio::abm::LocationType::SocialEvent); data.add_trip(trip1); data.add_trip(trip2); @@ -388,10 +388,10 @@ TEST_F(TestModel, evolveMobilityTrips) EXPECT_EQ(model.get_number_persons(hospital_id), 1); // Move all persons back to their home location to prepare for weekend trips. - model.change_location(p1.get_global_id(), home_id); - model.change_location(p1.get_global_id(), home_id); - model.change_location(p2.get_global_id(), home_id); - model.change_location(p5.get_global_id(), home_id); + model.change_location(p1.get_id(), home_id); + model.change_location(p1.get_id(), home_id); + model.change_location(p2.get_id(), home_id); + model.change_location(p5.get_id(), home_id); // Update the time to the weekend and reset the trip index. t = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(8); @@ -411,12 +411,12 @@ TEST_F(TestModel, evolveMobilityTrips) // Add additional weekend trips for further verification. bool weekend = true; - mio::abm::Trip tripweekend1(p1.get_global_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), - event_id, work_id, mio::abm::LocationType::SocialEvent); - mio::abm::Trip tripweekend2(p2.get_global_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), - home_id, event_id, mio::abm::LocationType::Home); - mio::abm::Trip tripweekend3(p5.get_global_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), - work_id, event_id, mio::abm::LocationType::Work); + mio::abm::Trip tripweekend1(p1.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), event_id, + work_id, mio::abm::LocationType::SocialEvent); + mio::abm::Trip tripweekend2(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), home_id, + event_id, mio::abm::LocationType::Home); + mio::abm::Trip tripweekend3(p5.get_id(), mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(10), work_id, + event_id, mio::abm::LocationType::Work); data.add_trip(tripweekend1, weekend); data.add_trip(tripweekend2, weekend); data.add_trip(tripweekend3, weekend); @@ -526,11 +526,11 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) // Add trip to see if a dead person can change location outside of cemetery by scheduled trips mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, + mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p_dead.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, mio::abm::LocationType::Home); - mio::abm::Trip trip3(p_severe.get_global_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, mio::abm::LocationType::Home); trip_list.add_trip(trip1); trip_list.add_trip(trip2); @@ -538,15 +538,15 @@ TEST_F(TestModel, checkMobilityOfDeadPerson) // Check the dead person got burried and the severely infected person starts in Hospital model.evolve(t, dt); - EXPECT_EQ(model.get_location(p_dead.get_global_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.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(model.get_location(p_severe.get_global_id()).get_type(), mio::abm::LocationType::Hospital); + EXPECT_EQ(model.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 model.evolve(t + dt, dt); - EXPECT_EQ(model.get_location(p_dead.get_global_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.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(model.get_location(p_severe.get_global_id()).get_type(), mio::abm::LocationType::Cemetery); + EXPECT_EQ(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); } using TestModelTestingCriteria = RandomNumberTest; @@ -926,12 +926,11 @@ TEST_F(TestModel, mobilityTripWithAppliedNPIs) // Using trip list mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_compliant_go_to_work.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip2(p_compliant_go_to_school.get_global_id(), t, school_id, home_id, - mio::abm::LocationType::School); - mio::abm::Trip trip3(p_no_mask.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip4(p_no_test.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); - mio::abm::Trip trip5(p_no_isolation.get_global_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip1(p_compliant_go_to_work.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip2(p_compliant_go_to_school.get_id(), t, school_id, home_id, mio::abm::LocationType::School); + mio::abm::Trip trip3(p_no_mask.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip4(p_no_test.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); + mio::abm::Trip trip5(p_no_isolation.get_id(), t, work_id, home_id, mio::abm::LocationType::Work); trip_list.add_trip(trip1); trip_list.add_trip(trip2); trip_list.add_trip(trip3); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 2bb9733eb3..74012cc964 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -49,7 +49,7 @@ TEST_F(TestPerson, init) auto copied_person = mio::abm::Person(person, 0); EXPECT_EQ(copied_person.get_infection_state(t), mio::abm::InfectionState::Susceptible); EXPECT_EQ(copied_person.get_location(), location.get_id()); - EXPECT_EQ(copied_person.get_global_id(), 0); + EXPECT_EQ(copied_person.get_id(), 0); } /** diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index de63b48cce..d0a63f6f8c 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -125,12 +125,10 @@ TEST(TestGraphAbm, test_apply_mobility) p4.set_assigned_location(event_2.get_type(), event_2.get_id(), event_2.get_model_id()); mio::abm::TripList& trips = model1.get_trip_list(); - mio::abm::Trip trip1(p3.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), - home_id, model1.get_id(), mio::abm::TransportMode::Unknown, - mio::abm::LocationType::SocialEvent); - mio::abm::Trip trip2(p4.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), - home_id, model1.get_id(), mio::abm::TransportMode::Unknown, - mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip1(p3.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_1, model1.get_id(), home_id, + model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); + mio::abm::Trip trip2(p4.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), event_id_2, model2.get_id(), home_id, + model1.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::SocialEvent); trips.add_trip(trip1); trips.add_trip(trip2); @@ -220,8 +218,8 @@ TEST(TestGraphABM, mask_compliance) //add trips for p2 mio::abm::TripList& trips = model.get_trip_list(); - mio::abm::Trip trip1(p2.get_global_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), school_id, model.get_id(), - home_id, model.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::School); + mio::abm::Trip trip1(p2.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(8), school_id, model.get_id(), home_id, + model.get_id(), mio::abm::TransportMode::Unknown, mio::abm::LocationType::School); trips.add_trip(trip1); diff --git a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp index 70cea07144..0ec4a20c78 100644 --- a/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/bindings/models/abm.cpp @@ -142,9 +142,9 @@ PYBIND11_MODULE(_simulation_abm, m) .def(py::init(), py::arg("id")) .def("index", &mio::abm::LocationId::get); - pymio::bind_class(m, "GlobalID") + pymio::bind_class(m, "PersonId") .def(py::init(), py::arg("id")) - .def("index", &mio::abm::GlobalID::get); + .def("index", &mio::abm::PersonId::get); pymio::bind_class(m, "Person") .def("set_assigned_location", py::overload_cast( @@ -209,7 +209,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def("add_person", py::overload_cast(&mio::abm::Model::add_person), py::arg("location_id"), py::arg("age_group")) .def("assign_location", - py::overload_cast(&mio::abm::Model::assign_location), + py::overload_cast(&mio::abm::Model::assign_location), py::arg("person_id"), py::arg("location_id")) .def_property_readonly("locations", py::overload_cast<>(&mio::abm::Model::get_locations, py::const_), py::keep_alive<1, 0>{}) //keep this model alive while contents are referenced in ranges From 907131323ffb57ea8271beecc1fe7fe1c8a454ce Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 20 Jan 2025 15:02:06 +0100 Subject: [PATCH 100/111] Review and CodeCov --- cpp/models/abm/model.cpp | 4 ++-- cpp/models/abm/model.h | 26 +++++++++++++------------- cpp/models/abm/person.cpp | 14 +++++++------- cpp/models/abm/person.h | 8 ++++---- cpp/models/graph_abm/graph_abmodel.h | 5 +++++ cpp/tests/test_abm_model.cpp | 2 ++ cpp/tests/test_graph_abm.cpp | 27 +++++++++++++++++++++++---- 7 files changed, 56 insertions(+), 30 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 7397478018..09896f4127 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -52,8 +52,8 @@ LocationId Model::add_location(LocationType type, uint32_t num_cells) PersonId Model::add_person(const LocationId id, AgeGroup age) { - PersonId global_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); - return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, global_id)); + PersonId person_id = (static_cast(m_id)) << 32 | static_cast(m_persons.size()); + return add_person(Person(m_rng, get_location(id).get_type(), id, m_id, age, person_id)); } PersonId Model::add_person(Person&& person) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index bc48cc2469..6718b6969b 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -359,19 +359,19 @@ class Model /** * @brief Get a reference to a Person from this Model. - * @param[in] global_id A Person's PersonId. + * @param[in] person_id A Person's PersonId. * @return A reference to the Person. */ - Person& get_person(PersonId global_id) + Person& get_person(PersonId person_id) { if (m_person_ids_equal_index) { - return m_persons[static_cast(global_id.get())]; + return m_persons[static_cast(person_id.get())]; } else { mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due " "to former removal of persons. Therefore m_persons is searched."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { - return person.get_id() == global_id; + auto it = std::find_if(m_persons.begin(), m_persons.end(), [person_id](auto& person) { + return person.get_id() == person_id; }); if (it == m_persons.end()) { log_error("Given Person is not in this Model."); @@ -380,16 +380,16 @@ class Model } } - const Person& get_person(PersonId global_id) const + const Person& get_person(PersonId person_id) const { if (m_person_ids_equal_index) { - return m_persons[static_cast(global_id.get())]; + return m_persons[static_cast(person_id.get())]; } else { mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due " "to former removal of persons. Therefore m_persons is searched."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { - return person.get_id() == global_id; + auto it = std::find_if(m_persons.begin(), m_persons.end(), [person_id](auto& person) { + return person.get_id() == person_id; }); if (it == m_persons.end()) { log_error("Given Person is not in this Model."); @@ -503,16 +503,16 @@ class Model /** * @brief Get index of person in m_persons. - * @param[in] id A person's unique PersonId. + * @param[in] person_id A person's unique PersonId. * First 32 bit are the Person's individual id and second 32 bit the Persons's home model id. * @return Index of Person in m_persons vector. * @{ */ - uint32_t get_person_index(PersonId global_id) const + uint32_t get_person_index(PersonId person_id) const { mio::log_debug("get_person_index is used leading to a search in m_persons."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [global_id](auto& person) { - return person.get_id() == global_id; + auto it = std::find_if(m_persons.begin(), m_persons.end(), [person_id](auto& person) { + return person.get_id() == person_id; }); if (it == m_persons.end()) { log_error("Given PersonId is not in this Model."); diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 172820953f..c644f38ee6 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -34,7 +34,7 @@ namespace abm { Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId global_id) + int location_model_id, AgeGroup age, PersonId person_id) : m_location(location_id) , m_location_type(location_type) , m_location_model_id(location_model_id) @@ -48,8 +48,8 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_last_transport_mode(TransportMode::Unknown) , m_test_results({TestType::Count}, TestResult()) , m_assigned_location_model_ids((int)LocationType::Count) - , m_global_id(global_id) - , m_rng_index(static_cast(global_id.get())) + , m_person_id(person_id) + , m_rng_index(static_cast(person_id.get())) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); @@ -57,11 +57,11 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } -Person::Person(const Person& other, PersonId global_id) +Person::Person(const Person& other, PersonId person_id) : Person(other) { - m_global_id = global_id; - m_rng_index = static_cast(global_id.get()); + m_person_id = person_id; + m_rng_index = static_cast(person_id.get()); } bool Person::is_infected(TimePoint t) const @@ -200,7 +200,7 @@ bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const T PersonId Person::get_id() const { - return m_global_id; + return m_person_id; } std::vector& Person::get_cells() diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 307f0ce9a2..e9f56edab6 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -59,16 +59,16 @@ class Person * */ explicit Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id, - int location_model_id, AgeGroup age, PersonId global_id = PersonId::invalid_ID()); + int location_model_id, AgeGroup age, PersonId person_id = PersonId::invalid_ID()); - explicit Person(const Person& other, PersonId global_id); + explicit Person(const Person& other, PersonId person_id); /** * @brief Compare two Person%s. */ bool operator==(const Person& other) const { - return (m_global_id == other.m_global_id); + return (m_person_id == other.m_person_id); } /** @@ -476,7 +476,7 @@ class Person CustomIndexArray m_test_results; ///< CustomIndexArray for TestResults. std::vector m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. - PersonId m_global_id; ///< Unique identifier of a person. + PersonId m_person_id; ///< Unique identifier of a person. uint32_t m_rng_index; }; diff --git a/cpp/models/graph_abm/graph_abmodel.h b/cpp/models/graph_abm/graph_abmodel.h index c22175a89b..aeba5687a6 100644 --- a/cpp/models/graph_abm/graph_abmodel.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -75,6 +75,11 @@ class GraphABModel : public abm::Model } } + /** + * @brief Evolve the Graph Model one time step. + * @param[in] t Current time. + * @param[in] dt Length of the time step. + */ void evolve(TimePoint t, TimeSpan dt) { Base::begin_step(t, dt); diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index fd49b956a5..e06fd40b12 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -22,7 +22,9 @@ #include "abm/person.h" #include "abm/model.h" #include "abm_helpers.h" +#include "memilio/epidemiology/age_group.h" #include "random_number_test.h" +#include using TestModel = RandomNumberTest; diff --git a/cpp/tests/test_graph_abm.cpp b/cpp/tests/test_graph_abm.cpp index d0a63f6f8c..187c3c68f4 100644 --- a/cpp/tests/test_graph_abm.cpp +++ b/cpp/tests/test_graph_abm.cpp @@ -171,8 +171,8 @@ TEST(TestGraphAbm, test_apply_mobility) TEST(TestGraphABM, test_graph_simulation) { - auto model1 = mio::GraphABModel(size_t(1), 1); - auto model2 = mio::GraphABModel(size_t(1), 2); + auto model1 = mio::GraphABModel(size_t(1), 0); + auto model2 = mio::GraphABModel(size_t(1), 1); mio::abm::TimePoint t0 = mio::abm::TimePoint(0); mio::abm::TimePoint tmax = t0 + mio::abm::days(5); @@ -180,8 +180,8 @@ TEST(TestGraphABM, test_graph_simulation) mio::Graph, mio::ABMMobilityEdge> graph; graph.add_node(model1.get_id(), MockHistory{}, t0, std::move(model1)); graph.add_node(model2.get_id(), MockHistory{}, t0, std::move(model2)); - graph.add_edge(0, 1); - graph.add_edge(1, 0); + graph.add_edge(model1.get_id(), model2.get_id()); + graph.add_edge(model2.get_id(), model1.get_id()); auto sim = mio::make_abm_graph_sim(t0, mio::abm::hours(12), std::move(graph)); sim.advance(tmax); @@ -241,3 +241,22 @@ TEST(TestGraphABM, mask_compliance) EXPECT_EQ(p1.get_location_type(), mio::abm::LocationType::Work); EXPECT_EQ(p2.get_location_type(), mio::abm::LocationType::School); } + +TEST(TestGraphABM, test_get_person) +{ + auto model = mio::GraphABModel(size_t(2), 0, std::vector{&mio::abm::go_to_work}); + auto home = model.add_location(mio::abm::LocationType::Home); + auto work = model.add_location(mio::abm::LocationType::Work); + auto pid1 = model.add_person(home, mio::AgeGroup(0)); + auto pid2 = model.add_person(work, mio::AgeGroup(1)); + + auto& p1 = model.get_person(pid1); + EXPECT_EQ(p1.get_location(), home); + EXPECT_EQ(p1.get_age(), mio::AgeGroup(0)); + model.remove_person(model.get_person_index(pid1)); + EXPECT_EQ(model.get_person_index(pid1), std::numeric_limits::max()); + + auto& p2 = model.get_person(pid2); + EXPECT_EQ(p2.get_location(), work); + EXPECT_EQ(p2.get_age(), mio::AgeGroup(1)); +} From c300dbfb1d920a590ea325d745d8fd73d100da16 Mon Sep 17 00:00:00 2001 From: jubicker Date: Mon, 20 Jan 2025 15:07:52 +0100 Subject: [PATCH 101/111] Add assertion --- cpp/models/abm/model.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 09896f4127..d93e285358 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -29,6 +29,7 @@ #include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" #include "memilio/utils/stl_util.h" +#include #include namespace mio @@ -88,6 +89,8 @@ void Model::interaction(TimePoint t, TimeSpan dt) PRAGMA_OMP(parallel for) for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { if (m_activeness_statuses[person_index]) { + assert(m_persons[person_index].get_location_model_id() == m_id && + "Person is not in this model but still active."); interact(m_persons[person_index], t, dt); } } @@ -221,7 +224,8 @@ void Model::build_compute_local_population_cache() const } // implicit taskloop barrier PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_persons; i++) { - if (m_persons[i].get_location_model_id() == m_id && m_activeness_statuses[i]) { + if (m_activeness_statuses[i]) { + assert(m_persons[i].get_location_model_id() == m_id && "Person is not in this model but still active."); ++m_local_population_cache[m_persons[i].get_location().get()]; } } // implicit taskloop barrier @@ -279,7 +283,8 @@ void Model::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().get(); - if (person.get_location_model_id() == m_id && m_activeness_statuses[i]) { + if (m_activeness_statuses[i]) { + assert(m_persons[i].get_location_model_id() == m_id && "Person is not in this model but still active."); mio::abm::add_exposure_contribution(m_air_exposure_rates_cache[location], m_contact_exposure_rates_cache[location], person, get_location(person.get_location()), t, dt); From 151e89b2af91461d3c945298ef4b6d4613667b6f Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 23 Jan 2025 09:18:21 +0100 Subject: [PATCH 102/111] add attributes to per son serialize --- cpp/models/abm/person.h | 4 +++- cpp/tests/test_abm_serialization.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index e9f56edab6..1f10200e96 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -433,7 +433,9 @@ class Person .add("cells", m_cells) .add("last_transport_mode", m_last_transport_mode) .add("rng_counter", m_rng_counter) - .add("test_results", m_test_results); + .add("test_results", m_test_results) + .add("id", m_person_id) + .add("rng_index", m_rng_index); } /** diff --git a/cpp/tests/test_abm_serialization.cpp b/cpp/tests/test_abm_serialization.cpp index 8435dbaa98..cf3b8b7e97 100644 --- a/cpp/tests/test_abm_serialization.cpp +++ b/cpp/tests/test_abm_serialization.cpp @@ -214,6 +214,8 @@ TEST(TestAbmSerialization, Person) mio::serialize_json(mio::CustomIndexArray{}).value(); reference_json["time_at_location"]["seconds"] = Json::Int(i++); reference_json["vaccinations"] = Json::Value(Json::arrayValue); + reference_json["id"] = Json::UInt(i++); + reference_json["rng_index"] = Json::UInt(i++); test_json_serialization(reference_json); } From 9ea7d301bbc11922582ed15de9dbe409a76b1613 Mon Sep 17 00:00:00 2001 From: Sascha Korf <51127093+xsaschako@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:25:11 +0100 Subject: [PATCH 103/111] bug finding comment --- cpp/tests/test_abm_model.cpp | 110 +++++++++++++++++------------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index e06fd40b12..2aff3698b5 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -495,61 +495,61 @@ TEST_F(TestModel, reachCapacity) /** * @brief Test that dead persons remain in the cemetery and can't be moved by scheduled trips. */ -TEST_F(TestModel, checkMobilityOfDeadPerson) -{ - using testing::Return; - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::days(1); - auto model = mio::abm::Model(num_age_groups); - - // Time to go from severe to critical infection is 1 day (dt). - model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; - // Time to go from critical infection to dead state is 1/2 day (0.5 * dt). - model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; - - auto home_id = model.add_location(mio::abm::LocationType::Home); - auto work_id = model.add_location(mio::abm::LocationType::Work); - auto icu_id = model.add_location(mio::abm::LocationType::ICU); - auto hospital_id = model.add_location(mio::abm::LocationType::Hospital); - // Create a person that is dead at time t - add_test_person(model, icu_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t); - // Create a person that is severe at hospital and will be dead at time t + dt - add_test_person(model, hospital_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); - - auto& p_dead = model.get_persons()[0]; - auto& p_severe = model.get_persons()[1]; - - p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); - p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); - p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); - p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - - // Add trip to see if a dead person can change location outside of cemetery by scheduled trips - mio::abm::TripList& trip_list = model.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, - mio::abm::LocationType::Work); - mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, - mio::abm::LocationType::Home); - mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, - mio::abm::LocationType::Home); - 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 - model.evolve(t, dt); - EXPECT_EQ(model.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(model.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 - model.evolve(t + dt, dt); - EXPECT_EQ(model.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(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); -} +// TEST_F(TestModel, checkMobilityOfDeadPerson) +// { +// using testing::Return; +// auto t = mio::abm::TimePoint(0); +// auto dt = mio::abm::days(1); +// auto model = mio::abm::Model(num_age_groups); + +// // Time to go from severe to critical infection is 1 day (dt). +// model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; +// // Time to go from critical infection to dead state is 1/2 day (0.5 * dt). +// model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; + +// auto home_id = model.add_location(mio::abm::LocationType::Home); +// auto work_id = model.add_location(mio::abm::LocationType::Work); +// auto icu_id = model.add_location(mio::abm::LocationType::ICU); +// auto hospital_id = model.add_location(mio::abm::LocationType::Hospital); +// // Create a person that is dead at time t +// add_test_person(model, icu_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t); +// // Create a person that is severe at hospital and will be dead at time t + dt +// add_test_person(model, hospital_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); + +// auto& p_dead = model.get_persons()[0]; +// auto& p_severe = model.get_persons()[1]; + +// p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); +// p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); +// p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); +// p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); +// p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); +// p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + +// // Add trip to see if a dead person can change location outside of cemetery by scheduled trips +// mio::abm::TripList& trip_list = model.get_trip_list(); +// mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, +// mio::abm::LocationType::Work); +// mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, +// mio::abm::LocationType::Home); +// mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, +// mio::abm::LocationType::Home); +// 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 +// model.evolve(t, dt); +// EXPECT_EQ(model.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(model.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 +// model.evolve(t + dt, dt); +// EXPECT_EQ(model.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(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); +// } using TestModelTestingCriteria = RandomNumberTest; From 03661d0f07302880b4cd4670e5e6ffef0b97e620 Mon Sep 17 00:00:00 2001 From: Sascha Korf <51127093+xsaschako@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:28:40 +0100 Subject: [PATCH 104/111] bug refining comment --- cpp/tests/test_abm_model.cpp | 110 +++++++++++++++++------------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 2aff3698b5..e06fd40b12 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -495,61 +495,61 @@ TEST_F(TestModel, reachCapacity) /** * @brief Test that dead persons remain in the cemetery and can't be moved by scheduled trips. */ -// TEST_F(TestModel, checkMobilityOfDeadPerson) -// { -// using testing::Return; -// auto t = mio::abm::TimePoint(0); -// auto dt = mio::abm::days(1); -// auto model = mio::abm::Model(num_age_groups); - -// // Time to go from severe to critical infection is 1 day (dt). -// model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; -// // Time to go from critical infection to dead state is 1/2 day (0.5 * dt). -// model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; - -// auto home_id = model.add_location(mio::abm::LocationType::Home); -// auto work_id = model.add_location(mio::abm::LocationType::Work); -// auto icu_id = model.add_location(mio::abm::LocationType::ICU); -// auto hospital_id = model.add_location(mio::abm::LocationType::Hospital); -// // Create a person that is dead at time t -// add_test_person(model, icu_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t); -// // Create a person that is severe at hospital and will be dead at time t + dt -// add_test_person(model, hospital_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); - -// auto& p_dead = model.get_persons()[0]; -// auto& p_severe = model.get_persons()[1]; - -// p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); -// p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); -// p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); -// p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); -// p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); -// p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); - -// // Add trip to see if a dead person can change location outside of cemetery by scheduled trips -// mio::abm::TripList& trip_list = model.get_trip_list(); -// mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, -// mio::abm::LocationType::Work); -// mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, -// mio::abm::LocationType::Home); -// mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, -// mio::abm::LocationType::Home); -// 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 -// model.evolve(t, dt); -// EXPECT_EQ(model.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(model.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 -// model.evolve(t + dt, dt); -// EXPECT_EQ(model.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(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); -// } +TEST_F(TestModel, checkMobilityOfDeadPerson) +{ + using testing::Return; + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::days(1); + auto model = mio::abm::Model(num_age_groups); + + // Time to go from severe to critical infection is 1 day (dt). + model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; + // Time to go from critical infection to dead state is 1/2 day (0.5 * dt). + model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 0.5; + + auto home_id = model.add_location(mio::abm::LocationType::Home); + auto work_id = model.add_location(mio::abm::LocationType::Work); + auto icu_id = model.add_location(mio::abm::LocationType::ICU); + auto hospital_id = model.add_location(mio::abm::LocationType::Hospital); + // Create a person that is dead at time t + add_test_person(model, icu_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t); + // Create a person that is severe at hospital and will be dead at time t + dt + add_test_person(model, hospital_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); + + auto& p_dead = model.get_persons()[0]; + auto& p_severe = model.get_persons()[1]; + + p_dead.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); + p_dead.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); + p_dead.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + + // Add trip to see if a dead person can change location outside of cemetery by scheduled trips + mio::abm::TripList& trip_list = model.get_trip_list(); + mio::abm::Trip trip1(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id, + mio::abm::LocationType::Work); + mio::abm::Trip trip2(p_dead.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::LocationType::Home); + mio::abm::Trip trip3(p_severe.get_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id, + mio::abm::LocationType::Home); + 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 + model.evolve(t, dt); + EXPECT_EQ(model.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(model.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 + model.evolve(t + dt, dt); + EXPECT_EQ(model.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(model.get_location(p_severe.get_id()).get_type(), mio::abm::LocationType::Cemetery); +} using TestModelTestingCriteria = RandomNumberTest; From 8147922215d09404455eb98a16839f0b5119e221 Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 23 Jan 2025 10:38:22 +0100 Subject: [PATCH 105/111] fix test after merge --- cpp/tests/test_abm_model.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index 4e61c402c6..b191f23782 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -698,8 +698,8 @@ TEST_F(TestModel, checkParameterConstraints) params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 5.; params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = -6.; EXPECT_TRUE(params.check_constraints()); - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 6.; - params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = -7.; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 6.; + params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = -7.; EXPECT_TRUE(params.check_constraints()); params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = 7.; params.get()[{mio::abm::VirusVariant::Wildtype, age_group_0_to_4}] = -8.; @@ -1000,13 +1000,14 @@ TEST_F(TestModel, personCanDieInHospital) { using testing::Return; - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::days(1); - auto model = mio::abm::Model(num_age_groups); + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::days(1); + auto model = mio::abm::Model(num_age_groups); model.get_rng() = this->get_rng(); // Time to go from infected with symptoms to severe infection is 1 day(dt). - model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 1; + model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = + 1; // Time to go from severe infection to critical is 1 day(dt). model.parameters.get()[{mio::abm::VirusVariant::Wildtype, age_group_60_to_79}] = 1; // Time to go from severe infection to dead is 1 day(dt). @@ -1028,10 +1029,10 @@ TEST_F(TestModel, personCanDieInHospital) add_test_person(model, home_id, age_group_60_to_79, mio::abm::InfectionState::Dead, t + dt); auto& p_severe = model.get_persons()[0]; - p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id); - p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id); - p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id); - p_severe.set_assigned_location(mio::abm::LocationType::Work, work_id); + p_severe.set_assigned_location(mio::abm::LocationType::Hospital, hospital_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::ICU, icu_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); + p_severe.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); // Check the infection course goes from InfectedSymptoms to Severe to Dead and skips Critical EXPECT_EQ(p_severe.get_infection_state(t - dt), mio::abm::InfectionState::InfectedSymptoms); From f37a92f9a2065863d11fe3923b3492c07e4819c4 Mon Sep 17 00:00:00 2001 From: jubicker Date: Sun, 26 Jan 2025 15:22:58 +0100 Subject: [PATCH 106/111] save rng key in person --- cpp/benchmarks/abm.cpp | 2 +- cpp/examples/abm_history_object.cpp | 2 +- cpp/examples/abm_minimal.cpp | 2 +- cpp/examples/graph_abm.cpp | 4 +- cpp/models/abm/model.cpp | 4 +- cpp/models/abm/model.h | 2 +- cpp/models/abm/person.cpp | 1 + cpp/models/abm/person.h | 12 ++++- cpp/models/abm/personal_rng.cpp | 4 +- cpp/models/abm/personal_rng.h | 4 +- cpp/models/graph_abm/graph_abmodel.h | 4 +- cpp/simulations/abm.cpp | 2 +- cpp/simulations/abm_braunschweig.cpp | 2 +- cpp/tests/abm_helpers.cpp | 2 +- cpp/tests/test_abm_location.cpp | 2 +- cpp/tests/test_abm_lockdown_rules.cpp | 16 +++--- cpp/tests/test_abm_masks.cpp | 4 +- cpp/tests/test_abm_mobility_rules.cpp | 68 +++++++++++-------------- cpp/tests/test_abm_model.cpp | 8 +-- cpp/tests/test_abm_person.cpp | 14 ++--- cpp/tests/test_abm_testing_strategy.cpp | 11 ++-- 21 files changed, 87 insertions(+), 83 deletions(-) diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 46e8167a6f..a9a179ae1d 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -68,7 +68,7 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list //infections and masks for (auto& person : model.get_persons()) { - auto prng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + auto prng = mio::abm::PersonalRandomNumberGenerator(person); //some % of people are infected, large enough to have some infection activity without everyone dying auto pct_infected = 0.05; if (mio::UniformDistribution::get_instance()(prng, 0.0, 1.0) < pct_infected) { diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index 5b76aca9a5..9eba7358d3 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -140,7 +140,7 @@ int main() // The infection states are chosen randomly. auto persons = model.get_persons(); for (auto& person : persons) { - auto rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(person); mio::abm::InfectionState infection_state = (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); if (infection_state != mio::abm::InfectionState::Susceptible) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 90293360dd..aab66e3f62 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -121,7 +121,7 @@ int main() for (auto& person : model.get_persons()) { mio::abm::InfectionState infection_state = mio::abm::InfectionState( mio::DiscreteDistribution::get_instance()(mio::thread_local_rng(), infection_distribution)); - auto rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), model.parameters, start_date, infection_state)); diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 3083e5107d..a4eedb9d55 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -198,7 +198,7 @@ int main() for (auto& person : model1.get_persons()) { mio::abm::InfectionState infection_state = mio::abm::InfectionState( mio::DiscreteDistribution::get_instance()(model1.get_rng(), infection_distribution_m1)); - auto rng = mio::abm::PersonalRandomNumberGenerator(model1.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), model1.parameters, start_date, infection_state)); @@ -228,7 +228,7 @@ int main() for (auto& person : model2.get_persons()) { mio::abm::InfectionState infection_state = mio::abm::InfectionState( mio::DiscreteDistribution::get_instance()(model2.get_rng(), infection_distribution_m2)); - auto rng = mio::abm::PersonalRandomNumberGenerator(model2.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(person); if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), model2.parameters, start_date, infection_state)); diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index 711f43abe2..fead965e23 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -103,7 +103,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) for (uint32_t person_index = 0; person_index < num_persons; ++person_index) { if (m_activeness_statuses[person_index]) { Person& person = m_persons[person_index]; - auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + auto personal_rng = PersonalRandomNumberGenerator(person); auto try_mobility_rule = [&](auto rule) -> bool { // run mobility rule and check if change of location can actually happen @@ -178,7 +178,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) m_trip_list.increase_index()) { auto& trip = m_trip_list.get_next_trip(weekend); auto& person = get_person(trip.person_id); - auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + auto personal_rng = PersonalRandomNumberGenerator(person); // skip the trip if the person is in quarantine or is dead if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { continue; diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index e1be85a2b5..9599d35e60 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -616,7 +616,7 @@ class Model compute_exposure_caches(t, dt); m_are_exposure_caches_valid = true; } - auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + auto personal_rng = PersonalRandomNumberGenerator(person); mio::abm::interact(personal_rng, person, get_location(person.get_location()), m_air_exposure_rates_cache[person.get_location().get()], m_contact_exposure_rates_cache[person.get_location().get()], t, dt, parameters); diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 3b39d51e46..f1c102f373 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -50,6 +50,7 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_assigned_location_model_ids((int)LocationType::Count) , m_person_id(person_id) , m_rng_index(static_cast(person_id.get())) + , m_rng_key(rng.get_key()) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 7a917a6761..b86f16f7d5 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -407,6 +407,15 @@ class Person return m_rng_index; } + /** + * @brief Get this person's key that is used for the RandomNumberGenerator. + * @see mio::abm::PersonalRandomNumberGenerator. + */ + mio::Key get_rng_key() + { + return m_rng_key; + } + /** * @brief Get the latest #ProtectionType and its initial TimePoint of the Person. */ @@ -479,7 +488,8 @@ class Person std::vector m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. PersonId m_person_id; ///< Unique identifier of a person. - uint32_t m_rng_index; + uint32_t m_rng_index; ///< Index for PersonalRandomNumberGenerator. + mio::Key m_rng_key; ///< Key for PersonalRandomNumberGenerator }; } // namespace abm diff --git a/cpp/models/abm/personal_rng.cpp b/cpp/models/abm/personal_rng.cpp index b7649942ef..2a5938fe80 100644 --- a/cpp/models/abm/personal_rng.cpp +++ b/cpp/models/abm/personal_rng.cpp @@ -35,8 +35,8 @@ PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(mio::Key { } -PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) - : PersonalRandomNumberGenerator(rng.get_key(), person.get_rng_index(), person.get_rng_counter()) +PersonalRandomNumberGenerator::PersonalRandomNumberGenerator(Person& person) + : PersonalRandomNumberGenerator(person.get_rng_key(), person.get_rng_index(), person.get_rng_counter()) { } diff --git a/cpp/models/abm/personal_rng.h b/cpp/models/abm/personal_rng.h index 319bc8f326..7a359bb10f 100644 --- a/cpp/models/abm/personal_rng.h +++ b/cpp/models/abm/personal_rng.h @@ -59,11 +59,9 @@ class PersonalRandomNumberGenerator : public mio::RandomNumberGeneratorBase bool { //run mobility rule and check if change of location can actually happen @@ -163,7 +163,7 @@ class GraphABModel : public abm::Model auto& trip = Base::m_trip_list.get_next_trip(weekend); auto& person = get_person(trip.person_id); auto person_index = Base::get_person_index(trip.person_id); - auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); + auto personal_rng = PersonalRandomNumberGenerator(person); // skip the trip if the person is in quarantine or is dead if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { continue; diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 8bfee8db00..655a078892 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -448,7 +448,7 @@ void assign_infection_state(mio::abm::Model& model, mio::abm::TimePoint t, doubl { auto persons = model.get_persons(); for (auto& person : persons) { - auto rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(person); auto infection_state = determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); if (infection_state != mio::abm::InfectionState::Susceptible) { diff --git a/cpp/simulations/abm_braunschweig.cpp b/cpp/simulations/abm_braunschweig.cpp index 3a68b96527..f97da97de8 100644 --- a/cpp/simulations/abm_braunschweig.cpp +++ b/cpp/simulations/abm_braunschweig.cpp @@ -89,7 +89,7 @@ void assign_infection_state(mio::abm::Model& model, mio::abm::TimePoint t, doubl { auto persons = model.get_persons(); for (auto& person : persons) { - auto rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + auto rng = mio::abm::PersonalRandomNumberGenerator(person); auto infection_state = determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); if (infection_state != mio::abm::InfectionState::Susceptible) diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index 5507030f90..8f70ba384d 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -29,7 +29,7 @@ mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Loc assert(age.get() < params.get_num_groups()); mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, id); if (infection_state != mio::abm::InfectionState::Susceptible) { - auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); + auto rng_p = mio::abm::PersonalRandomNumberGenerator(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_location.cpp b/cpp/tests/test_abm_location.cpp index 93bef0a09d..7b6fe62cfa 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -112,7 +112,7 @@ TEST_F(TestLocation, interact) auto susceptible = make_test_person(this->get_rng(), location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); // Probability of no infection - auto person_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), susceptible); + auto person_rng = mio::abm::PersonalRandomNumberGenerator(susceptible); interact_testing(person_rng, susceptible, location, local_population, t, dt, params); 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 1a8a932c0c..13d08aca01 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -71,9 +71,9 @@ TEST_F(TestLockdownRules, school_closure) mio::abm::set_school_closure(t, 0.7, params); // Test that p1 stays home and p2 goes to school - auto p1_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p1); + auto p1_rng = mio::abm::PersonalRandomNumberGenerator(p1); EXPECT_EQ(mio::abm::go_to_school(p1_rng, p1, t_morning, dt, params), mio::abm::LocationType::Home); - auto p2_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p2); + auto p2_rng = mio::abm::PersonalRandomNumberGenerator(p2); EXPECT_EQ(mio::abm::go_to_school(p2_rng, p2, t_morning, dt, params), mio::abm::LocationType::School); } @@ -119,7 +119,7 @@ TEST_F(TestLockdownRules, school_opening) mio::abm::set_school_closure(t_opening, 0., params); // Test that after reopening, the person goes to school - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(p); EXPECT_EQ(mio::abm::go_to_school(p_rng, p, t_morning, dt, params), mio::abm::LocationType::School); } @@ -167,9 +167,9 @@ TEST_F(TestLockdownRules, home_office) person2.set_assigned_location(work.get_type(), work.get_id(), work.get_model_id()); // Check that person1 goes to work and person2 stays at home. - auto p1_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person1); + auto p1_rng = mio::abm::PersonalRandomNumberGenerator(person1); EXPECT_EQ(mio::abm::go_to_work(p1_rng, person1, t_morning, dt, params), mio::abm::LocationType::Work); - auto p2_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person2); + auto p2_rng = mio::abm::PersonalRandomNumberGenerator(person2); EXPECT_EQ(mio::abm::go_to_work(p2_rng, person2, t_morning, dt, params), mio::abm::LocationType::Home); } @@ -212,7 +212,7 @@ TEST_F(TestLockdownRules, no_home_office) mio::abm::set_home_office(t_opening, 0., params); // Test that after removing the home office rules, p goes back to the office. - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(p); EXPECT_EQ(mio::abm::go_to_work(p_rng, p, t_morning, dt, params), mio::abm::LocationType::Work); } @@ -236,7 +236,7 @@ TEST_F(TestLockdownRules, social_event_closure) mio::abm::close_social_events(t, 1, params); // Checks that p stays home instead of attending social events during a closure. - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(p); EXPECT_EQ(mio::abm::go_to_event(p_rng, p, t_evening, dt, params), mio::abm::LocationType::Home); } @@ -266,6 +266,6 @@ TEST_F(TestLockdownRules, social_events_opening) EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); // Test that after reopening, p attends social events again. - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(p); EXPECT_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 db2a3c930d..1396f1b94f 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -99,11 +99,11 @@ TEST_F(TestMasks, maskProtection) mock_exponential_dist; // Person 1 interaction with full protection EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(1)); - auto p1_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), susc_person1); + auto p1_rng = mio::abm::PersonalRandomNumberGenerator(susc_person1); interact_testing(p1_rng, susc_person1, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params); // Person 2 interaction without protection EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); - auto p2_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), susc_person2); + auto p2_rng = mio::abm::PersonalRandomNumberGenerator(susc_person2); interact_testing(p2_rng, susc_person2, infection_location, {susc_person1, susc_person2, infected1}, t, dt, params); // susc_person1 (with mask) should remain susceptible diff --git a/cpp/tests/test_abm_mobility_rules.cpp b/cpp/tests/test_abm_mobility_rules.cpp index 255d3ee4ad..1ad41809a0 100644 --- a/cpp/tests/test_abm_mobility_rules.cpp +++ b/cpp/tests/test_abm_mobility_rules.cpp @@ -33,7 +33,7 @@ TEST_F(TestMobilityRules, random_mobility) int t = 0, dt = 1; auto default_type = mio::abm::LocationType::Cemetery; auto person = mio::abm::Person(this->get_rng(), default_type, 0, 0, age_group_15_to_34); - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(person); auto params = mio::abm::Parameters(num_age_groups); ScopedMockDistribution>>> mock_exp_dist; @@ -93,8 +93,8 @@ TEST_F(TestMobilityRules, 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::PersonalRandomNumberGenerator(this->get_rng(), p_child); - auto adult_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child); + auto child_rng = mio::abm::PersonalRandomNumberGenerator(p_child); + auto adult_rng = mio::abm::PersonalRandomNumberGenerator(p_adult); // 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; @@ -136,12 +136,10 @@ TEST_F(TestMobilityRules, 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(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - auto rng_child_goes_to_school_at_6 = - mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_6); + auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(p_child_goes_to_school_at_6); auto p_child_goes_to_school_at_8 = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - auto rng_child_goes_to_school_at_8 = - mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_8); + auto rng_child_goes_to_school_at_8 = mio::abm::PersonalRandomNumberGenerator(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); @@ -202,14 +200,12 @@ TEST_F(TestMobilityRules, students_go_to_school_in_different_times_with_smaller_ // First student goes to school at 6:00 AM auto p_child_goes_to_school_at_6 = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - auto rng_child_goes_to_school_at_6 = - mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_6); + auto rng_child_goes_to_school_at_6 = mio::abm::PersonalRandomNumberGenerator(p_child_goes_to_school_at_6); // Second student goes to school at 8:30 AM auto p_child_goes_to_school_at_8_30 = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_5_to_14); - auto rng_child_goes_to_school_at_8_30 = - mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child_goes_to_school_at_8_30); + auto rng_child_goes_to_school_at_8_30 = mio::abm::PersonalRandomNumberGenerator(p_child_goes_to_school_at_8_30); // Time points for the morning scenarios auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); @@ -247,7 +243,7 @@ TEST_F(TestMobilityRules, school_return) mio::abm::Location school(mio::abm::LocationType::School, 0, num_age_groups); auto p_child = mio::abm::Person(this->get_rng(), school.get_type(), school.get_id(), school.get_model_id(), age_group_5_to_14); - auto rng_child = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_child); + auto rng_child = mio::abm::PersonalRandomNumberGenerator(p_child); // Simulate a time point after school hours auto t = mio::abm::TimePoint(0) + mio::abm::hours(15); @@ -280,10 +276,10 @@ TEST_F(TestMobilityRules, worker_goes_to_work) auto p_retiree = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); - auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_retiree); + auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(p_retiree); auto p_adult = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult); + auto rng_adult = mio::abm::PersonalRandomNumberGenerator(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); @@ -330,10 +326,10 @@ TEST_F(TestMobilityRules, worker_goes_to_work_with_non_dividable_timespan) // Set up two people: one retiree and one working adult. auto p_retiree = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); - auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_retiree); + auto rng_retiree = mio::abm::PersonalRandomNumberGenerator(p_retiree); auto p_adult = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult); + auto rng_adult = mio::abm::PersonalRandomNumberGenerator(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); @@ -385,12 +381,10 @@ TEST_F(TestMobilityRules, workers_go_to_work_in_different_times) // Create two workers: one goes to work at 6 AM and the other at 8 AM. auto p_adult_goes_to_work_at_6 = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - auto rng_adult_goes_to_work_at_6 = - mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult_goes_to_work_at_6); + auto rng_adult_goes_to_work_at_6 = mio::abm::PersonalRandomNumberGenerator(p_adult_goes_to_work_at_6); auto p_adult_goes_to_work_at_8 = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_15_to_34); - auto rng_adult_goes_to_work_at_8 = - mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult_goes_to_work_at_8); + auto rng_adult_goes_to_work_at_8 = mio::abm::PersonalRandomNumberGenerator(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); @@ -434,7 +428,7 @@ TEST_F(TestMobilityRules, work_return) // Set up a random number generator and a worker who is currently at work auto p_adult = mio::abm::Person(this->get_rng(), work.get_type(), work.get_id(), work.get_model_id(), age_group_35_to_59); - auto rng_adult = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_adult); + auto rng_adult = mio::abm::PersonalRandomNumberGenerator(p_adult); // Set the time to 5 PM (17:00) when the worker should return home auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); auto dt = mio::abm::hours(1); @@ -457,7 +451,7 @@ TEST_F(TestMobilityRules, quarantine) auto p_inf1 = make_test_person(this->get_rng(), work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); - auto rng_inf1 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf1); + auto rng_inf1 = mio::abm::PersonalRandomNumberGenerator(p_inf1); p_inf1.get_tested(rng_inf1, t, test_params); // Check detected infected person quarantines at home EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf1, p_inf1, t, dt, mio::abm::Parameters(num_age_groups)), @@ -465,14 +459,14 @@ TEST_F(TestMobilityRules, quarantine) auto p_inf2 = make_test_person(this->get_rng(), work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); - auto rng_inf2 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf2); + auto rng_inf2 = mio::abm::PersonalRandomNumberGenerator(p_inf2); // Check that undetected infected person does not quaratine EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf2, p_inf2, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); auto p_inf3 = make_test_person(this->get_rng(), hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); - auto rng_inf3 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf3); + auto rng_inf3 = mio::abm::PersonalRandomNumberGenerator(p_inf3); p_inf1.get_tested(rng_inf3, t, test_params); // Check that detected infected person does not leave hospital to quarantine EXPECT_EQ(mio::abm::go_to_quarantine(rng_inf3, p_inf3, t, dt, mio::abm::Parameters(num_age_groups)), @@ -489,7 +483,7 @@ TEST_F(TestMobilityRules, hospital) auto dt = mio::abm::hours(1); auto p_inf = make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSevere, t); - auto rng_inf = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf); + auto rng_inf = mio::abm::PersonalRandomNumberGenerator(p_inf); // Ensure person goes to the hospital when severely infected EXPECT_EQ(mio::abm::go_to_hospital(rng_inf, p_inf, t, dt, mio::abm::Parameters(num_age_groups)), @@ -497,7 +491,7 @@ TEST_F(TestMobilityRules, hospital) auto p_car = make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); - auto rng_car = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_car); + auto rng_car = mio::abm::PersonalRandomNumberGenerator(p_car); // Ensure person has infection symptoms still stay at home EXPECT_EQ(mio::abm::go_to_hospital(rng_car, p_car, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Home); @@ -519,11 +513,11 @@ TEST_F(TestMobilityRules, go_shopping) // Create an infected child in the hospital auto p_hosp = make_test_person(this->get_rng(), hospital, age_group_0_to_4, mio::abm::InfectionState::InfectedSymptoms, t_weekday); - auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_hosp); + auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(p_hosp); // Create a healthy elderly person at home auto p_home = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); - auto rng_home = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_home); + auto rng_home = mio::abm::PersonalRandomNumberGenerator(p_home); // Check that an infected person stays in the hospital and doesn't go shopping EXPECT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, mio::abm::Parameters(num_age_groups)), @@ -557,7 +551,7 @@ TEST_F(TestMobilityRules, shop_return) mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0, num_age_groups); auto p = make_test_person(this->get_rng(), shop, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, t); - auto rng_p = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); + auto rng_p = mio::abm::PersonalRandomNumberGenerator(p); // Simulate the person spending 1 hour at the shop p.add_time_at_location(dt); @@ -575,11 +569,11 @@ TEST_F(TestMobilityRules, go_event) mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); auto p_work = mio::abm::Person(this->get_rng(), work.get_type(), work.get_id(), work.get_model_id(), age_group_35_to_59); - auto rng_work = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_work); + auto rng_work = mio::abm::PersonalRandomNumberGenerator(p_work); mio::abm::Location home(mio::abm::LocationType::Home, 1, num_age_groups); auto p_home = mio::abm::Person(this->get_rng(), home.get_type(), home.get_id(), home.get_model_id(), age_group_60_to_79); - auto rng_home = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_home); + auto rng_home = mio::abm::PersonalRandomNumberGenerator(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); @@ -620,7 +614,7 @@ TEST_F(TestMobilityRules, event_return) // Initialize the person at the social event location auto p = mio::abm::Person(this->get_rng(), social_event.get_type(), social_event.get_id(), social_event.get_model_id(), age_group_15_to_34); - auto rng_p = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); + auto rng_p = mio::abm::PersonalRandomNumberGenerator(p); // Simulate the person spending 3 hours at the social event p.add_time_at_location(dt); // After spending the time at the social event, the person should return home @@ -637,7 +631,7 @@ TEST_F(TestMobilityRules, icu) auto dt = mio::abm::hours(1); auto p_hosp = make_test_person(this->get_rng(), hospital, age_group_15_to_34, mio::abm::InfectionState::InfectedCritical, t); - auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_hosp); + auto rng_hosp = mio::abm::PersonalRandomNumberGenerator(p_hosp); // Ensure critically infected person goes to the ICU EXPECT_EQ(mio::abm::go_to_icu(rng_hosp, p_hosp, t, dt, mio::abm::Parameters(num_age_groups)), @@ -646,7 +640,7 @@ TEST_F(TestMobilityRules, icu) mio::abm::Location work(mio::abm::LocationType::Work, 1, num_age_groups); auto p_work = make_test_person(this->get_rng(), work, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, t); - auto rng_work = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_work); + auto rng_work = mio::abm::PersonalRandomNumberGenerator(p_work); // Ensure infected with symptions person can still go to work EXPECT_EQ(mio::abm::go_to_icu(rng_work, p_work, t, dt, mio::abm::Parameters(num_age_groups)), mio::abm::LocationType::Work); @@ -662,10 +656,10 @@ TEST_F(TestMobilityRules, recover) auto dt = mio::abm::hours(1); auto p_rec = make_test_person(this->get_rng(), hospital, age_group_60_to_79, mio::abm::InfectionState::Recovered, t); - auto rng_rec = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_rec); + auto rng_rec = mio::abm::PersonalRandomNumberGenerator(p_rec); auto p_inf = make_test_person(this->get_rng(), hospital, age_group_60_to_79, mio::abm::InfectionState::InfectedSevere, t); - auto rng_inf = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_inf); + auto rng_inf = mio::abm::PersonalRandomNumberGenerator(p_inf); // Ensure recovered person returns home and infected severe person stay in hospital EXPECT_EQ(mio::abm::return_home_when_recovered(rng_rec, p_rec, t, dt, {num_age_groups}), mio::abm::LocationType::Home); @@ -682,7 +676,7 @@ TEST_F(TestMobilityRules, dead) auto t = mio::abm::TimePoint(12346); auto dt = mio::abm::hours(1); auto p_dead = make_test_person(this->get_rng(), icu, age_group_60_to_79, mio::abm::InfectionState::Dead, t); - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p_dead); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(p_dead); 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_model.cpp b/cpp/tests/test_abm_model.cpp index b191f23782..948abd4901 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -381,13 +381,13 @@ TEST_F(TestModel, evolveMobilityTrips) .WillOnce(testing::Return(0.8)) // draw random beta p4 .RetiresOnSaturation(); - auto rng_p1 = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), p1); + auto rng_p1 = mio::abm::PersonalRandomNumberGenerator(p1); p1.add_new_infection(mio::abm::Infection(rng_p1, static_cast(0), p1.get_age(), model.parameters, t, mio::abm::InfectionState::InfectedNoSymptoms)); - auto rng_p3 = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), p1); + auto rng_p3 = mio::abm::PersonalRandomNumberGenerator(p1); p3.add_new_infection(mio::abm::Infection(rng_p3, static_cast(0), p3.get_age(), model.parameters, t, mio::abm::InfectionState::InfectedSevere)); - auto rng_p4 = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), p1); + auto rng_p4 = mio::abm::PersonalRandomNumberGenerator(p1); p4.add_new_infection(mio::abm::Infection(rng_p4, static_cast(0), p4.get_age(), model.parameters, t, mio::abm::InfectionState::Recovered)); @@ -606,7 +606,7 @@ TEST_F(TestModelTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) auto pid = add_test_person(model, home_id, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms, current_time - test_time); auto& person = model.get_person(pid); - auto rng_person = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(person); person.set_assigned_location(mio::abm::LocationType::Home, home_id, model.get_id()); person.set_assigned_location(mio::abm::LocationType::Work, work_id, model.get_id()); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index 64a86d1574..329afaae5e 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -156,7 +156,7 @@ TEST_F(TestPerson, quarantine) auto person = make_test_person(this->get_rng(), home, age_group_35_to_59, mio::abm::InfectionState::InfectedSymptoms, t_morning, infection_parameters); - auto rng_person = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(person); // Test quarantine when a person is tested and positive. person.get_tested(rng_person, t_morning, test_params); @@ -183,10 +183,10 @@ TEST_F(TestPerson, get_tested) mio::abm::Location loc(mio::abm::LocationType::Home, 0, num_age_groups); auto infected = make_test_person(this->get_rng(), loc, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); - auto rng_infected = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), infected); + auto rng_infected = mio::abm::PersonalRandomNumberGenerator(infected); auto susceptible = mio::abm::Person(this->get_rng(), loc.get_type(), loc.get_id(), loc.get_model_id(), age_group_15_to_34); - auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), susceptible); + auto rng_suscetible = mio::abm::PersonalRandomNumberGenerator(susceptible); auto pcr_parameters = params.get()[mio::abm::TestType::PCR]; auto antigen_parameters = params.get()[mio::abm::TestType::Antigen]; @@ -272,7 +272,7 @@ TEST_F(TestPerson, interact) // Create a person and set up a random number generator specific to that person. auto person = mio::abm::Person(this->get_rng(), loc.get_type(), loc.get_id(), loc.get_model_id(), age_group_15_to_34); - auto rng_person = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(person); auto dt = mio::abm::seconds(8640); //0.1 days // Simulate interaction and check that the person accumulates time at the location. interact_testing(rng_person, person, loc, {person}, t, dt, infection_parameters); @@ -332,7 +332,7 @@ TEST_F(TestPerson, getLatestProtection) auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); auto person = mio::abm::Person(this->get_rng(), location.get_type(), location.get_id(), location.get_model_id(), age_group_15_to_34); - auto prng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto prng = mio::abm::PersonalRandomNumberGenerator(person); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); auto t = mio::abm::TimePoint(0); @@ -361,7 +361,7 @@ TEST_F(TestPerson, rng) EXPECT_EQ(p.get_rng_counter(), mio::Counter(0)); // Verify RNG counter increments. - auto p_rng = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), p); + auto p_rng = mio::abm::PersonalRandomNumberGenerator(p); EXPECT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{0})); p_rng(); @@ -404,7 +404,7 @@ TEST_F(TestPerson, isCompliant) // Create test person and associated random number generator auto person = make_test_person(this->get_rng(), home); - auto rng_person = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person); + auto rng_person = mio::abm::PersonalRandomNumberGenerator(person); // Test cases with a complete truth table for compliance levels struct TestCase { diff --git a/cpp/tests/test_abm_testing_strategy.cpp b/cpp/tests/test_abm_testing_strategy.cpp index 10e359a9a2..8c8fb5de06 100644 --- a/cpp/tests/test_abm_testing_strategy.cpp +++ b/cpp/tests/test_abm_testing_strategy.cpp @@ -30,7 +30,8 @@ TEST_F(TestTestingCriteria, addRemoveAndEvaluateTestCriteria) // Create test locations and a person in a specific infection state. mio::abm::Location home(mio::abm::LocationType::Home, 0, num_age_groups); mio::abm::Location work(mio::abm::LocationType::Work, 0, num_age_groups); - auto person = make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); + auto person = + make_test_person(this->get_rng(), home, age_group_15_to_34, mio::abm::InfectionState::InfectedSymptoms); mio::abm::TimePoint t{0}; // Initialize testing criteria with no age group or infection state set. @@ -119,10 +120,10 @@ TEST_F(TestTestingScheme, runScheme) auto person1 = make_test_person(this->get_rng(), loc_home, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, start_date - test_params_pcr.required_time); - auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person1); + auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(person1); auto person2 = make_test_person(this->get_rng(), loc_home, age_group_15_to_34, mio::abm::InfectionState::Recovered, start_date - test_params_pcr.required_time); - auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person2); + auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(person2); // Mock uniform distribution to control random behavior in testing. ScopedMockDistribution>>> mock_uniform_dist; @@ -170,10 +171,10 @@ TEST_F(TestTestingScheme, initAndRunTestingStrategy) auto person1 = make_test_person(this->get_rng(), loc_work, age_group_15_to_34, mio::abm::InfectionState::InfectedNoSymptoms, start_date - test_params_pcr.required_time); - auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person1); + auto rng_person1 = mio::abm::PersonalRandomNumberGenerator(person1); auto person2 = make_test_person(this->get_rng(), loc_work, age_group_15_to_34, mio::abm::InfectionState::Recovered, start_date - test_params_pcr.required_time); - auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(this->get_rng(), person2); + auto rng_person2 = mio::abm::PersonalRandomNumberGenerator(person2); // Mock uniform distribution to control random behavior in testing. ScopedMockDistribution>>> mock_uniform_dist; From c4ae180d0fa9dad21837fe332157310c79472266 Mon Sep 17 00:00:00 2001 From: jubicker <113909589+jubicker@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:46:12 +0100 Subject: [PATCH 107/111] Update cpp/models/abm/person.h Co-authored-by: David Kerkmann <44698825+DavidKerkmann@users.noreply.github.com> --- cpp/models/abm/person.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index b86f16f7d5..787c306821 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -399,7 +399,7 @@ class Person } /** - * @brief Get this persons index that is used for the RandomNumberGenerator. + * @brief Get this Person's index that is used for the RandomNumberGenerator. * @see mio::abm::PersonalRandomNumberGenerator. */ uint32_t get_rng_index() From f9ab8182e75871d6e114b26563eea8b23b6483ab Mon Sep 17 00:00:00 2001 From: jubicker <113909589+jubicker@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:48:13 +0100 Subject: [PATCH 108/111] Apply suggestions from code review Co-authored-by: David Kerkmann <44698825+DavidKerkmann@users.noreply.github.com> --- cpp/models/abm/person.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 787c306821..d7011dfab9 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -408,7 +408,7 @@ class Person } /** - * @brief Get this person's key that is used for the RandomNumberGenerator. + * @brief Get this Person's key that is used for the RandomNumberGenerator. * @see mio::abm::PersonalRandomNumberGenerator. */ mio::Key get_rng_key() From f8927ccc4e662f4bf58835d62391a42d235b043b Mon Sep 17 00:00:00 2001 From: jubicker Date: Thu, 30 Jan 2025 10:51:52 +0100 Subject: [PATCH 109/111] Review --- cpp/models/abm/person.cpp | 2 +- cpp/models/abm/person.h | 4 ++-- cpp/models/graph_abm/graph_abmodel.h | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index f1c102f373..a0e450a64a 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -49,8 +49,8 @@ Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, Loca , m_test_results({TestType::Count}, TestResult()) , m_assigned_location_model_ids((int)LocationType::Count) , m_person_id(person_id) - , m_rng_index(static_cast(person_id.get())) , m_rng_key(rng.get_key()) + , m_rng_index(static_cast(person_id.get())) { m_random_workgroup = UniformDistribution::get_instance()(rng); m_random_schoolgroup = UniformDistribution::get_instance()(rng); diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index b86f16f7d5..279daa49b2 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -483,13 +483,13 @@ class Person m_compliance; ///< Vector of compliance values for all #InterventionType%s. Values from 0 to 1. 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. CustomIndexArray m_test_results; ///< CustomIndexArray for TestResults. std::vector m_assigned_location_model_ids; ///< Vector with model ids of the assigned locations. Only used in graph abm. PersonId m_person_id; ///< Unique identifier of a person. - uint32_t m_rng_index; ///< Index for PersonalRandomNumberGenerator. mio::Key m_rng_key; ///< Key for PersonalRandomNumberGenerator + uint32_t m_rng_index; ///< Index for PersonalRandomNumberGenerator. + Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator. }; } // namespace abm diff --git a/cpp/models/graph_abm/graph_abmodel.h b/cpp/models/graph_abm/graph_abmodel.h index a9603807e3..05490bcbe6 100644 --- a/cpp/models/graph_abm/graph_abmodel.h +++ b/cpp/models/graph_abm/graph_abmodel.h @@ -70,9 +70,7 @@ class GraphABModel : public abm::Model { Base::m_persons.erase(Base::m_persons.begin() + pos); Base::m_activeness_statuses.erase(Base::m_activeness_statuses.begin() + pos); - if (Base::m_person_ids_equal_index) { - Base::m_person_ids_equal_index = false; - } + Base::m_person_ids_equal_index = false; } /** From 533c3ed0474bc3284ecc2e9bc3263cd96f38a913 Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 31 Jan 2025 11:36:39 +0100 Subject: [PATCH 110/111] code cov --- cpp/models/abm/model.h | 60 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 9599d35e60..673eda0f79 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -362,40 +362,14 @@ class Model * @param[in] person_id A Person's PersonId. * @return A reference to the Person. */ - Person& get_person(PersonId person_id) + const Person& get_person(PersonId person_id) const { - if (m_person_ids_equal_index) { - return m_persons[static_cast(person_id.get())]; - } - else { - mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due " - "to former removal of persons. Therefore m_persons is searched."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [person_id](auto& person) { - return person.get_id() == person_id; - }); - if (it == m_persons.end()) { - log_error("Given Person is not in this Model."); - } - return *it; - } + return get_person_impl(*this, person_id); } - const Person& get_person(PersonId person_id) const + Person& get_person(PersonId person_id) { - if (m_person_ids_equal_index) { - return m_persons[static_cast(person_id.get())]; - } - else { - mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due " - "to former removal of persons. Therefore m_persons is searched."); - auto it = std::find_if(m_persons.begin(), m_persons.end(), [person_id](auto& person) { - return person.get_id() == person_id; - }); - if (it == m_persons.end()) { - log_error("Given Person is not in this Model."); - } - return *it; - } + return get_person_impl(*this, person_id); } /** @@ -622,6 +596,32 @@ class Model m_contact_exposure_rates_cache[person.get_location().get()], t, dt, parameters); } + /** + * @brief Implementation of Model::get_person. + * This function needs to use a template as otherwise std::is_const would be false. + * @param[in] m Model in which the person_id is searched for. + * @param[in] person_id A Person's PersonId. + * @return A reference to the Person. + */ + template + static std::conditional_t, const Person&, Person&> get_person_impl(M& m, PersonId person_id) + { + if (m.m_person_ids_equal_index) { + return m.m_persons[static_cast(person_id.get())]; + } + else { + mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due " + "to former removal of persons. Therefore m_persons is searched."); + auto it = std::find_if(m.m_persons.begin(), m.m_persons.end(), [person_id](auto& person) { + return person.get_id() == person_id; + }); + if (it == m.m_persons.end()) { + log_error("Given Person is not in this Model."); + } + return *it; + }; + } + mutable Eigen::Matrix m_local_population_cache; ///< Current number of Persons in a given location. Eigen::Matrix From f3632a7056d9d4df53e638766f8e29e9292d76ec Mon Sep 17 00:00:00 2001 From: jubicker Date: Fri, 31 Jan 2025 13:03:03 +0100 Subject: [PATCH 111/111] add assertion to get_person --- cpp/models/abm/model.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 673eda0f79..452210da29 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -598,15 +598,17 @@ class Model /** * @brief Implementation of Model::get_person. - * This function needs to use a template as otherwise std::is_const would be false. - * @param[in] m Model in which the person_id is searched for. + * This function needs to use a template to deduce whether the model and returned person should be const. + * @param[in] m A reference to `*this`, so we can access m_persons. * @param[in] person_id A Person's PersonId. - * @return A reference to the Person. + * @return A reference to the Person with matching ID. */ template static std::conditional_t, const Person&, Person&> get_person_impl(M& m, PersonId person_id) { if (m.m_person_ids_equal_index) { + assert(static_cast(person_id.get()) < m.m_persons.size() && + "Given PersonId is not in this Model."); return m.m_persons[static_cast(person_id.get())]; } else { @@ -615,9 +617,7 @@ class Model auto it = std::find_if(m.m_persons.begin(), m.m_persons.end(), [person_id](auto& person) { return person.get_id() == person_id; }); - if (it == m.m_persons.end()) { - log_error("Given Person is not in this Model."); - } + assert(it != m.m_persons.end() && "Given PersonId is not in this Model."); return *it; }; }