model.h Source File

CPP API: model.h Source File
ode_secirvvs/model.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 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 MIO_ODE_SECIRVVS_MODEL_H
21 #define MIO_ODE_SECIRVVS_MODEL_H
22 
30 #include "memilio/math/smoother.h"
32 
33 #include <numbers>
34 
35 namespace mio
36 {
37 namespace osecirvvs
38 {
39 // clang-format off
40 using Flows = TypeList<
41  //naive
57  //partial immunity
73  //improved immunity
89 // clang-format on
90 
91 template <typename FP>
92 class Model
93  : public FlowModel<FP, InfectionState, mio::Populations<FP, AgeGroup, InfectionState>, Parameters<FP>, Flows>
94 {
96 
97 public:
98  using typename Base::ParameterSet;
99  using typename Base::Populations;
100 
101  Model(const Populations& pop, const ParameterSet& params)
102  : Base(pop, params)
103  {
104  }
105 
106  Model(int num_agegroups)
107  : Model(Populations({AgeGroup(num_agegroups), InfectionState::Count}), ParameterSet(AgeGroup(num_agegroups)))
108  {
109  }
110 
111  void get_flows(Eigen::Ref<const Eigen::VectorX<FP>> pop, Eigen::Ref<const Eigen::VectorX<FP>> y, FP t,
112  Eigen::Ref<Eigen::VectorX<FP>> flows) const override
113  {
114  auto const& params = this->parameters;
115  AgeGroup n_agegroups = params.get_num_groups();
116 
117  ContactMatrixGroup<FP> const& contact_matrix = params.template get<ContactPatterns<FP>>();
118 
119  FP icu_occupancy = 0.0;
120  FP test_and_trace_required = 0.0;
121  for (auto i = AgeGroup(0); i < n_agegroups; ++i) {
122  test_and_trace_required +=
123  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
124  params.template get<TimeInfectedNoSymptoms<FP>>()[i] *
128  this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsNaiveConfirmed}) +
129  this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed}) +
130  this->populations.get_from(pop, {i, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed}));
131  icu_occupancy += this->populations.get_from(pop, {i, InfectionState::InfectedCriticalNaive}) +
132  this->populations.get_from(pop, {i, InfectionState::InfectedCriticalPartialImmunity}) +
133  this->populations.get_from(pop, {i, InfectionState::InfectedCriticalImprovedImmunity});
134  }
135 
136  for (auto i = AgeGroup(0); i < n_agegroups; i++) {
137 
138  size_t SNi = this->populations.get_flat_index({i, InfectionState::SusceptibleNaive});
139  size_t ENi = this->populations.get_flat_index({i, InfectionState::ExposedNaive});
140  size_t INSNi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaive});
141  size_t ISyNi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsNaive});
142  size_t ISevNi = this->populations.get_flat_index({i, InfectionState::InfectedSevereNaive});
143  size_t ICrNi = this->populations.get_flat_index({i, InfectionState::InfectedCriticalNaive});
144 
145  size_t INSNCi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaiveConfirmed});
146  size_t ISyNCi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsNaiveConfirmed});
147 
148  size_t SPIi = this->populations.get_flat_index({i, InfectionState::SusceptiblePartialImmunity});
149  size_t EPIi = this->populations.get_flat_index({i, InfectionState::ExposedPartialImmunity});
150  size_t INSPIi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunity});
151  size_t ISyPIi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunity});
152  size_t ISevPIi = this->populations.get_flat_index({i, InfectionState::InfectedSeverePartialImmunity});
153  size_t ICrPIi = this->populations.get_flat_index({i, InfectionState::InfectedCriticalPartialImmunity});
154 
155  size_t INSPICi =
156  this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed});
157  size_t ISyPICi =
158  this->populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
159 
160  size_t EIIi = this->populations.get_flat_index({i, InfectionState::ExposedImprovedImmunity});
161  size_t INSIIi = this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunity});
162  size_t ISyIIi = this->populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunity});
163  size_t ISevIIi = this->populations.get_flat_index({i, InfectionState::InfectedSevereImprovedImmunity});
164  size_t ICrIIi = this->populations.get_flat_index({i, InfectionState::InfectedCriticalImprovedImmunity});
165 
166  size_t INSIICi =
167  this->populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed});
168  size_t ISyIICi =
169  this->populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
170 
171  size_t SIIi = this->populations.get_flat_index({i, InfectionState::SusceptibleImprovedImmunity});
172 
173  FP reducExposedPartialImmunity = params.template get<ReducExposedPartialImmunity<FP>>()[i];
174  FP reducExposedImprovedImmunity = params.template get<ReducExposedImprovedImmunity<FP>>()[i];
175  FP reducInfectedSymptomsPartialImmunity =
176  params.template get<ReducInfectedSymptomsPartialImmunity<FP>>()[i];
177  FP reducInfectedSymptomsImprovedImmunity =
178  params.template get<ReducInfectedSymptomsImprovedImmunity<FP>>()[i];
179  FP reducInfectedSevereCriticalDeadPartialImmunity =
180  params.template get<ReducInfectedSevereCriticalDeadPartialImmunity<FP>>()[i];
181  FP reducInfectedSevereCriticalDeadImprovedImmunity =
182  params.template get<ReducInfectedSevereCriticalDeadImprovedImmunity<FP>>()[i];
183  FP reducTimeInfectedMild = params.template get<ReducTimeInfectedMild<FP>>()[i];
184 
185  //symptomatic are less well quarantined when testing and tracing is overwhelmed so they infect more people
186  auto riskFromInfectedSymptomatic =
187  smoother_cosine<FP>(test_and_trace_required, params.template get<TestAndTraceCapacity<FP>>(),
188  params.template get<TestAndTraceCapacity<FP>>() *
189  params.template get<TestAndTraceCapacityMaxRiskSymptoms<FP>>(),
190  params.template get<RiskOfInfectionFromSymptomatic<FP>>()[i],
191  params.template get<MaxRiskOfInfectionFromSymptomatic<FP>>()[i]);
192 
193  auto riskFromInfectedNoSymptoms =
194  smoother_cosine<FP>(test_and_trace_required, params.template get<TestAndTraceCapacity<FP>>(),
195  params.template get<TestAndTraceCapacity<FP>>() *
196  params.template get<TestAndTraceCapacityMaxRiskNoSymptoms<FP>>(),
197  params.template get<RelativeTransmissionNoSymptoms<FP>>()[i], 1.0);
198 
199  for (auto j = AgeGroup(0); j < n_agegroups; j++) {
200  size_t SNj = this->populations.get_flat_index({j, InfectionState::SusceptibleNaive});
201  size_t ENj = this->populations.get_flat_index({j, InfectionState::ExposedNaive});
202  size_t INSNj = this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsNaive});
203  size_t ISyNj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsNaive});
204  size_t ISevNj = this->populations.get_flat_index({j, InfectionState::InfectedSevereNaive});
205  size_t ICrNj = this->populations.get_flat_index({j, InfectionState::InfectedCriticalNaive});
206  size_t SIIj = this->populations.get_flat_index({j, InfectionState::SusceptibleImprovedImmunity});
207 
208  size_t INSNCj = this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsNaiveConfirmed});
209  size_t ISyNCj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsNaiveConfirmed});
210 
211  size_t SPIj = this->populations.get_flat_index({j, InfectionState::SusceptiblePartialImmunity});
212  size_t EPIj = this->populations.get_flat_index({j, InfectionState::ExposedPartialImmunity});
213  size_t INSPIj =
214  this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsPartialImmunity});
215  size_t ISyPIj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsPartialImmunity});
216  size_t ISevPIj = this->populations.get_flat_index({j, InfectionState::InfectedSeverePartialImmunity});
217  size_t ICrPIj = this->populations.get_flat_index({j, InfectionState::InfectedCriticalPartialImmunity});
218 
219  size_t INSPICj =
220  this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed});
221  size_t ISyPICj =
222  this->populations.get_flat_index({j, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
223 
224  size_t EIIj = this->populations.get_flat_index({j, InfectionState::ExposedImprovedImmunity});
225  size_t INSIIj =
226  this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsImprovedImmunity});
227  size_t ISyIIj = this->populations.get_flat_index({j, InfectionState::InfectedSymptomsImprovedImmunity});
228  size_t ISevIIj = this->populations.get_flat_index({j, InfectionState::InfectedSevereImprovedImmunity});
229  size_t ICrIIj = this->populations.get_flat_index({j, InfectionState::InfectedCriticalImprovedImmunity});
230 
231  size_t INSIICj =
232  this->populations.get_flat_index({j, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed});
233  size_t ISyIICj =
234  this->populations.get_flat_index({j, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
235 
236  // effective contact rate by contact rate between groups i and j and damping j
237  FP season_val = (1 + params.template get<Seasonality<FP>>() *
238  sin(std::numbers::pi_v<ScalarType> *
239  ((params.template get<StartDay<FP>>() + t) / 182.5 + 0.5)));
240  FP cont_freq_eff =
241  season_val * contact_matrix.get_matrix_at(SimulationTime<FP>(t))(
242  static_cast<Eigen::Index>((size_t)i), static_cast<Eigen::Index>((size_t)j));
243  // without died people
244  FP Nj = pop[SNj] + pop[ENj] + pop[INSNj] + pop[ISyNj] + pop[ISevNj] + pop[ICrNj] + pop[INSNCj] +
245  pop[ISyNCj] + pop[SPIj] + pop[EPIj] + pop[INSPIj] + pop[ISyPIj] + pop[ISevPIj] + pop[ICrPIj] +
246  pop[INSPICj] + pop[ISyPICj] + pop[SIIj] + pop[EIIj] + pop[INSIIj] + pop[ISyIIj] + pop[ISevIIj] +
247  pop[ICrIIj] + pop[INSIICj] + pop[ISyIICj];
248  const FP divNj = (Nj < Limits<FP>::zero_tolerance()) ? FP(0.0) : FP(1.0 / Nj);
249 
250  FP ext_inf_force_dummy = cont_freq_eff * divNj *
251  params.template get<TransmissionProbabilityOnContact<FP>>()[(AgeGroup)i] *
252  (riskFromInfectedNoSymptoms * (pop[INSNj] + pop[INSPIj] + pop[INSIIj]) +
253  riskFromInfectedSymptomatic * (pop[ISyNj] + pop[ISyPIj] + pop[ISyIIj]));
254 
255  FP dummy_SN = y[SNi] * ext_inf_force_dummy;
256 
257  FP dummy_SPI = y[SPIi] * reducExposedPartialImmunity * ext_inf_force_dummy;
258 
259  FP dummy_SII = y[SIIi] * reducExposedImprovedImmunity * ext_inf_force_dummy;
260 
261  flows[this->template get_flat_flow_index<InfectionState::SusceptibleNaive,
262  InfectionState::ExposedNaive>({i})] += dummy_SN;
263  flows[this->template get_flat_flow_index<InfectionState::SusceptiblePartialImmunity,
264  InfectionState::ExposedPartialImmunity>({i})] += dummy_SPI;
265  flows[this->template get_flat_flow_index<InfectionState::SusceptibleImprovedImmunity,
266  InfectionState::ExposedImprovedImmunity>({i})] += dummy_SII;
267  }
268 
269  // ICU capacity shortage is close
270  // TODO: if this is used with vaccination model, it has to be adapted if CriticalPerSevere
271  // is different for different vaccination status. This is not the case here and in addition, ICUCapacity
272  // is set to infinity and this functionality is deactivated, so this is OK for the moment.
273  FP criticalPerSevereAdjusted = smoother_cosine<FP>(
274  icu_occupancy, 0.90 * params.template get<ICUCapacity<FP>>(), params.template get<ICUCapacity<FP>>(),
275  params.template get<CriticalPerSevere<FP>>()[i], 0);
276 
277  FP deathsPerSevereAdjusted = params.template get<CriticalPerSevere<FP>>()[i] - criticalPerSevereAdjusted;
278 
279  /**** path of immune-naive ***/
280  // Exposed
281  flows[this->template get_flat_flow_index<InfectionState::ExposedNaive,
282  InfectionState::InfectedNoSymptomsNaive>({i})] +=
283  y[ENi] / params.template get<TimeExposed<FP>>()[i];
284 
285  // InfectedNoSymptoms
286  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaive,
287  InfectionState::SusceptibleImprovedImmunity>({i})] =
288  params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i] /
289  params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNi];
290  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaive,
291  InfectionState::InfectedSymptomsNaive>({i})] =
292  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
293  params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNi];
294  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaiveConfirmed,
295  InfectionState::InfectedSymptomsNaiveConfirmed>({i})] =
296  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
297  params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNCi];
298  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsNaiveConfirmed,
299  InfectionState::SusceptibleImprovedImmunity>({i})] =
300  params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i] /
301  params.template get<TimeInfectedNoSymptoms<FP>>()[i] * y[INSNCi];
302 
303  // InfectedSymptoms
304  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaive,
305  InfectionState::InfectedSevereNaive>({i})] =
306  params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
307  params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNi];
308 
309  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaive,
310  InfectionState::SusceptibleImprovedImmunity>({i})] =
311  (1 - params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
312  params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNi];
313 
314  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaiveConfirmed,
315  InfectionState::InfectedSevereNaive>({i})] =
316  params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
317  params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNCi];
318 
319  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsNaiveConfirmed,
320  InfectionState::SusceptibleImprovedImmunity>({i})] =
321  (1 - params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
322  params.template get<TimeInfectedSymptoms<FP>>()[i] * y[ISyNCi];
323 
324  // InfectedSevere
325  flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive,
326  InfectionState::InfectedCriticalNaive>({i})] =
327  criticalPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
328 
329  flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive,
330  InfectionState::SusceptibleImprovedImmunity>({i})] =
331  (1 - params.template get<CriticalPerSevere<FP>>()[i] - params.template get<DeathsPerSevere<FP>>()[i]) /
332  params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
333 
334  flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive, InfectionState::DeadNaive>(
335  {i})] = (params.template get<DeathsPerSevere<FP>>()[i] + deathsPerSevereAdjusted) /
336  params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
337 
338  // InfectedCritical
339  flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalNaive, InfectionState::DeadNaive>(
340  {i})] = params.template get<DeathsPerCritical<FP>>()[i] /
341  params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrNi];
342 
343  flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalNaive,
344  InfectionState::SusceptibleImprovedImmunity>({i})] =
345  (1 - params.template get<DeathsPerCritical<FP>>()[i]) /
346  params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrNi];
347 
348  // /**** path of partially immune (e.g., one dose of vaccination) ***/
349  // Exposed
350  flows[this->template get_flat_flow_index<InfectionState::ExposedPartialImmunity,
351  InfectionState::InfectedNoSymptomsPartialImmunity>({i})] +=
352  y[EPIi] / params.template get<TimeExposed<FP>>()[i];
353 
354  // InfectedNoSymptoms
355  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunity,
356  InfectionState::SusceptibleImprovedImmunity>({i})] =
357  (1 - (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
358  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
359  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPIi];
360  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunity,
361  InfectionState::InfectedSymptomsPartialImmunity>({i})] =
362  (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
363  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
364  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPIi];
365  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunityConfirmed,
366  InfectionState::InfectedSymptomsPartialImmunityConfirmed>({i})] =
367  (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
368  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
369  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPICi];
370  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsPartialImmunityConfirmed,
371  InfectionState::SusceptibleImprovedImmunity>({i})] =
372  (1 - (reducInfectedSymptomsPartialImmunity / reducExposedPartialImmunity) *
373  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
374  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSPICi];
375 
376  // InfectedSymptoms
377  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunity,
378  InfectionState::InfectedSeverePartialImmunity>({i})] =
379  reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity *
380  params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
381  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPIi];
382 
383  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunity,
384  InfectionState::SusceptibleImprovedImmunity>({i})] =
385  (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity) *
386  params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
387  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPIi];
388 
389  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunityConfirmed,
390  InfectionState::InfectedSeverePartialImmunity>({i})] =
391  reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity *
392  params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
393  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPICi];
394 
395  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsPartialImmunityConfirmed,
396  InfectionState::SusceptibleImprovedImmunity>({i})] =
397  (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSymptomsPartialImmunity) *
398  params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
399  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyPICi];
400 
401  // InfectedSevere
402  flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
403  InfectionState::InfectedCriticalPartialImmunity>({i})] =
404  reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity *
405  criticalPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
406 
407  flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
408  InfectionState::SusceptibleImprovedImmunity>({i})] =
409  (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
410  (params.template get<CriticalPerSevere<FP>>()[i] +
411  params.template get<DeathsPerSevere<FP>>()[i])) /
412  params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
413 
414  flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
415  InfectionState::DeadPartialImmunity>({i})] =
416  (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
417  (params.template get<DeathsPerSevere<FP>>()[i] + deathsPerSevereAdjusted) /
418  params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
419 
420  // InfectedCritical
421  flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalPartialImmunity,
422  InfectionState::DeadPartialImmunity>({i})] =
423  (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
424  params.template get<DeathsPerCritical<FP>>()[i] / params.template get<TimeInfectedCritical<FP>>()[i] *
425  y[ICrPIi];
426 
427  flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalPartialImmunity,
428  InfectionState::SusceptibleImprovedImmunity>({i})] =
429  (1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
430  params.template get<DeathsPerCritical<FP>>()[i]) /
431  params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrPIi];
432 
433  // /**** path of twice vaccinated, here called immune although reinfection is possible now ***/
434  // Exposed
435  flows[this->template get_flat_flow_index<InfectionState::ExposedImprovedImmunity,
436  InfectionState::InfectedNoSymptomsImprovedImmunity>({i})] +=
437  y[EIIi] / params.template get<TimeExposed<FP>>()[i];
438 
439  // InfectedNoSymptoms
440  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunity,
441  InfectionState::SusceptibleImprovedImmunity>({i})] =
442  (1 - (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
443  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
444  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIIi];
445  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunity,
446  InfectionState::InfectedSymptomsImprovedImmunity>({i})] =
447  (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
448  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
449  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIIi];
450  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed,
451  InfectionState::InfectedSymptomsImprovedImmunityConfirmed>({i})] =
452  (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
453  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i]) /
454  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIICi];
455  flows[this->template get_flat_flow_index<InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed,
456  InfectionState::SusceptibleImprovedImmunity>({i})] =
457  (1 - (reducInfectedSymptomsImprovedImmunity / reducExposedImprovedImmunity) *
458  (1 - params.template get<RecoveredPerInfectedNoSymptoms<FP>>()[i])) /
459  (params.template get<TimeInfectedNoSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[INSIICi];
460 
461  // InfectedSymptoms
462  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunity,
463  InfectionState::InfectedSevereImprovedImmunity>({i})] =
464  reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity *
465  params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
466  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIIi];
467 
468  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunity,
469  InfectionState::SusceptibleImprovedImmunity>({i})] =
470  (1 - (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity) *
471  params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
472  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIIi];
473 
474  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunityConfirmed,
475  InfectionState::InfectedSevereImprovedImmunity>({i})] =
476  reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity *
477  params.template get<SeverePerInfectedSymptoms<FP>>()[i] /
478  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIICi];
479 
480  flows[this->template get_flat_flow_index<InfectionState::InfectedSymptomsImprovedImmunityConfirmed,
481  InfectionState::SusceptibleImprovedImmunity>({i})] =
482  (1 - (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSymptomsImprovedImmunity) *
483  params.template get<SeverePerInfectedSymptoms<FP>>()[i]) /
484  (params.template get<TimeInfectedSymptoms<FP>>()[i] * reducTimeInfectedMild) * y[ISyIICi];
485 
486  // InfectedSevere
487  flows[this->template get_flat_flow_index<InfectionState::InfectedSevereImprovedImmunity,
488  InfectionState::InfectedCriticalImprovedImmunity>({i})] =
489  reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity *
490  criticalPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
491 
492  flows[this->template get_flat_flow_index<InfectionState::InfectedSevereImprovedImmunity,
493  InfectionState::SusceptibleImprovedImmunity>({i})] =
494  (1 -
495  (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
496  (params.template get<CriticalPerSevere<FP>>()[i] +
497  params.template get<DeathsPerSevere<FP>>()[i])) /
498  params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
499 
500  flows[this->template get_flat_flow_index<InfectionState::InfectedSevereImprovedImmunity,
501  InfectionState::DeadImprovedImmunity>({i})] =
502  (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
503  (params.template get<DeathsPerSevere<FP>>()[i] + deathsPerSevereAdjusted) /
504  params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
505 
506  // InfectedCritical
507  flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalImprovedImmunity,
508  InfectionState::DeadImprovedImmunity>({i})] =
509  (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
510  params.template get<DeathsPerCritical<FP>>()[i] / params.template get<TimeInfectedCritical<FP>>()[i] *
511  y[ICrIIi];
512 
513  flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalImprovedImmunity,
514  InfectionState::SusceptibleImprovedImmunity>({i})] =
515  (1 -
516  (reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
517  params.template get<DeathsPerCritical<FP>>()[i]) /
518  params.template get<TimeInfectedCritical<FP>>()[i] * y[ICrIIi];
519  }
520  }
521 
526  template <class IOContext>
527  void serialize(IOContext& io) const
528  {
529  auto obj = io.create_object("Model");
530  obj.add_element("Parameters", this->parameters);
531  obj.add_element("Populations", this->populations);
532  }
533 
538  template <class IOContext>
539  static IOResult<Model> deserialize(IOContext& io)
540  {
541  auto obj = io.expect_object("Model");
542  auto par = obj.expect_element("Parameters", Tag<ParameterSet>{});
543  auto pop = obj.expect_element("Populations", Tag<Populations>{});
544  return apply(
545  io,
546  [](auto&& par_, auto&& pop_) {
547  return Model{pop_, par_};
548  },
549  par, pop);
550  }
551 };
552 
553 //forward declaration, see below.
554 template <typename FP, class Base = mio::Simulation<FP, Model<FP>>>
555 class Simulation;
556 
564 template <typename FP, class Base = mio::Simulation<FP, Model<FP>>>
565 FP get_infections_relative(const Simulation<FP, Base>& model, FP t, const Eigen::Ref<const Eigen::VectorX<FP>>& y);
566 
572 template <typename FP, class BaseT>
573 class Simulation : public BaseT
574 {
575 public:
582  Simulation(mio::osecirvvs::Model<FP> const& model, FP t0 = 0., FP dt = 0.1)
583  : BaseT(model, t0, dt)
584  {
585  }
586 
599  void apply_variant(const FP t, const CustomIndexArray<UncertainValue<FP>, AgeGroup> base_infectiousness)
600  {
601  using std::min;
602  using std::pow;
603 
604  auto start_day = this->get_model().parameters.template get<StartDay<FP>>();
605  auto start_day_new_variant = this->get_model().parameters.template get<StartDayNewVariant<FP>>();
606 
607  if (start_day + t >= start_day_new_variant - 1e-10) {
608  const FP days_variant = t - (start_day_new_variant - start_day);
609  const FP share_new_variant = min<FP>(1.0, 0.01 * pow(2, (1. / 7) * days_variant));
610  const auto num_groups = this->get_model().parameters.get_num_groups();
611  for (auto i = AgeGroup(0); i < num_groups; ++i) {
612  FP new_transmission = (1 - share_new_variant) * base_infectiousness[i] +
613  share_new_variant * base_infectiousness[i] *
614  this->get_model().parameters.template get<InfectiousnessNewVariant<FP>>()[i];
615  this->get_model().parameters.template get<TransmissionProbabilityOnContact<FP>>()[i] = new_transmission;
616  }
617  }
618  }
619 
620  void apply_vaccination(FP t)
621  {
622  using std::floor;
623  auto t_idx = SimulationDay(size_t(floor(t)));
624  auto& params = this->get_model().parameters;
625  size_t num_groups = (size_t)params.get_num_groups();
626  auto last_value = this->get_result().get_last_value();
627 
628  auto count = (size_t)InfectionState::Count;
629  auto S = (size_t)InfectionState::SusceptibleNaive;
630  auto SV = (size_t)InfectionState::SusceptiblePartialImmunity;
631  auto R = (size_t)InfectionState::SusceptibleImprovedImmunity;
632 
633  for (size_t i = 0; i < num_groups; ++i) {
634 
635  FP first_vacc;
636  FP full_vacc;
637  if (t_idx == SimulationDay(0)) {
638  first_vacc = params.template get<DailyPartialVaccinations<FP>>()[{(AgeGroup)i, t_idx}];
639  full_vacc = params.template get<DailyFullVaccinations<FP>>()[{(AgeGroup)i, t_idx}];
640  }
641  else {
642  first_vacc =
643  params.template get<DailyPartialVaccinations<FP>>()[{(AgeGroup)i, t_idx}] -
644  params.template get<DailyPartialVaccinations<FP>>()[{(AgeGroup)i, t_idx - SimulationDay(1)}];
645  full_vacc = params.template get<DailyFullVaccinations<FP>>()[{(AgeGroup)i, t_idx}] -
646  params.template get<DailyFullVaccinations<FP>>()[{(AgeGroup)i, t_idx - SimulationDay(1)}];
647  }
648 
649  if (last_value(count * i + S) - first_vacc < 0) {
650  FP corrected = 0.99 * last_value(count * i + S);
651  log_warning("too many first vaccinated at time {}: setting first_vacc from {} to {}", t, first_vacc,
652  corrected);
653  first_vacc = corrected;
654  }
655 
656  last_value(count * i + S) -= first_vacc;
657  last_value(count * i + SV) += first_vacc;
658 
659  if (last_value(count * i + SV) - full_vacc < 0) {
660  FP corrected = 0.99 * last_value(count * i + SV);
661  log_warning("too many fully vaccinated at time {}: setting full_vacc from {} to {}", t, full_vacc,
662  corrected);
663  full_vacc = corrected;
664  }
665 
666  last_value(count * i + SV) -= full_vacc;
667  last_value(count * i + R) += full_vacc;
668  }
669  }
670 
678  Eigen::Ref<Eigen::VectorX<FP>> advance(FP tmax)
679  {
680  using std::floor;
681  using std::min;
682 
683  auto& dyn_npis = this->get_model().parameters.template get<DynamicNPIsInfectedSymptoms<FP>>();
684  auto& contact_patterns = this->get_model().parameters.template get<ContactPatterns<FP>>();
685  // const size_t num_groups = (size_t)this->get_model().parameters.get_num_groups();
686 
687  // in the apply_variant function, we adjust the TransmissionProbabilityOnContact parameter. We need to store
688  // the base value to use it in the apply_variant function and also to reset the parameter after the simulation.
689  auto base_infectiousness = this->get_model().parameters.template get<TransmissionProbabilityOnContact<FP>>();
690 
691  FP delay_npi_implementation;
692  FP t = BaseT::get_result().get_last_time();
693  while (t < tmax) {
694 
695  if (t > 0) {
696  delay_npi_implementation = FP(dyn_npis.get_implementation_delay());
697  }
698  else { // DynamicNPIs for t=0 are treated as 'from-start NPIs'. I.e., do not enforce delay.
699  delay_npi_implementation = 0;
700  }
701  if (t == 0) {
702  //this->apply_vaccination(t); // done in init now?
703  this->apply_variant(t, base_infectiousness);
704  }
705 
706  if (dyn_npis.get_thresholds().size() > 0) {
707  FP direc_begin = FP(dyn_npis.get_directive_begin());
708  FP direc_end = FP(dyn_npis.get_directive_end());
709  if (floating_point_greater_equal(t, direc_begin, 1e-10) && t < direc_end) {
710  auto inf_rel = get_infections_relative<FP>(*this, t, this->get_result().get_last_value()) *
711  dyn_npis.get_base_value();
712  auto exceeded_threshold = dyn_npis.get_max_exceeded_threshold(inf_rel);
713  const bool npi_expired = floating_point_greater_equal(t, FP(m_dynamic_npi.second), 1e-10);
714  if (exceeded_threshold != dyn_npis.get_thresholds().end() &&
715  (exceeded_threshold->first > m_dynamic_npi.first || npi_expired)) {
716  //old npi was weaker or is expired
717 
718  // Keep-alive: if the NPI expired but the threshold is still exceeded at the
719  // same level, renew immediately without delay to avoid a gap.
720  // Apply implementation delay only if stronger NPI needed.
721  bool is_expiry_renewal =
722  npi_expired && !(exceeded_threshold->first > m_dynamic_npi.first);
723  FP effective_delay = is_expiry_renewal ? FP(0) : delay_npi_implementation;
724  if (t + effective_delay < direc_end) {
725  auto t_start = SimulationTime<FP>(t + effective_delay);
726  // set the end to the minimum of start+duration and the end of the directive
727  auto t_end = SimulationTime<FP>(min<FP>(direc_end, FP(t_start + dyn_npis.get_duration())));
728  this->get_model().parameters.get_start_commuter_detection() = (FP)t_start;
729  this->get_model().parameters.get_end_commuter_detection() = (FP)t_end;
730  m_dynamic_npi = std::make_pair(exceeded_threshold->first, t_end);
731  // For new NPIs (t_start > 0): shift t_start_damping by +1 so the smooth
732  // transition window [t_start, t_start+1] lies in the future, consistent with
733  // predefined dampings. For t_start = 0 (global t0): the window [-1, 0]
734  // is in the past and no shift is needed.
735  // For keep-alive renewals (is_expiry_renewal): do not shift t_start_damping.
736  // Since t_start == t_end_damping_old, the new start damping has the same
737  // (time, level, type) as the previous entry.
738  // Therefore, the contact matrix stays constant at the NPI level with no dip.
739  auto t_start_damping = (!is_expiry_renewal && FP(t_start) > FP(0))
740  ? SimulationTime<FP>(FP(t_start) + FP(1))
741  : t_start;
742  auto t_end_damping = SimulationTime<FP>(FP(t_end) + FP(1));
743  implement_dynamic_npis<FP>(contact_patterns.get_cont_freq_mat(), exceeded_threshold->second,
744  t_start_damping, t_end_damping, [](auto& g) {
745  return make_contact_damping_matrix(g);
746  });
747  }
748  }
749  }
750  }
751 
752  FP dt_eff = min<FP>(1.0, tmax - t);
753  // Clamp step size at the NPI end time to avoid stepping over the lifting phase.
754  // This ensures the keep-alive check activates exactly at t_end, preventing a brief dip.
755  FP npi_end = FP(m_dynamic_npi.second);
756  if (t < npi_end) {
757  dt_eff = min<FP>(dt_eff, npi_end - t);
758  }
759  BaseT::advance(t + dt_eff);
760  if (t + 0.5 + dt_eff - floor(t + 0.5) >= 1) {
761  this->apply_vaccination(t + 0.5 + dt_eff);
762  this->apply_variant(t, base_infectiousness);
763  }
764  t = t + dt_eff;
765  }
766  // reset TransmissionProbabilityOnContact. This is important for the graph simulation where the advance
767  // function is called multiple times for the same model.
768  this->get_model().parameters.template get<TransmissionProbabilityOnContact<FP>>() = base_infectiousness;
769 
770  return this->get_result().get_last_value();
771  }
772 
773 private:
774  std::pair<FP, SimulationTime<FP>> m_dynamic_npi = {-std::numeric_limits<FP>::max(), SimulationTime<FP>(0)};
775 };
776 
789 template <typename FP>
790 inline auto simulate(FP t0, FP tmax, FP dt, const Model<FP>& model,
791  std::unique_ptr<OdeIntegratorCore<FP>>&& integrator_core = nullptr)
792 {
793  return mio::simulate<FP, Model<FP>, Simulation<FP>>(t0, tmax, dt, model, std::move(integrator_core));
794 }
795 
808 template <typename FP>
809 inline auto simulate_flows(FP t0, FP tmax, FP dt, const Model<FP>& model,
810  std::unique_ptr<OdeIntegratorCore<FP>>&& integrator_core = nullptr)
811 {
812  return mio::simulate_flows<FP, Model<FP>, Simulation<FP, mio::FlowSimulation<FP, Model<FP>>>>(
813  t0, tmax, dt, model, std::move(integrator_core));
814 }
815 
816 //see declaration above.
817 template <typename FP, class Base>
818 FP get_infections_relative(const Simulation<FP, Base>& sim, FP /*t*/, const Eigen::Ref<const Eigen::VectorX<FP>>& y)
819 {
820  FP sum_inf = 0;
821  for (auto i = AgeGroup(0); i < sim.get_model().parameters.get_num_groups(); ++i) {
822  sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsNaive});
823  sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsNaiveConfirmed});
824  sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsPartialImmunity});
825  sum_inf += sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsImprovedImmunity});
826  sum_inf +=
827  sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
828  sum_inf +=
829  sim.get_model().populations.get_from(y, {i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
830  }
831  auto inf_rel = sum_inf / sim.get_model().populations.get_total();
832 
833  return inf_rel;
834 }
835 
846 template <typename FP, class Base = mio::Simulation<FP, Model<FP>>>
847 auto get_mobility_factors(const Simulation<Base>& sim, FP /*t*/, const Eigen::Ref<const Eigen::VectorX<FP>>& y)
848 
849 {
850  auto& params = sim.get_model().parameters;
851  //parameters as arrays
852  auto& p_asymp = params.template get<RecoveredPerInfectedNoSymptoms<FP>>().array().template cast<FP>();
853  auto& p_inf = params.template get<RiskOfInfectionFromSymptomatic<FP>>().array().template cast<FP>();
854  auto& p_inf_max = params.template get<MaxRiskOfInfectionFromSymptomatic<FP>>().array().template cast<FP>();
855  //slice of InfectedNoSymptoms
856  auto y_INS = slice(y, {Eigen::Index(InfectionState::InfectedNoSymptomsNaive),
857  Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) +
858  slice(y, {Eigen::Index(InfectionState::InfectedNoSymptomsPartialImmunity),
859  Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)}) +
860  slice(y, {Eigen::Index(InfectionState::InfectedNoSymptomsImprovedImmunity),
861  Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)});
862 
863  //compute isolation, same as infection risk from main model
864  auto test_and_trace_required =
865  ((1 - p_asymp) / params.template get<TimeInfectedNoSymptoms<FP>>().array().template cast<FP>() * y_INS.array())
866  .sum();
867  auto riskFromInfectedSymptomatic =
868  smoother_cosine<FP>(test_and_trace_required, FP(params.template get<TestAndTraceCapacity<FP>>()),
869  params.template get<TestAndTraceCapacity<FP>>() *
870  params.template get<TestAndTraceCapacityMaxRiskSymptoms<FP>>(),
871  p_inf.matrix(), p_inf_max.matrix());
872 
873  //set factor for infected
874  auto factors = Eigen::VectorX<FP>::Ones(y.rows()).eval();
875  slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsNaive), Eigen::Index(size_t(params.get_num_groups())),
876  Eigen::Index(InfectionState::Count)})
877  .array() = riskFromInfectedSymptomatic;
878  slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsPartialImmunity),
879  Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)})
880  .array() = riskFromInfectedSymptomatic;
881  slice(factors, {Eigen::Index(InfectionState::InfectedSymptomsImprovedImmunity),
882  Eigen::Index(size_t(params.get_num_groups())), Eigen::Index(InfectionState::Count)})
883  .array() = riskFromInfectedSymptomatic;
884  return factors;
885 }
886 
887 template <typename FP, class Base = mio::Simulation<FP, Model<FP>>>
888 auto test_commuters(Simulation<FP, Base>& sim, Eigen::Ref<Eigen::VectorX<FP>> mobile_population, FP time)
889 {
890  auto& model = sim.get_model();
891  FP nondetection = 1.0;
892  if (time >= model.parameters.get_start_commuter_detection() &&
893  time < model.parameters.get_end_commuter_detection()) {
894  nondetection = (FP)model.parameters.get_commuter_nondetection();
895  }
896  for (auto i = AgeGroup(0); i < model.parameters.get_num_groups(); ++i) {
897  auto ISyNi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsNaive});
898  auto ISyNCi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsNaiveConfirmed});
899  auto INSNi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaive});
900  auto INSNCi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsNaiveConfirmed});
901 
902  auto ISPIi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunity});
903  auto ISPICi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsPartialImmunityConfirmed});
904  auto INSPIi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunity});
905  auto INSPICi =
906  model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed});
907 
908  auto ISyIIi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunity});
909  auto ISyIICi = model.populations.get_flat_index({i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed});
910  auto INSIIi = model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunity});
911  auto INSIICi =
912  model.populations.get_flat_index({i, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed});
913 
914  //put detected commuters in their own compartment so they don't contribute to infections in their home node
915  sim.get_result().get_last_value()[ISyNi] -= mobile_population[ISyNi] * (1 - nondetection);
916  sim.get_result().get_last_value()[ISyNCi] += mobile_population[ISyNi] * (1 - nondetection);
917  sim.get_result().get_last_value()[INSNi] -= mobile_population[INSNi] * (1 - nondetection);
918  sim.get_result().get_last_value()[INSNCi] += mobile_population[INSNi] * (1 - nondetection);
919 
920  sim.get_result().get_last_value()[ISPIi] -= mobile_population[ISPIi] * (1 - nondetection);
921  sim.get_result().get_last_value()[ISPICi] += mobile_population[ISPIi] * (1 - nondetection);
922  sim.get_result().get_last_value()[INSPIi] -= mobile_population[INSPIi] * (1 - nondetection);
923  sim.get_result().get_last_value()[INSPICi] += mobile_population[INSPIi] * (1 - nondetection);
924 
925  sim.get_result().get_last_value()[ISyIIi] -= mobile_population[ISyIIi] * (1 - nondetection);
926  sim.get_result().get_last_value()[ISyIICi] += mobile_population[ISyIIi] * (1 - nondetection);
927  sim.get_result().get_last_value()[INSIIi] -= mobile_population[INSIIi] * (1 - nondetection);
928  sim.get_result().get_last_value()[INSIICi] += mobile_population[INSIIi] * (1 - nondetection);
929 
930  //reduce the number of commuters
931  mobile_population[ISyNi] *= nondetection;
932  mobile_population[INSNi] *= nondetection;
933 
934  mobile_population[ISPIi] *= nondetection;
935  mobile_population[INSPIi] *= nondetection;
936 
937  mobile_population[ISyIIi] *= nondetection;
938  mobile_population[INSIIi] *= nondetection;
939  }
940 }
941 
942 } // namespace osecirvvs
943 } // namespace mio
944 
945 #endif //MIO_ODE_SECIRVVS_MODEL_H
represents a collection of contact frequency matrices that whose sum is the total number of contacts.
Definition: contact_matrix.h:536
A class template for an array with custom indices.
Definition: custom_index_array.h:136
A FlowModel is a CompartmentalModel defined by the flows between compartments.
Definition: flow_model.h:74
Interface class defining the integration step used in a SystemIntegrator.
Definition: integrator.h:48
A class template for compartment populations.
Definition: populations.h:55
decltype(auto) get_from(Arr &&y, Index const &cats) const
get_from returns the value of a flat container at the flat index corresponding to set of enum values.
Definition: populations.h:123
Represents the simulation time as an integer index.
Definition: simulation_day.h:32
Typesafe wrapper for a floating-point simulation time value (in days).
Definition: damping.h:78
A class for simulating a CompartmentalModel.
Definition: memilio/compartments/simulation.h:43
Definition: ode_secirvvs/model.h:94
void serialize(IOContext &io) const
serialize this.
Definition: ode_secirvvs/model.h:527
Model(const Populations &pop, const ParameterSet &params)
Definition: ode_secirvvs/model.h:101
static IOResult< Model > deserialize(IOContext &io)
deserialize an object of this class.
Definition: ode_secirvvs/model.h:539
void get_flows(Eigen::Ref< const Eigen::VectorX< FP >> pop, Eigen::Ref< const Eigen::VectorX< FP >> y, FP t, Eigen::Ref< Eigen::VectorX< FP >> flows) const override
Definition: ode_secirvvs/model.h:111
Model(int num_agegroups)
Definition: ode_secirvvs/model.h:106
Parameters of an age-resolved SECIR/SECIHURD model with paths for partial and improved immunity throu...
Definition: ode_secirvvs/parameters.h:646
specialization of compartment model simulation for the SECIRVVS model.
Definition: ode_secirvvs/model.h:574
void apply_vaccination(FP t)
Definition: ode_secirvvs/model.h:620
Eigen::Ref< Eigen::VectorX< FP > > advance(FP tmax)
advance simulation to tmax.
Definition: ode_secirvvs/model.h:678
Simulation(mio::osecirvvs::Model< FP > const &model, FP t0=0., FP dt=0.1)
construct a simulation.
Definition: ode_secirvvs/model.h:582
void apply_variant(const FP t, const CustomIndexArray< UncertainValue< FP >, AgeGroup > base_infectiousness)
Applies the effect of a new variant of a disease to the transmission probability of the model.
Definition: ode_secirvvs/model.h:599
ad::internal::unary_intermediate< AD_TAPE_REAL, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::operations::ad_sin< AD_TAPE_REAL > > sin(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &x1)
Definition: ad.hpp:913
static double floor(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &x)
Definition: ad.hpp:2451
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type min(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2599
ad::internal::binary_intermediate_aa< AD_TAPE_REAL, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::operations::ad_pow_aa< AD_TAPE_REAL > > pow(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &x1, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &x2)
Definition: ad.hpp:1610
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type max(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2596
auto get_mobility_factors(const Simulation< Base > &sim, FP, const Eigen::Ref< const Eigen::VectorX< FP >> &y)
Get mobility factors.
Definition: ode_secirvvs/model.h:847
auto test_commuters(Simulation< FP, Base > &sim, Eigen::Ref< Eigen::VectorX< FP >> mobile_population, FP time)
Definition: ode_secirvvs/model.h:888
FP get_infections_relative(const Simulation< FP, Base > &model, FP t, const Eigen::Ref< const Eigen::VectorX< FP >> &y)
get percentage of infections per total population.
Definition: ode_secirvvs/model.h:818
auto simulate(FP t0, FP tmax, FP dt, const Model< FP > &model, std::unique_ptr< OdeIntegratorCore< FP >> &&integrator_core=nullptr)
Specialization of simulate for SECIRVVS models using Simulation.
Definition: ode_secirvvs/model.h:790
auto simulate_flows(FP t0, FP tmax, FP dt, const Model< FP > &model, std::unique_ptr< OdeIntegratorCore< FP >> &&integrator_core=nullptr)
Specialization of simulate for SECIRVVS models using the FlowSimulation.
Definition: ode_secirvvs/model.h:809
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:408
auto i
Definition: io.h:810
void log_warning(spdlog::string_view_t fmt, const Args &... args)
Definition: logging.h:126
details::ApplyResultT< F, T... > apply(IOContext &io, F f, const IOResult< T > &... rs)
Evaluate a function with zero or more unpacked IOResults as arguments.
Definition: io.h:482
auto slice(V &&v, Seq< Eigen::Index > elems)
take a regular slice of a row or column vector.
Definition: eigen_util.h:107
bool floating_point_greater_equal(FP v1, FP v2, FP abs_tol=0, FP rel_tol=std::numeric_limits< FP >::min())
compare two floating point values with tolerances.
Definition: floating_point.h:136
constexpr std::tuple_element< I, std::tuple< Index< CategoryTags >... > >::type & get(Index< CategoryTags... > &i) noexcept
Retrieves the Index (by reference) at the Ith position of a MultiIndex.
Definition: index.h:304
boost::outcome_v2::unchecked< T, IOStatus > IOResult
Value-or-error type for operations that return a value but can fail.
Definition: io.h:354
Typesafe index representing an age group.
Definition: age_group.h:40
mio::Populations< FP, AgeGroup, InfectionState > Populations
Definition: compartmental_model.h:62
A Flow defines a possible transition between two Compartments in a FlowModel.
Definition: flow.h:33
Collection of types. Each type is mapped to an index of type size_t.
Definition: type_list.h:32
Multiplier for the test and trace capacity to determine when it is considered overloaded by symptomat...
Definition: ode_secirvvs/parameters.h:144
capacity to test and trace contacts of infected for quarantine per day.
Definition: ode_secirvvs/parameters.h:112
the (mean) time in day unit for asymptomatic cases that are infected but have not yet developed sympt...
Definition: ode_secirvvs/parameters.h:209