Line data Source code
1 : /*
2 : * Copyright (C) 2020-2024 MEmilio
3 : *
4 : * Authors: Wadim Koslow, Daniel Abele, Martin J. Kühn
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 ODESECIRVVS_MODEL_H
21 : #define ODESECIRVVS_MODEL_H
22 :
23 : #include "memilio/compartments/flow_model.h"
24 : #include "memilio/compartments/simulation.h"
25 : #include "memilio/compartments/flow_simulation.h"
26 : #include "memilio/epidemiology/populations.h"
27 : #include "ode_secirvvs/analyze_result.h"
28 : #include "ode_secirvvs/infection_state.h"
29 : #include "ode_secirvvs/parameters.h"
30 : #include "memilio/math/smoother.h"
31 : #include "memilio/math/eigen_util.h"
32 :
33 : namespace mio
34 : {
35 : namespace osecirvvs
36 : {
37 : // clang-format off
38 : using Flows = TypeList<
39 : //naive
40 : Flow<InfectionState::SusceptibleNaive, InfectionState::ExposedNaive>,
41 : Flow<InfectionState::ExposedNaive, InfectionState::InfectedNoSymptomsNaive>,
42 : Flow<InfectionState::InfectedNoSymptomsNaive, InfectionState::InfectedSymptomsNaive>,
43 : Flow<InfectionState::InfectedNoSymptomsNaive, InfectionState::SusceptibleImprovedImmunity>,
44 : Flow<InfectionState::InfectedNoSymptomsNaiveConfirmed, InfectionState::InfectedSymptomsNaiveConfirmed>,
45 : Flow<InfectionState::InfectedNoSymptomsNaiveConfirmed, InfectionState::SusceptibleImprovedImmunity>,
46 : Flow<InfectionState::InfectedSymptomsNaive, InfectionState::InfectedSevereNaive>,
47 : Flow<InfectionState::InfectedSymptomsNaive, InfectionState::SusceptibleImprovedImmunity>,
48 : Flow<InfectionState::InfectedSymptomsNaiveConfirmed, InfectionState::InfectedSevereNaive>,
49 : Flow<InfectionState::InfectedSymptomsNaiveConfirmed, InfectionState::SusceptibleImprovedImmunity>,
50 : Flow<InfectionState::InfectedSevereNaive, InfectionState::InfectedCriticalNaive>,
51 : Flow<InfectionState::InfectedSevereNaive, InfectionState::SusceptibleImprovedImmunity>,
52 : Flow<InfectionState::InfectedSevereNaive, InfectionState::DeadNaive>,
53 : Flow<InfectionState::InfectedCriticalNaive, InfectionState::DeadNaive>,
54 : Flow<InfectionState::InfectedCriticalNaive, InfectionState::SusceptibleImprovedImmunity>,
55 : //partial immunity
56 : Flow<InfectionState::SusceptiblePartialImmunity, InfectionState::ExposedPartialImmunity>,
57 : Flow<InfectionState::ExposedPartialImmunity, InfectionState::InfectedNoSymptomsPartialImmunity>,
58 : Flow<InfectionState::InfectedNoSymptomsPartialImmunity, InfectionState::InfectedSymptomsPartialImmunity>,
59 : Flow<InfectionState::InfectedNoSymptomsPartialImmunity, InfectionState::SusceptibleImprovedImmunity>,
60 : Flow<InfectionState::InfectedNoSymptomsPartialImmunityConfirmed, InfectionState::InfectedSymptomsPartialImmunityConfirmed>,
61 : Flow<InfectionState::InfectedNoSymptomsPartialImmunityConfirmed, InfectionState::SusceptibleImprovedImmunity>,
62 : Flow<InfectionState::InfectedSymptomsPartialImmunity, InfectionState::InfectedSeverePartialImmunity>,
63 : Flow<InfectionState::InfectedSymptomsPartialImmunity, InfectionState::SusceptibleImprovedImmunity>,
64 : Flow<InfectionState::InfectedSymptomsPartialImmunityConfirmed, InfectionState::InfectedSeverePartialImmunity>,
65 : Flow<InfectionState::InfectedSymptomsPartialImmunityConfirmed, InfectionState::SusceptibleImprovedImmunity>,
66 : Flow<InfectionState::InfectedSeverePartialImmunity, InfectionState::InfectedCriticalPartialImmunity>,
67 : Flow<InfectionState::InfectedSeverePartialImmunity, InfectionState::SusceptibleImprovedImmunity>,
68 : Flow<InfectionState::InfectedSeverePartialImmunity, InfectionState::DeadPartialImmunity>,
69 : Flow<InfectionState::InfectedCriticalPartialImmunity, InfectionState::DeadPartialImmunity>,
70 : Flow<InfectionState::InfectedCriticalPartialImmunity, InfectionState::SusceptibleImprovedImmunity>,
71 : //improved immunity
72 : Flow<InfectionState::SusceptibleImprovedImmunity, InfectionState::ExposedImprovedImmunity>,
73 : Flow<InfectionState::ExposedImprovedImmunity, InfectionState::InfectedNoSymptomsImprovedImmunity>,
74 : Flow<InfectionState::InfectedNoSymptomsImprovedImmunity, InfectionState::InfectedSymptomsImprovedImmunity>,
75 : Flow<InfectionState::InfectedNoSymptomsImprovedImmunity, InfectionState::SusceptibleImprovedImmunity>,
76 : Flow<InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed, InfectionState::InfectedSymptomsImprovedImmunityConfirmed>,
77 : Flow<InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed, InfectionState::SusceptibleImprovedImmunity>,
78 : Flow<InfectionState::InfectedSymptomsImprovedImmunity, InfectionState::InfectedSevereImprovedImmunity>,
79 : Flow<InfectionState::InfectedSymptomsImprovedImmunity, InfectionState::SusceptibleImprovedImmunity>,
80 : Flow<InfectionState::InfectedSymptomsImprovedImmunityConfirmed, InfectionState::InfectedSevereImprovedImmunity>,
81 : Flow<InfectionState::InfectedSymptomsImprovedImmunityConfirmed, InfectionState::SusceptibleImprovedImmunity>,
82 : Flow<InfectionState::InfectedSevereImprovedImmunity, InfectionState::InfectedCriticalImprovedImmunity>,
83 : Flow<InfectionState::InfectedSevereImprovedImmunity, InfectionState::SusceptibleImprovedImmunity>,
84 : Flow<InfectionState::InfectedSevereImprovedImmunity, InfectionState::DeadImprovedImmunity>,
85 : Flow<InfectionState::InfectedCriticalImprovedImmunity, InfectionState::DeadImprovedImmunity>,
86 : Flow<InfectionState::InfectedCriticalImprovedImmunity, InfectionState::SusceptibleImprovedImmunity>>;
87 : // clang-format on
88 :
89 : template <typename FP = ScalarType>
90 : class Model
91 : : public FlowModel<FP, InfectionState, mio::Populations<FP, AgeGroup, InfectionState>, Parameters<FP>, Flows>
92 : {
93 : using Base = FlowModel<FP, InfectionState, mio::Populations<FP, AgeGroup, InfectionState>, Parameters<FP>, Flows>;
94 :
95 : public:
96 : using typename Base::ParameterSet;
97 : using typename Base::Populations;
98 :
99 267 : Model(const Populations& pop, const ParameterSet& params)
100 267 : : Base(pop, params)
101 : {
102 267 : }
103 :
104 266 : Model(int num_agegroups)
105 266 : : Model(Populations({AgeGroup(num_agegroups), InfectionState::Count}), ParameterSet(AgeGroup(num_agegroups)))
106 : {
107 266 : }
108 :
109 6921 : void get_flows(Eigen::Ref<const Vector<FP>> pop, Eigen::Ref<const Vector<FP>> y, FP t,
110 : Eigen::Ref<Vector<FP>> flows) const override
111 : {
112 6921 : auto const& params = this->parameters;
113 6921 : AgeGroup n_agegroups = params.get_num_groups();
114 :
115 6921 : ContactMatrixGroup const& contact_matrix = params.template get<ContactPatterns<FP>>();
116 :
117 6921 : auto icu_occupancy = 0.0;
118 6921 : auto test_and_trace_required = 0.0;
119 23670 : for (auto i = AgeGroup(0); i < n_agegroups; ++i) {
120 16749 : test_and_trace_required +=
121 33498 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
122 16749 : params.template get<TimeInfectedNoSymptoms<FP>>()[i] *
123 33498 : (this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsNaive}) +
124 33498 : this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsPartialImmunity}) +
125 33498 : this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsImprovedImmunity}) +
126 33498 : this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsNaiveConfirmed}) +
127 33498 : this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed}) +
128 16749 : this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed}));
129 33498 : icu_occupancy += this->populations.get_from(pop, {i, InfectionState::InfectedCriticalNaive}) +
130 33498 : this->populations.get_from(pop, {i, InfectionState::InfectedCriticalPartialImmunity}) +
131 16749 : this->populations.get_from(pop, {i, InfectionState::InfectedCriticalImprovedImmunity});
132 : }
133 :
134 23670 : for (auto i = AgeGroup(0); i < n_agegroups; i++) {
135 :
136 16749 : size_t SNi = this->populations.get_flat_index({i, InfectionState::SusceptibleNaive});
137 16749 : size_t ENi = this->populations.get_flat_index({i, InfectionState::ExposedNaive});
138 16749 : size_t INSNi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaive});
139 16749 : size_t ISyNi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsNaive});
140 16749 : size_t ISevNi = this->populations.get_flat_index({i, InfectionState::InfectedSevereNaive});
141 16749 : size_t ICrNi = this->populations.get_flat_index({i, InfectionState::InfectedCriticalNaive});
142 :
143 16749 : size_t INSNCi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaiveConfirmed});
144 16749 : size_t ISyNCi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsNaiveConfirmed});
145 :
146 16749 : size_t SPIi = this->populations.get_flat_index({i, InfectionState::SusceptiblePartialImmunity});
147 16749 : size_t EPIi = this->populations.get_flat_index({i, InfectionState::ExposedPartialImmunity});
148 16749 : size_t INSPIi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunity});
149 16749 : size_t ISyPIi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunity});
150 16749 : size_t ISevPIi = this->populations.get_flat_index({i, InfectionState::InfectedSeverePartialImmunity});
151 16749 : size_t ICrPIi = this->populations.get_flat_index({i, InfectionState::InfectedCriticalPartialImmunity});
152 :
153 : size_t INSPICi =
154 16749 : this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed});
155 : size_t ISyPICi =
156 16749 : this->populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
157 :
158 16749 : size_t EIIi = this->populations.get_flat_index({i, InfectionState::ExposedImprovedImmunity});
159 16749 : size_t INSIIi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunity});
160 16749 : size_t ISyIIi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunity});
161 16749 : size_t ISevIIi = this->populations.get_flat_index({i, InfectionState::InfectedSevereImprovedImmunity});
162 16749 : size_t ICrIIi = this->populations.get_flat_index({i, InfectionState::InfectedCriticalImprovedImmunity});
163 :
164 : size_t INSIICi =
165 16749 : this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed});
166 : size_t ISyIICi =
167 16749 : this->populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
168 :
169 16749 : size_t SIIi = this->populations.get_flat_index({i, InfectionState::SusceptibleImprovedImmunity});
170 :
171 16749 : double reducExposedPartialImmunity = params.template get<ReducExposedPartialImmunity<FP>>()[i];
172 16749 : double reducExposedImprovedImmunity = params.template get<ReducExposedImprovedImmunity<FP>>()[i];
173 16749 : double reducInfectedSymptomsPartialImmunity =
174 16749 : params.template get<ReducInfectedSymptomsPartialImmunity<FP>>()[i];
175 16749 : double reducInfectedSymptomsImprovedImmunity =
176 16749 : params.template get<ReducInfectedSymptomsImprovedImmunity<FP>>()[i];
177 16749 : double reducInfectedSevereCriticalDeadPartialImmunity =
178 16749 : params.template get<ReducInfectedSevereCriticalDeadPartialImmunity<FP>>()[i];
179 16749 : double reducInfectedSevereCriticalDeadImprovedImmunity =
180 16749 : params.template get<ReducInfectedSevereCriticalDeadImprovedImmunity<FP>>()[i];
181 16749 : double reducTimeInfectedMild = params.template get<ReducTimeInfectedMild<FP>>()[i];
182 :
183 : //symptomatic are less well quarantined when testing and tracing is overwhelmed so they infect more people
184 : auto riskFromInfectedSymptomatic =
185 66996 : smoother_cosine(test_and_trace_required, params.template get<TestAndTraceCapacity<FP>>(),
186 33498 : params.template get<TestAndTraceCapacity<FP>>() *
187 16749 : params.template get<TestAndTraceCapacityMaxRiskSymptoms<FP>>(),
188 16749 : params.template get<RiskOfInfectionFromSymptomatic<FP>>()[i],
189 16749 : params.template get<MaxRiskOfInfectionFromSymptomatic<FP>>()[i]);
190 :
191 : auto riskFromInfectedNoSymptoms =
192 50247 : smoother_cosine(test_and_trace_required, params.template get<TestAndTraceCapacity<FP>>(),
193 33498 : params.template get<TestAndTraceCapacity<FP>>() *
194 16749 : params.template get<TestAndTraceCapacityMaxRiskNoSymptoms<FP>>(),
195 16749 : params.template get<RelativeTransmissionNoSymptoms<FP>>()[i], 1.0);
196 :
197 59742 : for (auto j = AgeGroup(0); j < n_agegroups; j++) {
198 42993 : size_t SNj = this->populations.get_flat_index({j, InfectionState::SusceptibleNaive});
199 42993 : size_t ENj = this->populations.get_flat_index({j, InfectionState::ExposedNaive});
200 42993 : size_t INSNj = this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsNaive});
201 42993 : size_t ISyNj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsNaive});
202 42993 : size_t ISevNj = this->populations.get_flat_index({j, InfectionState::InfectedSevereNaive});
203 42993 : size_t ICrNj = this->populations.get_flat_index({j, InfectionState::InfectedCriticalNaive});
204 42993 : size_t SIIj = this->populations.get_flat_index({j, InfectionState::SusceptibleImprovedImmunity});
205 :
206 42993 : size_t INSNCj = this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsNaiveConfirmed});
207 42993 : size_t ISyNCj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsNaiveConfirmed});
208 :
209 42993 : size_t SPIj = this->populations.get_flat_index({j, InfectionState::SusceptiblePartialImmunity});
210 42993 : size_t EPIj = this->populations.get_flat_index({j, InfectionState::ExposedPartialImmunity});
211 : size_t INSPIj =
212 42993 : this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsPartialImmunity});
213 42993 : size_t ISyPIj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsPartialImmunity});
214 42993 : size_t ISevPIj = this->populations.get_flat_index({j, InfectionState::InfectedSeverePartialImmunity});
215 42993 : size_t ICrPIj = this->populations.get_flat_index({j, InfectionState::InfectedCriticalPartialImmunity});
216 :
217 : size_t INSPICj =
218 42993 : this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed});
219 : size_t ISyPICj =
220 42993 : this->populations.get_flat_index({j, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
221 :
222 42993 : size_t EIIj = this->populations.get_flat_index({j, InfectionState::ExposedImprovedImmunity});
223 : size_t INSIIj =
224 42993 : this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsImprovedImmunity});
225 42993 : size_t ISyIIj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsImprovedImmunity});
226 42993 : size_t ISevIIj = this->populations.get_flat_index({j, InfectionState::InfectedSevereImprovedImmunity});
227 42993 : size_t ICrIIj = this->populations.get_flat_index({j, InfectionState::InfectedCriticalImprovedImmunity});
228 :
229 : size_t INSIICj =
230 42993 : this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed});
231 : size_t ISyIICj =
232 42993 : this->populations.get_flat_index({j, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
233 :
234 : // effective contact rate by contact rate between groups i and j and damping j
235 42993 : FP season_val =
236 42993 : (1 + params.template get<Seasonality<FP>>() *
237 42993 : sin(3.141592653589793 * ((params.template get<StartDay>() + t) / 182.5 + 0.5)));
238 85986 : FP cont_freq_eff = season_val * contact_matrix.get_matrix_at(t)(static_cast<Eigen::Index>((size_t)i),
239 42993 : static_cast<Eigen::Index>((size_t)j));
240 : // without died people
241 42993 : FP Nj = pop[SNj] + pop[ENj] + pop[INSNj] + pop[ISyNj] + pop[ISevNj] + pop[ICrNj] + pop[INSNCj] +
242 42993 : pop[ISyNCj] + pop[SPIj] + pop[EPIj] + pop[INSPIj] + pop[ISyPIj] + pop[ISevPIj] + pop[ICrPIj] +
243 42993 : pop[INSPICj] + pop[ISyPICj] + pop[SIIj] + pop[EIIj] + pop[INSIIj] + pop[ISyIIj] + pop[ISevIIj] +
244 42993 : pop[ICrIIj] + pop[INSIICj] + pop[ISyIICj];
245 42993 : const FP divNj = (Nj < Limits<ScalarType>::zero_tolerance()) ? 0.0 : 1.0 / Nj;
246 :
247 42993 : FP ext_inf_force_dummy = cont_freq_eff * divNj *
248 42993 : params.template get<TransmissionProbabilityOnContact<FP>>()[(AgeGroup)i] *
249 42993 : (riskFromInfectedNoSymptoms * (pop[INSNj] + pop[INSPIj] + pop[INSIIj]) +
250 42993 : riskFromInfectedSymptomatic * (pop[ISyNj] + pop[ISyPIj] + pop[ISyIIj]));
251 :
252 42993 : FP dummy_SN = y[SNi] * ext_inf_force_dummy;
253 :
254 42993 : FP dummy_SPI = y[SPIi] * reducExposedPartialImmunity * ext_inf_force_dummy;
255 :
256 42993 : FP dummy_SII = y[SIIi] * reducExposedImprovedImmunity * ext_inf_force_dummy;
257 :
258 85986 : flows[this->template get_flat_flow_index<InfectionState::SusceptibleNaive,
259 42993 : InfectionState::ExposedNaive>({i})] += dummy_SN;
260 85986 : flows[this->template get_flat_flow_index<InfectionState::SusceptiblePartialImmunity,
261 42993 : InfectionState::ExposedPartialImmunity>({i})] += dummy_SPI;
262 85986 : flows[this->template get_flat_flow_index<InfectionState::SusceptibleImprovedImmunity,
263 42993 : InfectionState::ExposedImprovedImmunity>({i})] += dummy_SII;
264 : }
265 :
266 : // ICU capacity shortage is close
267 : // TODO: if this is used with vaccination model, it has to be adapted if CriticalPerSevere
268 : // is different for different vaccination status. This is not the case here and in addition, ICUCapacity
269 : // is set to infinity and this functionality is deactivated, so this is OK for the moment.
270 50247 : double criticalPerSevereAdjusted = smoother_cosine(
271 33498 : icu_occupancy, 0.90 * params.template get<ICUCapacity<FP>>(), params.template get<ICUCapacity<FP>>(),
272 16749 : params.template get<CriticalPerSevere<FP>>()[i], 0);
273 :
274 16749 : FP deathsPerSevereAdjusted = params.template get<CriticalPerSevere<FP>>()[i] - criticalPerSevereAdjusted;
275 :
276 : /**** path of immune-naive ***/
277 : // Exposed
278 33498 : flows[this->template get_flat_flow_index<InfectionState::ExposedNaive,
279 16749 : InfectionState::InfectedNoSymptomsNaive>({i})] +=
280 16749 : y[ENi] / params.template get<TimeExposed<FP>>()[i];
281 :
282 : // InfectedNoSymptoms
283 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaive,
284 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
285 33498 : params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i] /
286 33498 : params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNi];
287 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaive,
288 16749 : InfectionState::InfectedSymptomsNaive>({i})] =
289 33498 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
290 33498 : params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNi];
291 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaiveConfirmed,
292 16749 : InfectionState::InfectedSymptomsNaiveConfirmed>({i})] =
293 33498 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
294 33498 : params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNCi];
295 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaiveConfirmed,
296 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
297 33498 : params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i] /
298 33498 : params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNCi];
299 :
300 : // InfectedSymptoms
301 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaive,
302 16749 : InfectionState::InfectedSevereNaive>({i})] =
303 33498 : params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
304 33498 : params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNi];
305 :
306 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaive,
307 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
308 33498 : (1 - params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
309 33498 : params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNi];
310 :
311 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaiveConfirmed,
312 16749 : InfectionState::InfectedSevereNaive>({i})] =
313 33498 : params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
314 33498 : params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNCi];
315 :
316 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaiveConfirmed,
317 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
318 33498 : (1 - params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
319 33498 : params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNCi];
320 :
321 : // InfectedSevere
322 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive,
323 16749 : InfectionState::InfectedCriticalNaive>({i})] =
324 16749 : criticalPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
325 :
326 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive,
327 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
328 33498 : (1 - params.template get<CriticalPerSevere<FP>>()[i]) /
329 33498 : params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
330 :
331 16749 : flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive, InfectionState::DeadNaive>(
332 33498 : {i})] = deathsPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
333 :
334 : // InfectedCritical
335 16749 : flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalNaive, InfectionState::DeadNaive>(
336 50247 : {i})] = params.template get<DeathsPerCritical<FP>>()[i] /
337 33498 : params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrNi];
338 :
339 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalNaive,
340 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
341 33498 : (1 - params.template get<DeathsPerCritical<FP>>()[i]) /
342 33498 : params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrNi];
343 :
344 : // /**** path of partially immune (e.g., one dose of vaccination) ***/
345 : // Exposed
346 33498 : flows[this->template get_flat_flow_index<InfectionState::ExposedPartialImmunity,
347 16749 : InfectionState::InfectedNoSymptomsPartialImmunity>({i})] +=
348 16749 : y[EPIi] / params.template get<TimeExposed<FP>>()[i];
349 :
350 : // InfectedNoSymptoms
351 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunity,
352 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
353 33498 : (1 - (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
354 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
355 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPIi];
356 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunity,
357 16749 : InfectionState::InfectedSymptomsPartialImmunity>({i})] =
358 33498 : (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
359 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
360 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPIi];
361 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunityConfirmed,
362 16749 : InfectionState::InfectedSymptomsPartialImmunityConfirmed>({i})] =
363 33498 : (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
364 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
365 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPICi];
366 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunityConfirmed,
367 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
368 33498 : (1 - (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
369 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
370 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPICi];
371 :
372 : // InfectedSymptoms
373 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunity,
374 16749 : InfectionState::InfectedSeverePartialImmunity>({i})] =
375 16749 : reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity *
376 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
377 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPIi];
378 :
379 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunity,
380 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
381 16749 : (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity) *
382 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
383 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPIi];
384 :
385 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunityConfirmed,
386 16749 : InfectionState::InfectedSeverePartialImmunity>({i})] =
387 16749 : reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity *
388 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
389 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPICi];
390 :
391 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunityConfirmed,
392 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
393 16749 : (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity) *
394 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
395 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPICi];
396 :
397 : // InfectedSevere
398 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
399 16749 : InfectionState::InfectedCriticalPartialImmunity>({i})] =
400 16749 : reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity *
401 16749 : criticalPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
402 :
403 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
404 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
405 16749 : (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
406 33498 : params.template get<CriticalPerSevere<FP>>()[i]) /
407 33498 : params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
408 :
409 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
410 16749 : InfectionState::DeadPartialImmunity>({i})] =
411 16749 : (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
412 16749 : deathsPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
413 :
414 : // InfectedCritical
415 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalPartialImmunity,
416 16749 : InfectionState::DeadPartialImmunity>({i})] =
417 16749 : (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
418 33498 : params.template get<DeathsPerCritical<FP>>()[i] / params.template get<TimeInfectedCritical<FP>>()[i] *
419 16749 : y[ICrPIi];
420 :
421 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalPartialImmunity,
422 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
423 16749 : (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
424 33498 : params.template get<DeathsPerCritical<FP>>()[i]) /
425 33498 : params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrPIi];
426 :
427 : // /**** path of twice vaccinated, here called immune although reinfection is possible now ***/
428 : // Exposed
429 33498 : flows[this->template get_flat_flow_index<InfectionState::ExposedImprovedImmunity,
430 16749 : InfectionState::InfectedNoSymptomsImprovedImmunity>({i})] +=
431 16749 : y[EIIi] / params.template get<TimeExposed<FP>>()[i];
432 :
433 : // InfectedNoSymptoms
434 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunity,
435 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
436 33498 : (1 - (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
437 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
438 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIIi];
439 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunity,
440 16749 : InfectionState::InfectedSymptomsImprovedImmunity>({i})] =
441 33498 : (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
442 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
443 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIIi];
444 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed,
445 16749 : InfectionState::InfectedSymptomsImprovedImmunityConfirmed>({i})] =
446 33498 : (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
447 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
448 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIICi];
449 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed,
450 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
451 33498 : (1 - (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
452 16749 : (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
453 16749 : (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIICi];
454 :
455 : // InfectedSymptoms
456 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunity,
457 16749 : InfectionState::InfectedSevereImprovedImmunity>({i})] =
458 16749 : reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity *
459 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
460 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIIi];
461 :
462 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunity,
463 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
464 16749 : (1 - (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity) *
465 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
466 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIIi];
467 :
468 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunityConfirmed,
469 16749 : InfectionState::InfectedSevereImprovedImmunity>({i})] =
470 16749 : reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity *
471 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
472 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIICi];
473 :
474 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunityConfirmed,
475 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
476 16749 : (1 - (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity) *
477 16749 : params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
478 16749 : (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIICi];
479 :
480 : // InfectedSevere
481 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSevereImprovedImmunity,
482 16749 : InfectionState::InfectedCriticalImprovedImmunity>({i})] =
483 16749 : reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity *
484 16749 : criticalPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
485 :
486 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSevereImprovedImmunity,
487 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
488 16749 : (1 -
489 16749 : (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
490 33498 : params.template get<CriticalPerSevere<FP>>()[i]) /
491 33498 : params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
492 :
493 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedSevereImprovedImmunity,
494 16749 : InfectionState::DeadImprovedImmunity>({i})] =
495 16749 : (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
496 16749 : deathsPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
497 :
498 : // InfectedCritical
499 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalImprovedImmunity,
500 16749 : InfectionState::DeadImprovedImmunity>({i})] =
501 16749 : (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
502 33498 : params.template get<DeathsPerCritical<FP>>()[i] / params.template get<TimeInfectedCritical<FP>>()[i] *
503 16749 : y[ICrIIi];
504 :
505 33498 : flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalImprovedImmunity,
506 16749 : InfectionState::SusceptibleImprovedImmunity>({i})] =
507 16749 : (1 -
508 16749 : (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
509 33498 : params.template get<DeathsPerCritical<FP>>()[i]) /
510 33498 : params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrIIi];
511 : }
512 6921 : }
513 :
514 : /**
515 : * serialize this.
516 : * @see mio::serialize
517 : */
518 : template <class IOContext>
519 1 : void serialize(IOContext& io) const
520 : {
521 3 : auto obj = io.create_object("Model");
522 1 : obj.add_element("Parameters", this->parameters);
523 1 : obj.add_element("Populations", this->populations);
524 2 : }
525 :
526 : /**
527 : * deserialize an object of this class.
528 : * @see mio::deserialize
529 : */
530 : template <class IOContext>
531 1 : static IOResult<Model> deserialize(IOContext& io)
532 : {
533 3 : auto obj = io.expect_object("Model");
534 3 : auto par = obj.expect_element("Parameters", Tag<ParameterSet>{});
535 3 : auto pop = obj.expect_element("Populations", Tag<Populations>{});
536 : return apply(
537 : io,
538 1 : [](auto&& par_, auto&& pop_) {
539 1 : return Model{pop_, par_};
540 : },
541 2 : par, pop);
542 1 : }
543 : };
544 :
545 : //forward declaration, see below.
546 : template <typename FP = ScalarType, class Base = mio::Simulation<FP, Model<FP>>>
547 : class Simulation;
548 :
549 : /**
550 : * get percentage of infections per total population.
551 : * @param model the compartment model with initial values.
552 : * @param t current simulation time.
553 : * @param y current value of compartments.
554 : * @tparam Base simulation type that uses a secir compartment model. see Simulation.
555 : */
556 : template <typename FP = ScalarType, class Base = mio::Simulation<FP, Model<FP>>>
557 : FP get_infections_relative(const Simulation<FP, Base>& model, FP t, const Eigen::Ref<const Vector<FP>>& y);
558 :
559 : /**
560 : * specialization of compartment model simulation for the SECIRVVS model.
561 : * @tparam FP floating point type, e.g., double.
562 : * @tparam BaseT simulation type, default mio::Simulation. For testing purposes only!
563 : */
564 : template <typename FP, class BaseT>
565 : class Simulation : public BaseT
566 : {
567 : public:
568 : /**
569 : * construct a simulation.
570 : * @param model the model to simulate.
571 : * @param t0 start time
572 : * @param dt time steps
573 : */
574 89 : Simulation(mio::osecirvvs::Model<FP> const& model, FP t0 = 0., FP dt = 0.1)
575 : : BaseT(model, t0, dt)
576 89 : , m_t_last_npi_check(t0)
577 : {
578 89 : }
579 :
580 : /**
581 : * @brief Applies the effect of a new variant of a disease to the transmission probability of the model.
582 : *
583 : * This function adjusts the transmission probability of the disease for each age group based on the share of the new variant.
584 : * The share of the new variant is calculated based on the time `t` and the start day of the new variant.
585 : * The transmission probability is then updated for each age group in the model.
586 : *
587 : * Based on Equation (35) and (36) in doi.org/10.1371/journal.pcbi.1010054
588 : *
589 : * @param [in] t The current time.
590 : * @param [in] base_infectiousness The base infectiousness of the old variant for each age group.
591 : */
592 738 : void apply_variant(const double t, const CustomIndexArray<UncertainValue<FP>, AgeGroup> base_infectiousness)
593 : {
594 738 : auto start_day = this->get_model().parameters.template get<StartDay>();
595 738 : auto start_day_new_variant = this->get_model().parameters.template get<StartDayNewVariant>();
596 :
597 738 : if (start_day + t >= start_day_new_variant - 1e-10) {
598 27 : const double days_variant = t - (start_day_new_variant - start_day);
599 27 : const double share_new_variant = std::min(1.0, 0.01 * pow(2, (1. / 7) * days_variant));
600 27 : const auto num_groups = this->get_model().parameters.get_num_groups();
601 54 : for (auto i = AgeGroup(0); i < num_groups; ++i) {
602 27 : double new_transmission =
603 27 : (1 - share_new_variant) * base_infectiousness[i] +
604 27 : share_new_variant * base_infectiousness[i] *
605 27 : this->get_model().parameters.template get<InfectiousnessNewVariant<FP>>()[i];
606 27 : this->get_model().parameters.template get<TransmissionProbabilityOnContact<FP>>()[i] = new_transmission;
607 : }
608 : }
609 738 : }
610 :
611 645 : void apply_vaccination(double t)
612 : {
613 645 : auto t_idx = SimulationDay((size_t)t);
614 645 : auto& params = this->get_model().parameters;
615 645 : size_t num_groups = (size_t)params.get_num_groups();
616 645 : auto last_value = this->get_result().get_last_value();
617 :
618 645 : auto count = (size_t)InfectionState::Count;
619 645 : auto S = (size_t)InfectionState::SusceptibleNaive;
620 645 : auto SV = (size_t)InfectionState::SusceptiblePartialImmunity;
621 645 : auto R = (size_t)InfectionState::SusceptibleImprovedImmunity;
622 :
623 2100 : for (size_t i = 0; i < num_groups; ++i) {
624 :
625 1455 : double first_vacc;
626 1455 : double full_vacc;
627 1455 : if (t_idx == SimulationDay(0)) {
628 0 : first_vacc = params.template get<DailyFirstVaccination<FP>>()[{(AgeGroup)i, t_idx}];
629 0 : full_vacc = params.template get<DailyFullVaccination<FP>>()[{(AgeGroup)i, t_idx}];
630 : }
631 : else {
632 2910 : first_vacc = params.template get<DailyFirstVaccination<FP>>()[{(AgeGroup)i, t_idx}] -
633 1455 : params.template get<DailyFirstVaccination<FP>>()[{(AgeGroup)i, t_idx - SimulationDay(1)}];
634 2910 : full_vacc = params.template get<DailyFullVaccination<FP>>()[{(AgeGroup)i, t_idx}] -
635 1455 : params.template get<DailyFullVaccination<FP>>()[{(AgeGroup)i, t_idx - SimulationDay(1)}];
636 : }
637 :
638 1455 : if (last_value(count * i + S) - first_vacc < 0) {
639 0 : auto corrected = 0.99 * last_value(count * i + S);
640 0 : log_warning("too many first vaccinated at time {}: setting first_vacc from {} to {}", t, first_vacc,
641 : corrected);
642 0 : first_vacc = corrected;
643 : }
644 :
645 1455 : last_value(count * i + S) -= first_vacc;
646 1455 : last_value(count * i + SV) += first_vacc;
647 :
648 1455 : if (last_value(count * i + SV) - full_vacc < 0) {
649 0 : auto corrected = 0.99 * last_value(count * i + SV);
650 0 : log_warning("too many fully vaccinated at time {}: setting full_vacc from {} to {}", t, full_vacc,
651 : corrected);
652 0 : full_vacc = corrected;
653 : }
654 :
655 1455 : last_value(count * i + SV) -= full_vacc;
656 1455 : last_value(count * i + R) += full_vacc;
657 : }
658 645 : }
659 :
660 : /**
661 : * @brief advance simulation to tmax.
662 : * Overwrites Simulation::advance and includes a check for dynamic NPIs in regular intervals.
663 : * @see Simulation::advance
664 : * @param tmax next stopping point of simulation
665 : * @return value at tmax
666 : */
667 62 : Eigen::Ref<Vector<FP>> advance(FP tmax)
668 : {
669 62 : auto& t_end_dyn_npis = this->get_model().parameters.get_end_dynamic_npis();
670 62 : auto& dyn_npis = this->get_model().parameters.template get<DynamicNPIsInfectedSymptoms<FP>>();
671 62 : auto& contact_patterns = this->get_model().parameters.template get<ContactPatterns<FP>>();
672 : // const size_t num_groups = (size_t)this->get_model().parameters.get_num_groups();
673 :
674 : // in the apply_variant function, we adjust the TransmissionProbabilityOnContact parameter. We need to store
675 : // the base value to use it in the apply_variant function and also to reset the parameter after the simulation.
676 62 : auto base_infectiousness = this->get_model().parameters.template get<TransmissionProbabilityOnContact<FP>>();
677 :
678 : ScalarType delay_npi_implementation;
679 62 : auto t = BaseT::get_result().get_last_time();
680 62 : const auto dt = dyn_npis.get_thresholds().size() > 0 ? dyn_npis.get_interval().get() : tmax;
681 716 : while (t < tmax) {
682 :
683 654 : auto dt_eff = std::min({dt, tmax - t, m_t_last_npi_check + dt - t});
684 654 : if (dt_eff >= 1.0) {
685 645 : dt_eff = 1.0;
686 : }
687 :
688 654 : if (t == 0) {
689 : //this->apply_vaccination(t); // done in init now?
690 48 : this->apply_variant(t, base_infectiousness);
691 : }
692 654 : BaseT::advance(t + dt_eff);
693 654 : if (t + 0.5 + dt_eff - std::floor(t + 0.5) >= 1) {
694 645 : this->apply_vaccination(t + 0.5 + dt_eff);
695 645 : this->apply_variant(t, base_infectiousness);
696 : }
697 :
698 654 : if (t > 0) {
699 606 : delay_npi_implementation =
700 606 : this->get_model().parameters.template get<DynamicNPIsImplementationDelay<FP>>();
701 : }
702 : else { // DynamicNPIs for t=0 are 'misused' to be from-start NPIs. I.e., do not enforce delay.
703 48 : delay_npi_implementation = 0;
704 : }
705 654 : t = t + dt_eff;
706 :
707 654 : if (dyn_npis.get_thresholds().size() > 0) {
708 645 : if (floating_point_greater_equal(t, m_t_last_npi_check + dt)) {
709 215 : if (t < t_end_dyn_npis) {
710 178 : auto inf_rel = get_infections_relative<FP>(*this, t, this->get_result().get_last_value()) *
711 89 : dyn_npis.get_base_value();
712 89 : auto exceeded_threshold = dyn_npis.get_max_exceeded_threshold(inf_rel);
713 171 : if (exceeded_threshold != dyn_npis.get_thresholds().end() &&
714 118 : (exceeded_threshold->first > m_dynamic_npi.first ||
715 36 : t > ScalarType(m_dynamic_npi.second))) { //old npi was weaker or is expired
716 :
717 46 : auto t_start = SimulationTime(t + delay_npi_implementation);
718 46 : auto t_end = t_start + SimulationTime(dyn_npis.get_duration());
719 46 : this->get_model().parameters.get_start_commuter_detection() = (ScalarType)t_start;
720 46 : this->get_model().parameters.get_end_commuter_detection() = (ScalarType)t_end;
721 46 : m_dynamic_npi = std::make_pair(exceeded_threshold->first, t_end);
722 46 : implement_dynamic_npis(contact_patterns.get_cont_freq_mat(), exceeded_threshold->second,
723 46 : t_start, t_end, [](auto& g) {
724 46 : return make_contact_damping_matrix(g);
725 : });
726 : }
727 : }
728 215 : m_t_last_npi_check = t;
729 : }
730 : }
731 : else {
732 9 : m_t_last_npi_check = t;
733 : }
734 : }
735 : // reset TransmissionProbabilityOnContact. This is important for the graph simulation where the advance
736 : // function is called multiple times for the same model.
737 62 : this->get_model().parameters.template get<TransmissionProbabilityOnContact<FP>>() = base_infectiousness;
738 :
739 124 : return this->get_result().get_last_value();
740 62 : }
741 :
742 : private:
743 : double m_t_last_npi_check;
744 : std::pair<double, SimulationTime> m_dynamic_npi = {-std::numeric_limits<double>::max(), SimulationTime(0)};
745 : };
746 :
747 : /**
748 : * @brief Specialization of simulate for SECIRVVS models using Simulation.
749 : *
750 : * @tparam FP floating point type, e.g., double.
751 : * @param[in] t0 start time.
752 : * @param[in] tmax end time.
753 : * @param[in] dt time step.
754 : * @param[in] model SECIRVVS model to simulate.
755 : * @param[in] integrator optional integrator, uses rk45 if nullptr.
756 : *
757 : * @return Returns the result of the simulation.
758 : */
759 : template <typename FP = ScalarType>
760 18 : inline auto simulate(FP t0, FP tmax, FP dt, const Model<FP>& model,
761 : std::shared_ptr<IntegratorCore<FP>> integrator = nullptr)
762 : {
763 18 : return mio::simulate<FP, Model<FP>, Simulation<FP>>(t0, tmax, dt, model, integrator);
764 : }
765 :
766 : /**
767 : * @brief Specialization of simulate for SECIRVVS models using the FlowSimulation.
768 : *
769 : * @tparam FP floating point type, e.g., double.
770 : * @param[in] t0 start time.
771 : * @param[in] tmax end time.
772 : * @param[in] dt time step.
773 : * @param[in] model SECIRVVS model to simulate.
774 : * @param[in] integrator optional integrator, uses rk45 if nullptr.
775 : *
776 : * @return Returns the result of the Flowsimulation.
777 : */
778 : template <typename FP = ScalarType>
779 : inline auto simulate_flows(FP t0, FP tmax, FP dt, const Model<FP>& model,
780 : std::shared_ptr<IntegratorCore<FP>> integrator = nullptr)
781 : {
782 : return mio::simulate_flows<FP, Model<FP>, Simulation<FP, mio::FlowSimulation<FP, Model<FP>>>>(t0, tmax, dt, model,
783 : integrator);
784 : }
785 :
786 : //see declaration above.
787 : template <typename FP, class Base>
788 98 : FP get_infections_relative(const Simulation<FP, Base>& sim, FP /*t*/, const Eigen::Ref<const Vector<FP>>& y)
789 : {
790 98 : FP sum_inf = 0;
791 286 : for (auto i = AgeGroup(0); i < sim.get_model().parameters.get_num_groups(); ++i) {
792 188 : sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsNaive});
793 188 : sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsNaiveConfirmed});
794 188 : sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsPartialImmunity});
795 188 : sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsImprovedImmunity});
796 188 : sum_inf +=
797 188 : sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
798 188 : sum_inf +=
799 188 : sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
800 : }
801 98 : auto inf_rel = sum_inf / sim.get_model().populations.get_total();
802 :
803 98 : return inf_rel;
804 : }
805 :
806 : /**
807 : * Get mobility factors.
808 : * Used by mobility graph simulation.
809 : * Like infection risk, mobility of infected individuals is reduced if they are well isolated.
810 : * @param model the compartment model with initial values.
811 : * @param t current simulation time.
812 : * @param y current value of compartments.
813 : * @return vector expression, same size as y, with mobility factors per compartment.
814 : * @tparam Base simulation type that uses a secir compartment model. see Simulation.
815 : */
816 : template <typename FP = double, class Base = mio::Simulation<Model<FP>, FP>>
817 9 : auto get_mobility_factors(const Simulation<Base>& sim, FP /*t*/, const Eigen::Ref<const Vector<FP>>& y)
818 :
819 : {
820 9 : auto& params = sim.get_model().parameters;
821 : //parameters as arrays
822 9 : auto& p_asymp = params.template get<RecoveredPerInfectedNoSymptoms<FP>>().array().template cast<FP>();
823 9 : auto& p_inf = params.template get<RiskOfInfectionFromSymptomatic<FP>>().array().template cast<FP>();
824 9 : auto& p_inf_max = params.template get<MaxRiskOfInfectionFromSymptomatic<FP>>().array().template cast<FP>();
825 : //slice of InfectedNoSymptoms
826 54 : auto y_INS = slice(y, {Eigen::Index(InfectionState::InfectedNoSymptomsNaive),
827 18 : Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) +
828 : slice(y, {Eigen::Index(InfectionState::InfectedNoSymptomsPartialImmunity),
829 18 : Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) +
830 : slice(y, {Eigen::Index(InfectionState::InfectedNoSymptomsImprovedImmunity),
831 18 : Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)});
832 :
833 : //compute isolation, same as infection risk from main model
834 9 : auto test_and_trace_required =
835 27 : ((1 - p_asymp) / params.template get<TimeInfectedNoSymptoms<FP>>().array().template cast<double>() *
836 : y_INS.array())
837 36 : .sum();
838 36 : auto riskFromInfectedSymptomatic =
839 9 : smoother_cosine(test_and_trace_required, double(params.template get<TestAndTraceCapacity<FP>>()),
840 18 : params.template get<TestAndTraceCapacity<FP>>() *
841 9 : params.template get<TestAndTraceCapacityMaxRiskSymptoms<FP>>(),
842 : p_inf.matrix(), p_inf_max.matrix());
843 :
844 : //set factor for infected
845 9 : auto factors = Eigen::VectorXd::Ones(y.rows()).eval();
846 27 : slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsNaive), Eigen::Index(size_t(params.get_num_groups())),
847 : Eigen::Index(InfectionState::Count)})
848 18 : .array() = riskFromInfectedSymptomatic;
849 9 : slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsPartialImmunity),
850 18 : Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)})
851 18 : .array() = riskFromInfectedSymptomatic;
852 9 : slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsImprovedImmunity),
853 18 : Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)})
854 18 : .array() = riskFromInfectedSymptomatic;
855 18 : return factors;
856 9 : }
857 :
858 : template <typename FP = double, class Base = mio::Simulation<Model<FP>, FP>>
859 9 : auto test_commuters(Simulation<FP, Base>& sim, Eigen::Ref<Vector<FP>> mobile_population, FP time)
860 : {
861 9 : auto& model = sim.get_model();
862 9 : auto nondetection = 1.0;
863 18 : if (time >= model.parameters.get_start_commuter_detection() &&
864 9 : time < model.parameters.get_end_commuter_detection()) {
865 9 : nondetection = (double)model.parameters.get_commuter_nondetection();
866 : }
867 27 : for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); ++i) {
868 18 : auto ISyNi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsNaive});
869 18 : auto ISyNCi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsNaiveConfirmed});
870 18 : auto INSNi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaive});
871 18 : auto INSNCi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaiveConfirmed});
872 :
873 18 : auto ISPIi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunity});
874 18 : auto ISPICi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
875 18 : auto INSPIi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunity});
876 : auto INSPICi =
877 18 : model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed});
878 :
879 18 : auto ISyIIi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunity});
880 18 : auto ISyIICi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
881 18 : auto INSIIi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunity});
882 : auto INSIICi =
883 18 : model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed});
884 :
885 : //put detected commuters in their own compartment so they don't contribute to infections in their home node
886 18 : sim.get_result().get_last_value()[ISyNi] -= mobile_population[ISyNi] * (1 - nondetection);
887 18 : sim.get_result().get_last_value()[ISyNCi] += mobile_population[ISyNi] * (1 - nondetection);
888 18 : sim.get_result().get_last_value()[INSNi] -= mobile_population[INSNi] * (1 - nondetection);
889 18 : sim.get_result().get_last_value()[INSNCi] += mobile_population[INSNi] * (1 - nondetection);
890 :
891 18 : sim.get_result().get_last_value()[ISPIi] -= mobile_population[ISPIi] * (1 - nondetection);
892 18 : sim.get_result().get_last_value()[ISPICi] += mobile_population[ISPIi] * (1 - nondetection);
893 18 : sim.get_result().get_last_value()[INSPIi] -= mobile_population[INSPIi] * (1 - nondetection);
894 18 : sim.get_result().get_last_value()[INSPICi] += mobile_population[INSPIi] * (1 - nondetection);
895 :
896 18 : sim.get_result().get_last_value()[ISyIIi] -= mobile_population[ISyIIi] * (1 - nondetection);
897 18 : sim.get_result().get_last_value()[ISyIICi] += mobile_population[ISyIIi] * (1 - nondetection);
898 18 : sim.get_result().get_last_value()[INSIIi] -= mobile_population[INSIIi] * (1 - nondetection);
899 18 : sim.get_result().get_last_value()[INSIICi] += mobile_population[INSIIi] * (1 - nondetection);
900 :
901 : //reduce the number of commuters
902 18 : mobile_population[ISyNi] *= nondetection;
903 18 : mobile_population[INSNi] *= nondetection;
904 :
905 18 : mobile_population[ISPIi] *= nondetection;
906 18 : mobile_population[INSPIi] *= nondetection;
907 :
908 18 : mobile_population[ISyIIi] *= nondetection;
909 18 : mobile_population[INSIIi] *= nondetection;
910 : }
911 9 : }
912 :
913 : } // namespace osecirvvs
914 : } // namespace mio
915 :
916 : #endif //ODESECIRVVS_MODEL_H
|