Line data Source code
1 : /*
2 : * Copyright (C) 2020-2025 MEmilio
3 : *
4 : * Authors: Daniel Abele, Elisabeth Kluth, David Kerkmann, 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 : #include "abm/person.h"
21 : #include "abm/location_type.h"
22 : #include "abm/mask_type.h"
23 : #include "abm/parameters.h"
24 : #include "abm/infection.h"
25 : #include "abm/location.h"
26 : #include "abm/person_id.h"
27 : #include "memilio/utils/random_number_generator.h"
28 : #include <cstdint>
29 : #include <vector>
30 :
31 : namespace mio
32 : {
33 : namespace abm
34 : {
35 :
36 1954 : Person::Person(mio::RandomNumberGenerator& rng, LocationType location_type, LocationId location_id,
37 1954 : int location_model_id, AgeGroup age, PersonId person_id)
38 1954 : : m_location(location_id)
39 1954 : , m_location_type(location_type)
40 1954 : , m_location_model_id(location_model_id)
41 1954 : , m_assigned_locations((uint32_t)LocationType::Count, LocationId::invalid_id())
42 1954 : , m_home_isolation_start(TimePoint(-(std::numeric_limits<int>::max() / 2)))
43 1954 : , m_age(age)
44 1954 : , m_time_at_location(0)
45 1954 : , m_mask(Mask(MaskType::None, TimePoint(-(std::numeric_limits<int>::max() / 2))))
46 1954 : , m_compliance((uint32_t)InterventionType::Count, 1.)
47 1954 : , m_cells{0}
48 1954 : , m_last_transport_mode(TransportMode::Unknown)
49 1954 : , m_test_results({TestType::Count}, TestResult())
50 1954 : , m_assigned_location_model_ids((int)LocationType::Count)
51 1954 : , m_person_id(person_id)
52 1954 : , m_rng_key(rng.get_key())
53 3908 : , m_rng_index(static_cast<uint32_t>(person_id.get()))
54 : {
55 1954 : m_random_workgroup = UniformDistribution<double>::get_instance()(rng);
56 1954 : m_random_schoolgroup = UniformDistribution<double>::get_instance()(rng);
57 1954 : m_random_goto_work_hour = UniformDistribution<double>::get_instance()(rng);
58 1954 : m_random_goto_school_hour = UniformDistribution<double>::get_instance()(rng);
59 1954 : }
60 :
61 9 : Person::Person(const Person& other, PersonId person_id)
62 9 : : Person(other)
63 : {
64 9 : m_person_id = person_id;
65 9 : m_rng_index = static_cast<uint32_t>(person_id.get());
66 9 : }
67 :
68 3132 : bool Person::is_infected(TimePoint t) const
69 : {
70 3132 : if (m_infections.empty()) {
71 2088 : return false;
72 : }
73 : // subject to change if Recovered is removed
74 4176 : if (m_infections.back().get_infection_state(t) == InfectionState::Susceptible ||
75 3132 : m_infections.back().get_infection_state(t) == InfectionState::Recovered) {
76 81 : return false;
77 : }
78 963 : return true;
79 : }
80 :
81 29287 : InfectionState Person::get_infection_state(TimePoint t) const
82 : {
83 29287 : if (m_infections.empty()) {
84 18973 : return InfectionState::Susceptible;
85 : }
86 : else {
87 10314 : return m_infections.back().get_infection_state(t);
88 : }
89 : }
90 :
91 441 : void Person::add_new_infection(Infection&& inf)
92 : {
93 441 : m_infections.push_back(std::move(inf));
94 441 : }
95 :
96 125052 : LocationId Person::get_location() const
97 : {
98 125052 : return m_location;
99 : }
100 :
101 371 : void Person::set_location(LocationType type, LocationId id, int model_id)
102 : {
103 371 : m_location = id;
104 371 : m_location_type = type;
105 371 : m_location_model_id = model_id;
106 371 : m_time_at_location = TimeSpan(0);
107 371 : }
108 :
109 855 : const Infection& Person::get_infection() const
110 : {
111 855 : return m_infections.back();
112 : }
113 :
114 0 : Infection& Person::get_infection()
115 : {
116 0 : return m_infections.back();
117 : }
118 :
119 4088 : void Person::set_assigned_location(LocationType type, LocationId id, int model_id = 0)
120 : {
121 4088 : m_assigned_locations[static_cast<uint32_t>(type)] = id;
122 4088 : m_assigned_location_model_ids[static_cast<uint32_t>(type)] = model_id;
123 4088 : }
124 :
125 10033 : LocationId Person::get_assigned_location(LocationType type) const
126 : {
127 10033 : return m_assigned_locations[static_cast<uint32_t>(type)];
128 : }
129 :
130 9020 : int Person::get_assigned_location_model_id(LocationType type) const
131 : {
132 9020 : return m_assigned_location_model_ids[static_cast<uint32_t>(type)];
133 : }
134 :
135 105 : bool Person::goes_to_work(TimePoint t, const Parameters& params) const
136 : {
137 105 : return m_random_workgroup < params.get<WorkRatio>().get_matrix_at(t.days())[0];
138 : }
139 :
140 348 : TimeSpan Person::get_go_to_work_time(const Parameters& params) const
141 : {
142 348 : TimeSpan minimum_goto_work_time = params.get<GotoWorkTimeMinimum>()[m_age];
143 348 : TimeSpan maximum_goto_work_time = params.get<GotoWorkTimeMaximum>()[m_age];
144 348 : int timeSlots = (maximum_goto_work_time.seconds() - minimum_goto_work_time.seconds());
145 348 : int seconds_after_minimum = int(timeSlots * m_random_goto_work_hour);
146 696 : return minimum_goto_work_time + seconds(seconds_after_minimum);
147 : }
148 :
149 871 : TimeSpan Person::get_go_to_school_time(const Parameters& params) const
150 : {
151 871 : TimeSpan minimum_goto_school_time = params.get<GotoSchoolTimeMinimum>()[m_age];
152 871 : TimeSpan maximum_goto_school_time = params.get<GotoSchoolTimeMaximum>()[m_age];
153 871 : int timeSlots = (maximum_goto_school_time.seconds() - minimum_goto_school_time.seconds());
154 871 : int seconds_after_minimum = int(timeSlots * m_random_goto_school_hour);
155 1742 : return minimum_goto_school_time + seconds(seconds_after_minimum);
156 : }
157 :
158 99 : bool Person::goes_to_school(TimePoint t, const Parameters& params) const
159 : {
160 99 : return m_random_schoolgroup < params.get<SchoolRatio>().get_matrix_at(t.days())[0];
161 : }
162 :
163 18 : void Person::remove_quarantine()
164 : {
165 18 : m_home_isolation_start = TimePoint(-(std::numeric_limits<int>::max() / 2));
166 18 : }
167 :
168 162 : bool Person::get_tested(PersonalRandomNumberGenerator& rng, TimePoint t, const TestParameters& params)
169 : {
170 162 : ScalarType random = UniformDistribution<double>::get_instance()(rng);
171 162 : if (is_infected(t)) {
172 : // true positive
173 108 : if (random < params.sensitivity) {
174 : // If the Person complies to isolation, start the quarantine.
175 90 : if (is_compliant(rng, InterventionType::Isolation)) {
176 72 : m_home_isolation_start = t;
177 : }
178 90 : m_infections.back().set_detected();
179 90 : return true;
180 : }
181 : // false negative
182 : else {
183 18 : return false;
184 : }
185 : }
186 : else {
187 : // true negative
188 54 : if (random < params.specificity) {
189 36 : return false;
190 : }
191 : // false positive
192 : else {
193 : // If the Person complies to isolation, start the quarantine.
194 18 : if (is_compliant(rng, InterventionType::Isolation)) {
195 18 : m_home_isolation_start = t;
196 : }
197 18 : return true;
198 : }
199 : }
200 : }
201 :
202 4339 : PersonId Person::get_id() const
203 : {
204 4339 : return m_person_id;
205 : }
206 :
207 10318 : std::vector<uint32_t>& Person::get_cells()
208 : {
209 10318 : return m_cells;
210 : }
211 :
212 855 : const std::vector<uint32_t>& Person::get_cells() const
213 : {
214 855 : return m_cells;
215 : }
216 :
217 2088 : ScalarType Person::get_mask_protective_factor(const Parameters& params) const
218 : {
219 2088 : return params.get<MaskProtection>()[m_mask.get_type()];
220 : }
221 :
222 692 : bool Person::is_compliant(PersonalRandomNumberGenerator& rng, InterventionType intervention) const
223 : {
224 692 : ScalarType compliance_check = UniformDistribution<double>::get_instance()(rng);
225 692 : return compliance_check <= get_compliance(intervention);
226 : }
227 :
228 2142 : ProtectionEvent Person::get_latest_protection() const
229 : {
230 2142 : ProtectionType latest_protection_type = ProtectionType::NoProtection;
231 2142 : TimePoint infection_time = TimePoint(0);
232 2142 : if (!m_infections.empty()) {
233 9 : latest_protection_type = ProtectionType::NaturalInfection;
234 9 : infection_time = m_infections.back().get_start_date();
235 : }
236 2142 : if (!m_vaccinations.empty() && infection_time.days() <= m_vaccinations.back().time.days()) {
237 54 : latest_protection_type = m_vaccinations.back().type;
238 54 : infection_time = m_vaccinations.back().time;
239 : }
240 4284 : return ProtectionEvent{latest_protection_type, infection_time};
241 : }
242 :
243 2088 : ScalarType Person::get_protection_factor(TimePoint t, VirusVariant virus, const Parameters& params) const
244 : {
245 2088 : auto latest_protection = get_latest_protection();
246 : // If there is no previous protection or vaccination, return 0.
247 2088 : if (latest_protection.type == ProtectionType::NoProtection) {
248 2052 : return 0;
249 : }
250 108 : return params.get<InfectionProtectionFactor>()[{latest_protection.type, m_age, virus}](
251 72 : t.days() - latest_protection.time.days());
252 : }
253 :
254 373 : void Person::set_mask(MaskType type, TimePoint t)
255 : {
256 373 : m_mask.change_mask(type, t);
257 373 : }
258 :
259 72 : void Person::add_test_result(TimePoint t, TestType type, bool result)
260 : {
261 : // Remove outdated test results or replace the old result of the same type
262 72 : m_test_results[{type}] = {t, result};
263 72 : }
264 :
265 171 : TestResult Person::get_test_result(TestType type) const
266 : {
267 171 : return m_test_results[{type}];
268 : }
269 :
270 : } // namespace abm
271 : } // namespace mio
|