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