LCOV - code coverage report
Current view: top level - models/abm - model.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 151 154 98.1 %
Date: 2025-02-17 13:46:44 Functions: 31 32 96.9 %

          Line data    Source code
       1             : /* 
       2             : * Copyright (C) 2020-2025 MEmilio
       3             : *
       4             : * Authors: Daniel Abele, Majid Abedi, Elisabeth Kluth, David Kerkmann, Sascha Korf, Martin J. Kuehn, Khoa Nguyen
       5             : *
       6             : * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
       7             : *
       8             : * Licensed under the Apache License, Version 2.0 (the "License");
       9             : * you may not use this file except in compliance with the License.
      10             : * You may obtain a copy of the License at
      11             : *
      12             : *     http://www.apache.org/licenses/LICENSE-2.0
      13             : *
      14             : * Unless required by applicable law or agreed to in writing, software
      15             : * distributed under the License is distributed on an "AS IS" BASIS,
      16             : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      17             : * See the License for the specific language governing permissions and
      18             : * limitations under the License.
      19             : */
      20             : #ifndef MIO_ABM_MODEL_H
      21             : #define MIO_ABM_MODEL_H
      22             : 
      23             : #include "abm/model_functions.h"
      24             : #include "abm/location_type.h"
      25             : #include "abm/mobility_data.h"
      26             : #include "abm/parameters.h"
      27             : #include "abm/location.h"
      28             : #include "abm/person.h"
      29             : #include "abm/person_id.h"
      30             : #include "abm/time.h"
      31             : #include "abm/trip_list.h"
      32             : #include "abm/random_events.h"
      33             : #include "abm/testing_strategy.h"
      34             : #include "memilio/epidemiology/age_group.h"
      35             : #include "memilio/utils/logging.h"
      36             : #include "memilio/utils/random_number_generator.h"
      37             : #include "memilio/utils/stl_util.h"
      38             : 
      39             : #include <bitset>
      40             : #include <cstdint>
      41             : #include <vector>
      42             : 
      43             : namespace mio
      44             : {
      45             : namespace abm
      46             : {
      47             : 
      48             : /**
      49             :  * @brief The Model of the Simulation.
      50             :  * It consists of Location%s and Person%s (Agents).
      51             :  */
      52             : class Model
      53             : {
      54             : public:
      55             :     using LocationIterator        = std::vector<Location>::iterator;
      56             :     using ConstLocationIterator   = std::vector<Location>::const_iterator;
      57             :     using PersonIterator          = std::vector<Person>::iterator;
      58             :     using ConstPersonIterator     = std::vector<Person>::const_iterator;
      59             :     using ActivenessIterator      = std::vector<bool>::iterator;
      60             :     using ConstActivenessIterator = std::vector<bool>::const_iterator;
      61             :     using MobilityRuleType        = LocationType (*)(PersonalRandomNumberGenerator&, const Person&, TimePoint, TimeSpan,
      62             :                                               const Parameters&);
      63             : 
      64             :     /**
      65             :      * @brief Create a Model.
      66             :      * @param[in] num_agegroups The number of AgeGroup%s in the simulated Model. Must be less than MAX_NUM_AGE_GROUPS.
      67             :      */
      68         216 :     Model(size_t num_agegroups, int id = 0)
      69         216 :         : parameters(num_agegroups)
      70         216 :         , m_id(id)
      71         216 :         , m_trip_list()
      72         216 :         , m_use_mobility_rules(true)
      73         216 :         , m_cemetery_id(add_location(LocationType::Cemetery))
      74         864 :         , m_person_ids_equal_index(true)
      75             :     {
      76         216 :         assert(num_agegroups < MAX_NUM_AGE_GROUPS && "MAX_NUM_AGE_GROUPS exceeded.");
      77         216 :     }
      78             : 
      79             :     /**
      80             :      * @brief Create a Model.
      81             :      * @param[in] params Initial simulation parameters.
      82             :      */
      83           9 :     Model(const Parameters& params, int id = 0)
      84           9 :         : parameters(params.get_num_groups())
      85           9 :         , m_id(id)
      86           9 :         , m_trip_list()
      87           9 :         , m_use_mobility_rules(true)
      88           9 :         , m_cemetery_id(add_location(LocationType::Cemetery))
      89          36 :         , m_person_ids_equal_index(true)
      90             :     {
      91           9 :         parameters = params;
      92           9 :     }
      93             : 
      94         141 :     Model(const Model& other, int id = 0)
      95         141 :         : parameters(other.parameters)
      96         141 :         , m_local_population_cache()
      97         141 :         , m_air_exposure_rates_cache()
      98         141 :         , m_contact_exposure_rates_cache()
      99         141 :         , m_is_local_population_cache_valid(false)
     100         141 :         , m_are_exposure_caches_valid(false)
     101         141 :         , m_exposure_caches_need_rebuild(true)
     102         141 :         , m_id(id)
     103         141 :         , m_persons(other.m_persons)
     104         141 :         , m_locations(other.m_locations)
     105         141 :         , m_activeness_statuses(other.m_activeness_statuses)
     106         141 :         , m_has_locations(other.m_has_locations)
     107         141 :         , m_testing_strategy(other.m_testing_strategy)
     108         141 :         , m_trip_list(other.m_trip_list)
     109         141 :         , m_use_mobility_rules(other.m_use_mobility_rules)
     110         141 :         , m_mobility_rules(other.m_mobility_rules)
     111         141 :         , m_cemetery_id(other.m_cemetery_id)
     112         141 :         , m_rng(other.m_rng)
     113         141 :         , m_person_ids_equal_index(true)
     114             :     {
     115         141 :     }
     116             :     Model& operator=(const Model&) = default;
     117          78 :     Model(Model&&)                 = default;
     118             :     Model& operator=(Model&&)      = default;
     119             : 
     120             :     /**
     121             :      * serialize this.
     122             :      * @see mio::serialize
     123             :      */
     124             :     template <class IOContext>
     125           9 :     void serialize(IOContext& io) const
     126             :     {
     127          27 :         auto obj = io.create_object("Model");
     128           9 :         obj.add_element("parameters", parameters);
     129             :         // skip caches, they are rebuild by the deserialized model
     130           9 :         obj.add_list("persons", get_persons().begin(), get_persons().end());
     131           9 :         obj.add_list("locations", get_locations().begin(), get_locations().end());
     132           9 :         obj.add_element("location_types", m_has_locations.to_ulong());
     133           9 :         obj.add_element("testing_strategy", m_testing_strategy);
     134           9 :         obj.add_element("trip_list", m_trip_list);
     135           9 :         obj.add_element("use_mobility_rules", m_use_mobility_rules);
     136           9 :         obj.add_element("cemetery_id", m_cemetery_id);
     137           9 :         obj.add_element("rng", m_rng);
     138          18 :     }
     139             : 
     140             :     /**
     141             :      * deserialize an object of this class.
     142             :      * @see mio::deserialize
     143             :      */
     144             :     template <class IOContext>
     145           9 :     static IOResult<Model> deserialize(IOContext& io)
     146             :     {
     147          27 :         auto obj                = io.expect_object("Model");
     148          27 :         auto params             = obj.expect_element("parameters", Tag<Parameters>{});
     149          27 :         auto persons            = obj.expect_list("persons", Tag<Person>{});
     150          27 :         auto locations          = obj.expect_list("locations", Tag<Location>{});
     151          27 :         auto location_types     = obj.expect_element("location_types", Tag<unsigned long>{});
     152          27 :         auto trip_list          = obj.expect_element("trip_list", Tag<TripList>{});
     153          27 :         auto use_mobility_rules = obj.expect_element("use_mobility_rules", Tag<bool>{});
     154          27 :         auto cemetery_id        = obj.expect_element("cemetery_id", Tag<LocationId>{});
     155          27 :         auto rng                = obj.expect_element("rng", Tag<RandomNumberGenerator>{});
     156             :         return apply(
     157             :             io,
     158           9 :             [](auto&& params_, auto&& persons_, auto&& locations_, auto&& location_types_, auto&& trip_list_,
     159             :                auto&& use_mobility_rules_, auto&& cemetery_id_, auto&& rng_) {
     160           9 :                 Model model{params_};
     161           9 :                 model.m_persons.assign(persons_.cbegin(), persons_.cend());
     162           9 :                 model.m_locations.assign(locations_.cbegin(), locations_.cend());
     163           9 :                 model.m_has_locations      = location_types_;
     164           9 :                 model.m_trip_list          = trip_list_;
     165           9 :                 model.m_use_mobility_rules = use_mobility_rules_;
     166           9 :                 model.m_cemetery_id        = cemetery_id_;
     167           9 :                 model.m_rng                = rng_;
     168          18 :                 return model;
     169           9 :             },
     170          18 :             params, persons, locations, location_types, trip_list, use_mobility_rules, cemetery_id, rng);
     171           9 :     }
     172             : 
     173             :     /** 
     174             :      * @brief Prepare the Model for the next Simulation step.
     175             :      * @param[in] t Current time.
     176             :      * @param[in] dt Length of the time step.
     177             :      */
     178             :     void begin_step(TimePoint t, TimeSpan dt);
     179             : 
     180             :     /** 
     181             :      * @brief Evolve the Model one time step.
     182             :      * @param[in] t Current time.
     183             :      * @param[in] dt Length of the time step.
     184             :      */
     185             :     void evolve(TimePoint t, TimeSpan dt);
     186             : 
     187             :     /** 
     188             :      * @brief Add a Location to the Model.
     189             :      * @param[in] type Type of Location to add.
     190             :      * @param[in] num_cells [Default: 1] Number of Cell%s that the Location is divided into.
     191             :      * @return ID of the newly created Location.
     192             :      */
     193             :     LocationId add_location(LocationType type, uint32_t num_cells = 1);
     194             : 
     195             :     /** 
     196             :      * @brief Add a Person to the Model.
     197             :      * @param[in] id The LocationID of the initial Location of the Person.
     198             :      * @param[in] age AgeGroup of the person.
     199             :      * @return Id of the newly created Person.
     200             :      */
     201             :     PersonId add_person(const LocationId id, AgeGroup age);
     202             : 
     203             :     /**
     204             :      * @brief Adds a copy of a given Person to the Model.
     205             :      * @param[in] person The Person to copy from. 
     206             :      * @return Id of the newly created Person.
     207             :      */
     208             :     PersonId add_person(Person&& person);
     209             : 
     210             :     /**
     211             :      * @brief Get a range of all Location%s in the Model.
     212             :      * @return A range of all Location%s.
     213             :      * @{
     214             :      */
     215             :     Range<std::pair<ConstLocationIterator, ConstLocationIterator>> get_locations() const;
     216             :     Range<std::pair<LocationIterator, LocationIterator>> get_locations();
     217             :     /** @} */
     218             : 
     219             :     /**
     220             :      * @brief Get a range of all Person%s in the Model.
     221             :      * @return A range of all Person%s.
     222             :      * @{
     223             :      */
     224             :     Range<std::pair<ConstPersonIterator, ConstPersonIterator>> get_persons() const;
     225             :     Range<std::pair<PersonIterator, PersonIterator>> get_persons();
     226             :     /** @} */
     227             : 
     228             :     /**
     229             :      * @brief Get a range of all Person%s activeness statuses in the Model.
     230             :      * @return A range of all Person%s activeness statuses.
     231             :      * @{
     232             :      */
     233             :     Range<std::pair<ConstActivenessIterator, ConstActivenessIterator>> get_activeness_statuses() const;
     234             :     Range<std::pair<ActivenessIterator, ActivenessIterator>> get_activeness_statuses();
     235             :     /** @} */
     236             : 
     237             :     /**
     238             :      * @brief Find an assigned Location of a Person.
     239             :      * @param[in] type The #LocationType that specifies the assigned Location.
     240             :      * @param[in] person Id of the Person.
     241             :      * @return ID of the Location of LocationType type assigend to person.
     242             :      */
     243             :     LocationId find_location(LocationType type, const PersonId person) const;
     244             : 
     245             :     /**
     246             :      * @brief Assign a Location to a Person.
     247             :      * A Person can have at most one assigned Location of a certain LocationType.
     248             :      * Assigning another Location of an already assigned LocationType will replace the prior assignment.  
     249             :      * @param[in] person The Id of the person this location will be assigned to.
     250             :      * @param[in] location The LocationId of the Location.
     251             :      */
     252        1098 :     void assign_location(PersonId person, LocationId location)
     253             :     {
     254        1098 :         assign_location(get_person(person), location);
     255        1098 :     }
     256             : 
     257             :     /**
     258             :      * @brief Get the number of Persons in one #InfectionState at all Location%s.
     259             :      * @param[in] t Specified #TimePoint.
     260             :      * @param[in] s Specified #InfectionState.
     261             :      */
     262             :     size_t get_subpopulation_combined(TimePoint t, InfectionState s) const;
     263             : 
     264             :     /**
     265             :      * @brief Get the number of Persons in one #InfectionState at all Location%s of a type.
     266             :      * @param[in] t Specified #TimePoint.
     267             :      * @param[in] s Specified #InfectionState.
     268             :      * @param[in] type Specified #LocationType.
     269             :      */
     270             :     size_t get_subpopulation_combined_per_location_type(TimePoint t, InfectionState s, LocationType type) const;
     271             : 
     272             :     /**
     273             :      * @brief Get the mobility data.
     274             :      * @return Reference to the list of Trip%s that the Person%s make.
     275             :      */
     276             :     TripList& get_trip_list();
     277             : 
     278             :     const TripList& get_trip_list() const;
     279             : 
     280             :     /**
     281             :      * @brief Decide if mobility rules (like go to school/work) are used or not;
     282             :      * The mobility rules regarding hospitalization/ICU/quarantine are always used.
     283             :      * @param[in] param If true uses the mobility rules for changing location to school/work etc., else only the rules
     284             :      * regarding hospitalization/ICU/quarantine.
     285             :      */
     286             :     void use_mobility_rules(bool param);
     287             :     bool use_mobility_rules() const;
     288             : 
     289             :     /**
     290             :     * @brief Check if at least one Location with a specified LocationType exists.
     291             :     * @return True if there is at least one Location of LocationType `type`. False otherwise.
     292             :     */
     293       28980 :     bool has_location(LocationType type) const
     294             :     {
     295       28980 :         return m_has_locations[size_t(type)];
     296             :     }
     297             : 
     298             :     /**
     299             :     * @brief Check if at least one Location of every specified LocationType exists.
     300             :     * @tparam C A type of container of LocationType.
     301             :     * @param location_types A container of LocationType%s.
     302             :     * @return True if there is at least one Location of every LocationType in `location_types`. False otherwise.
     303             :     */
     304             :     template <class C = std::initializer_list<LocationType>>
     305       24219 :     bool has_locations(const C& location_types) const
     306             :     {
     307       82179 :         return std::all_of(location_types.begin(), location_types.end(), [&](auto loc) {
     308       57960 :             return has_location(loc);
     309       24219 :         });
     310             :     }
     311             : 
     312             :     /**
     313             :      * @brief Get the TestingStrategy.
     314             :      * @return Reference to the list of TestingScheme%s that are checked for testing.
     315             :      */
     316             :     TestingStrategy& get_testing_strategy();
     317             : 
     318             :     const TestingStrategy& get_testing_strategy() const;
     319             : 
     320             :     /** 
     321             :      * @brief The simulation parameters of the Model.
     322             :      */
     323             :     Parameters parameters;
     324             : 
     325             :     /**
     326             :     * Get the RandomNumberGenerator used by this Model for random events.
     327             :     * Persons use their own generators with the same key as the global one. 
     328             :     * @return The random number generator.
     329             :     */
     330        1296 :     RandomNumberGenerator& get_rng()
     331             :     {
     332        1296 :         return m_rng;
     333             :     }
     334             : 
     335             :     /**
     336             :      * Get the model id. Is only relevant for graph abm or hybrid model.
     337             :      * @return The model id
     338             :      */
     339         550 :     int get_id() const
     340             :     {
     341         550 :         return m_id;
     342             :     }
     343             : 
     344             :     /**
     345             :      * @brief Add a TestingScheme to the set of schemes that are checked for testing at all Locations that have
     346             :      * the LocationType.
     347             :      * @param[in] loc_type LocationId key for TestingScheme to be added.
     348             :      * @param[in] scheme TestingScheme to be added.
     349             :      */
     350             :     void add_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme);
     351             : 
     352             :     /**
     353             :      * @brief Remove a TestingScheme from the set of schemes that are checked for testing at all Locations that have
     354             :      * the LocationType.
     355             :      * @param[in] loc_type LocationId key for TestingScheme to be added.
     356             :      * @param[in] scheme TestingScheme to be added.
     357             :      */
     358             :     void remove_testing_scheme(const LocationType& loc_type, const TestingScheme& scheme);
     359             : 
     360             :     /**
     361             :      * @brief Get a reference to a Person from this Model.
     362             :      * @param[in] person_id A Person's PersonId.
     363             :      * @return A reference to the Person.
     364             :      */
     365          81 :     const Person& get_person(PersonId person_id) const
     366             :     {
     367          81 :         return get_person_impl(*this, person_id);
     368             :     }
     369             : 
     370        1643 :     Person& get_person(PersonId person_id)
     371             :     {
     372        1643 :         return get_person_impl(*this, person_id);
     373             :     }
     374             : 
     375             :     /**
     376             :      * @brief Get the number of Person%s of a particular #InfectionState for all Cell%s.
     377             :      * @param[in] location A LocationId from the Model.
     378             :      * @param[in] t TimePoint of querry.
     379             :      * @param[in] state #InfectionState of interest.
     380             :      * @return Amount of Person%s of the #InfectionState in all Cell%s of the Location.
     381             :      */
     382       27315 :     size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const
     383             :     {
     384       27315 :         return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) {
     385      320238 :             return p.get_location_model_id() == m_id && p.get_location() == location &&
     386      226980 :                    p.get_infection_state(t) == state;
     387       27315 :         });
     388             :     }
     389             : 
     390             :     /**
     391             :      * @brief Get the total number of Person%s at the Location.
     392             :      * @param[in] location A LocationId from the Model.
     393             :      * @return Number of Person%s in the location.
     394             :      */
     395         290 :     size_t get_number_persons(LocationId location) const
     396             :     {
     397         290 :         if (!m_is_local_population_cache_valid) {
     398           0 :             build_compute_local_population_cache();
     399             :         }
     400         290 :         return m_local_population_cache[location.get()];
     401             :     }
     402             : 
     403             :     // Change the Location of a Person. this requires that Location is part of this Model.
     404             :     /**
     405             :      * @brief Let a Person change to another Location.
     406             :      * @param[in] person Id of a person from this Model.
     407             :      * @param[in] destination LocationId of the Location in this Model, which the Person should change to.
     408             :      * @param[in] mode The transport mode the person uses to change the Location.
     409             :      * @param[in] cells The cells within the destination the person should be in.
     410             :      */
     411          36 :     inline void change_location(PersonId person, LocationId destination, TransportMode mode = TransportMode::Unknown,
     412             :                                 const std::vector<uint32_t>& cells = {0})
     413             :     {
     414          36 :         change_location(get_person(person), destination, mode, cells);
     415          36 :     }
     416             : 
     417             :     /**
     418             :      * @brief Let a person interact with the population at its current location.
     419             :      * @param[in] person Id of a person from this Model.
     420             :      * @param[in] t Time step of the simulation.
     421             :      * @param[in] dt Step size of the simulation.
     422             :      */
     423             :     inline void interact(PersonId person, TimePoint t, TimeSpan dt)
     424             :     {
     425             :         interact(get_person(person), t, dt);
     426             :     }
     427             : 
     428             :     /**
     429             :      * @brief Get a reference to a location in this Model.
     430             :      * @param[in] id LocationId of the Location.
     431             :      * @return Reference to the Location.
     432             :      * @{
     433             :      */
     434             :     const Location& get_location(LocationId id) const
     435             :     {
     436             :         assert(id != LocationId::invalid_id() && "Given LocationId must be valid.");
     437             :         assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model.");
     438             :         return m_locations[id.get()];
     439             :     }
     440             : 
     441             :     /**
     442             :      * @brief Assign a Location to a Person.
     443             :      * A Person can have at most one assigned Location of a certain LocationType.
     444             :      * Assigning another Location of an already assigned LocationType will replace the prior assignment.  
     445             :      * @param[in] person reference to the Person the location will be assigned to.
     446             :      * @param[in] location The LocationId of the Location.
     447             :      */
     448        1098 :     void assign_location(Person& person, LocationId location)
     449             :     {
     450        1098 :         person.set_assigned_location(get_location(location).get_type(), location, m_id);
     451        1098 :     }
     452             : 
     453       18296 :     Location& get_location(LocationId id)
     454             :     {
     455       18296 :         assert(id != LocationId::invalid_id() && "Given LocationId must be valid.");
     456       18296 :         assert(id < LocationId((uint32_t)m_locations.size()) && "Given LocationId is not in this Model.");
     457       18296 :         return m_locations[id.get()];
     458             :     }
     459             :     /** @} */
     460             : 
     461             :     /**
     462             :      * @brief Get a reference to the location of a person.
     463             :      * @param[in] id Id of a person.
     464             :      * @return Reference to the Location.
     465             :      * @{
     466             :      */
     467          63 :     inline Location& get_location(PersonId id)
     468             :     {
     469          63 :         return get_location(get_person(id).get_location());
     470             :     }
     471             : 
     472             :     inline const Location& get_location(PersonId id) const
     473             :     {
     474             :         return get_location(get_person(id).get_location());
     475             :     }
     476             :     /** @} */
     477             : 
     478             :     /**
     479             :      * @brief Get index of person in m_persons.
     480             :      * @param[in] person_id A person's unique PersonId. 
     481             :      * First 32 bit are the Person's individual id and second 32 bit the Persons's home model id. 
     482             :      * @return Index of Person in m_persons vector.
     483             :      * @{
     484             :      */
     485          10 :     uint32_t get_person_index(PersonId person_id) const
     486             :     {
     487          10 :         mio::log_debug("get_person_index is used leading to a search in m_persons.");
     488          10 :         auto it = std::find_if(m_persons.begin(), m_persons.end(), [person_id](auto& person) {
     489          25 :             return person.get_id() == person_id;
     490             :         });
     491          10 :         if (it == m_persons.end()) {
     492           1 :             log_error("Given PersonId is not in this Model.");
     493           1 :             return std::numeric_limits<uint32_t>::max();
     494             :         }
     495             :         else {
     496           9 :             return static_cast<uint32_t>(std::distance(m_persons.begin(), it));
     497             :         }
     498             :     }
     499             : 
     500             : protected:
     501             :     /**
     502             :      * @brief Person%s interact at their Location and may become infected.
     503             :      * @param[in] t The current TimePoint.
     504             :      * @param[in] dt The length of the time step of the Simulation.
     505             :      */
     506             :     void interaction(TimePoint t, TimeSpan dt);
     507             :     /**
     508             :      * @brief Person%s change location in the Model according to rules.
     509             :      * @param[in] t The current TimePoint.
     510             :      * @param[in] dt The length of the time step of the Simulation.
     511             :      */
     512             :     void perform_mobility(TimePoint t, TimeSpan dt);
     513             : 
     514             :     /// @brief Shape the cache and store how many Person%s are at any Location. Use from single thread!
     515             :     void build_compute_local_population_cache() const;
     516             : 
     517             :     /// @brief Shape the air and contact exposure cache according to the current Location%s.
     518             :     void build_exposure_caches();
     519             : 
     520             :     /**
     521             :      * @brief Store all air/contact exposures for the current simulation step.
     522             :      * @param[in] t Current TimePoint of the simulation.
     523             :      * @param[in] dt The duration of the simulation step.
     524             :      */
     525             :     void compute_exposure_caches(TimePoint t, TimeSpan dt);
     526             : 
     527             :     // Change the Location of a Person. this requires that Location is part of this Model.
     528             :     /**
     529             :      * @brief Let a Person change to another Location.
     530             :      * @param[in] person Reference to Person.
     531             :      * @param[in] destination LocationId of the Location in this Model, which the Person should change to.
     532             :      * @param[in] mode The transport mode the person uses to change the Location.
     533             :      * @param[in] cells The cells within the destination the person should be in.
     534             :      */
     535         337 :     inline void change_location(Person& person, LocationId destination, TransportMode mode = TransportMode::Unknown,
     536             :                                 const std::vector<uint32_t>& cells = {0})
     537             :     {
     538         337 :         LocationId origin               = get_location(person).get_id();
     539         337 :         const bool has_changed_location = mio::abm::change_location(person, get_location(destination), mode, cells);
     540             :         // if the person has changed location, invalidate exposure caches but keep population caches valid
     541         337 :         if (has_changed_location) {
     542         328 :             m_are_exposure_caches_valid = false;
     543         328 :             if (m_is_local_population_cache_valid) {
     544         328 :                 --m_local_population_cache[origin.get()];
     545         328 :                 ++m_local_population_cache[destination.get()];
     546             :             }
     547             :         }
     548         337 :     }
     549             : 
     550             :     /**
     551             :      * @brief Get a reference to the location of a person.
     552             :      * @param[in] person Reference to a Person.
     553             :      * @return Reference to the Location.
     554             :      * @{
     555             :      */
     556         337 :     inline Location& get_location(Person& person)
     557             :     {
     558         337 :         return get_location(person.get_location());
     559             :     }
     560             : 
     561             :     inline const Location& get_location(Person& person) const
     562             :     {
     563             :         return get_location(person.get_location());
     564             :     }
     565             : 
     566             :     /**
     567             :      * @brief Find an assigned Location of a Person.
     568             :      * @param[in] type The #LocationType that specifies the assigned Location.
     569             :      * @param[in] person Reference to Person.
     570             :      * @return ID of the Location of LocationType type assigend to person.
     571             :      */
     572        9094 :     LocationId find_location(LocationType type, const Person& person) const
     573             :     {
     574        9094 :         auto location_id = person.get_assigned_location(type);
     575        9094 :         assert(location_id != LocationId::invalid_id() && "The person has no assigned location of that type.");
     576       18188 :         return location_id;
     577             :     }
     578             : 
     579             :     /**
     580             :      * @brief Let a person interact with the population at its current location.
     581             :      * @param[in] person Reference to Person.
     582             :      * @param[in] t Time step of the simulation.
     583             :      * @param[in] dt Step size of the simulation.
     584             :      */
     585        2853 :     inline void interact(Person& person, TimePoint t, TimeSpan dt)
     586             :     {
     587        2853 :         if (!m_are_exposure_caches_valid) {
     588             :             // checking caches is only needed for external calls
     589             :             // during simulation (i.e. in evolve()), the caches are computed in begin_step
     590           0 :             compute_exposure_caches(t, dt);
     591           0 :             m_are_exposure_caches_valid = true;
     592             :         }
     593        2853 :         auto personal_rng = PersonalRandomNumberGenerator(person);
     594        8559 :         mio::abm::interact(personal_rng, person, get_location(person.get_location()),
     595        5706 :                            m_air_exposure_rates_cache[person.get_location().get()],
     596        8559 :                            m_contact_exposure_rates_cache[person.get_location().get()], t, dt, parameters);
     597        2853 :     }
     598             : 
     599             :     /**
     600             :      * @brief Implementation of Model::get_person. 
     601             :      * This function needs to use a template to deduce whether the model and returned person should be const.
     602             :      * @param[in] m A reference to `*this`, so we can access m_persons.
     603             :      * @param[in] person_id A Person's PersonId.
     604             :      * @return A reference to the Person with matching ID.
     605             :      */
     606             :     template <class M>
     607        1724 :     static std::conditional_t<std::is_const_v<M>, const Person&, Person&> get_person_impl(M& m, PersonId person_id)
     608             :     {
     609        1724 :         if (m.m_person_ids_equal_index) {
     610        1723 :             assert(static_cast<uint32_t>(person_id.get()) < m.m_persons.size() &&
     611             :                    "Given PersonId is not in this Model.");
     612        1723 :             return m.m_persons[static_cast<uint32_t>(person_id.get())];
     613             :         }
     614             :         else {
     615           1 :             mio::log_warning("get_person is accessed by PersonId which does not align with the index of the person due "
     616             :                              "to former removal of persons. Therefore m_persons is searched.");
     617           1 :             auto it = std::find_if(m.m_persons.begin(), m.m_persons.end(), [person_id](auto& person) {
     618           1 :                 return person.get_id() == person_id;
     619             :             });
     620           1 :             assert(it != m.m_persons.end() && "Given PersonId is not in this Model.");
     621           1 :             return *it;
     622             :         };
     623             :     }
     624             : 
     625             :     mutable Eigen::Matrix<std::atomic_int_fast32_t, Eigen::Dynamic, 1>
     626             :         m_local_population_cache; ///< Current number of Persons in a given location.
     627             :     Eigen::Matrix<AirExposureRates, Eigen::Dynamic, 1>
     628             :         m_air_exposure_rates_cache; ///< Cache for local exposure through droplets in #transmissions/day.
     629             :     Eigen::Matrix<ContactExposureRates, Eigen::Dynamic, 1>
     630             :         m_contact_exposure_rates_cache; ///< Cache for local exposure through contacts in #transmissions/day.
     631             :     bool m_is_local_population_cache_valid = false;
     632             :     bool m_are_exposure_caches_valid       = false;
     633             :     bool m_exposure_caches_need_rebuild    = true;
     634             : 
     635             :     int m_id; ///< Model id. Is only used for abm graph model or hybrid model.
     636             :     std::vector<Person> m_persons; ///< Vector of every Person.
     637             :     std::vector<Location> m_locations; ///< Vector of every Location.
     638             :     std::vector<bool>
     639             :         m_activeness_statuses; ///< Vector with activeness status for every person. Is only used for abm graph model or hybrid model.
     640             :     std::bitset<size_t(LocationType::Count)>
     641             :         m_has_locations; ///< Flags for each LocationType, set if a Location of that type exists.
     642             :     TestingStrategy m_testing_strategy; ///< List of TestingScheme%s that are checked for testing.
     643             :     TripList m_trip_list; ///< List of all Trip%s the Person%s do.
     644             :     bool m_use_mobility_rules; ///< Whether mobility rules are considered.
     645             :     std::vector<MobilityRuleType> m_mobility_rules; ///< Rules that govern the mobility between Location%s.
     646             :     LocationId m_cemetery_id; // Central cemetery for all dead persons.
     647             :     RandomNumberGenerator m_rng; ///< Global random number generator
     648             :     bool m_person_ids_equal_index;
     649             : };
     650             : 
     651             : } // namespace abm
     652             : } // namespace mio
     653             : 
     654             : #endif

Generated by: LCOV version 1.14