model.h Source File

CPP API: model.h Source File
lct_secir_2_diseases/model.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Annika Jungklaus, Lena Ploetzke
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 
21 #ifndef LCT_SECIR_2_DISEASE_MODEL_H
22 #define LCT_SECIR_2_DISEASE_MODEL_H
23 
28 #include "memilio/config.h"
30 #include "memilio/utils/logging.h"
33 
34 namespace mio
35 {
36 namespace lsecir2d
37 {
49 template <typename FP, class... LctStates>
50 class Model : public CompartmentalModel<FP, InfectionState, LctPopulations<FP, LctStates...>, Parameters<FP>>
51 {
52 public:
53  using LctStatesGroups = TypeList<LctStates...>;
55  using typename Base::ParameterSet;
56  using typename Base::Populations;
57  static size_t constexpr num_groups = sizeof...(LctStates);
58  static_assert(num_groups >= 1, "The number of LctStates provided should be at least one.");
59 
63  {
64  }
65 
70  Model(const Populations& pop, const ParameterSet& params)
71  : Base(pop, params)
72  {
73  }
74 
86  void get_derivatives(Eigen::Ref<const Eigen::VectorX<FP>> pop, Eigen::Ref<const Eigen::VectorX<FP>> y, FP t,
87  Eigen::Ref<Eigen::VectorX<FP>> dydt) const override
88  {
89  // Vectors are sorted such that we first have all InfectionState%s for AgeGroup 0,
90  // afterwards all for AgeGroup 1 and so on.
91  dydt.setZero();
92  get_derivatives_impl(pop, y, t, dydt);
93  }
94 
107  TimeSeries<FP> calculate_compartments(const TimeSeries<FP>& subcompartments_ts) const
108  {
109  Eigen::Index count_InfStates = (Eigen::Index)InfectionState::Count;
110  Eigen::Index num_compartments = count_InfStates * num_groups;
111  TimeSeries<FP> compartments_ts(num_compartments);
112  if (!(this->populations.get_num_compartments() == (size_t)subcompartments_ts.get_num_elements())) {
113  log_error("Result does not match InfectionState of the Model.");
114  Eigen::VectorX<FP> wrong_size = Eigen::VectorX<FP>::Constant(num_compartments, -1);
115  compartments_ts.add_time_point(-1, wrong_size);
116  return compartments_ts;
117  }
118  Eigen::VectorX<FP> compartments(num_compartments);
119  for (Eigen::Index timepoint = 0; timepoint < subcompartments_ts.get_num_time_points(); ++timepoint) {
120  compress_vector(subcompartments_ts[timepoint], compartments);
121  compartments_ts.add_time_point(subcompartments_ts.get_time(timepoint), compartments);
122  }
123 
124  return compartments_ts;
125  }
126 
131  bool check_constraints() const
132  {
133 
134  return (check_constraints_impl() || this->parameters.check_constraints() ||
135  this->populations.check_constraints());
136  }
137 
138 private:
148  template <size_t Group = 0>
149  void compress_vector(const Eigen::VectorX<FP>& subcompartments, Eigen::VectorX<FP>& compartments) const
150  {
151  static_assert((Group < num_groups) && (Group >= 0), "The template parameter Group should be valid.");
152  using LctStateGroup = type_at_index_t<Group, LctStates...>;
153 
154  // Define first index of the group Group in a vector including all compartments without a resolution
155  // in subcompartments.
156  Eigen::Index count_InfStates = (Eigen::Index)InfectionState::Count;
157  Eigen::Index first_index_group_comps = Group * count_InfStates;
158 
159  // Use function from the LctState of the Group to calculate the vector without subcompartments
160  // using the corresponding vector with subcompartments.
161  compartments.segment(first_index_group_comps, count_InfStates) =
162  LctStateGroup::calculate_compartments(subcompartments.segment(
163  this->populations.template get_first_index_of_group<Group>(), LctStateGroup::Count));
164 
165  // Function call for next group if applicable.
166  if constexpr (Group + 1 < num_groups) {
167  compress_vector<Group + 1>(subcompartments, compartments);
168  }
169  }
170 
182  template <size_t Group = 0>
183  void get_derivatives_impl(Eigen::Ref<const Eigen::VectorX<FP>> pop, Eigen::Ref<const Eigen::VectorX<FP>> y, FP t,
184  Eigen::Ref<Eigen::VectorX<FP>> dydt) const
185  {
186  static_assert((Group < num_groups) && (Group >= 0), "The template parameter Group should be valid.");
187  using LctStateGroup = type_at_index_t<Group, LctStates...>;
188 
189  size_t first_index_group = this->populations.template get_first_index_of_group<Group>();
190  const auto& params = this->parameters;
191  FP flow = 0;
192 
193  // Indices of first subcompartment of the InfectionState for the group in the vectors.
194  size_t Ei_1a_first_index =
195  first_index_group + LctStateGroup::template get_first_index<InfectionState::Exposed_1a>();
196  size_t Ei_2a_first_index =
197  first_index_group + LctStateGroup::template get_first_index<InfectionState::Exposed_2a>();
198  size_t Ei_1b_first_index =
199  first_index_group + LctStateGroup::template get_first_index<InfectionState::Exposed_1b>();
200  size_t Ei_2b_first_index =
201  first_index_group + LctStateGroup::template get_first_index<InfectionState::Exposed_2b>();
202  size_t INSi_1a_first_index =
203  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedNoSymptoms_1a>();
204  size_t INSi_2a_first_index =
205  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedNoSymptoms_2a>();
206  size_t INSi_1b_first_index =
207  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedNoSymptoms_1b>();
208  size_t INSi_2b_first_index =
209  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedNoSymptoms_2b>();
210  size_t ISyi_1a_first_index =
211  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms_1a>();
212  size_t ISyi_2a_first_index =
213  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms_2a>();
214  size_t ISyi_1b_first_index =
215  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms_1b>();
216  size_t ISyi_2b_first_index =
217  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms_2b>();
218  size_t ISevi_1a_first_index =
219  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSevere_1a>();
220  size_t ISevi_2a_first_index =
221  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSevere_2a>();
222  size_t ISevi_1b_first_index =
223  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSevere_1b>();
224  size_t ISevi_2b_first_index =
225  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSevere_2b>();
226  size_t ICri_1a_first_index =
227  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical_1a>();
228  size_t ICri_2a_first_index =
229  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical_2a>();
230  size_t ICri_1b_first_index =
231  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical_1b>();
232  size_t ICri_2b_first_index =
233  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical_2b>();
234  size_t Ri_a = first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered_1a>();
235  size_t Ri_b = first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered_1b>();
236  size_t Ri_ab = first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered_2ab>();
237  size_t Di_a = first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead_a>();
238  size_t Di_b = first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead_b>();
239 
240  // Calculate derivative of the Susceptible compartment.
241  // The outflow is generated by disease a and disease b both.
242  // To split the outflow correctly, we need the proportion of people infected with each disease which only depends
243  // on the disease-specific parameters (not on e.g. the contact patterns or the seasonality), calculated in the interact function
244  FP part_a = 0.; // Part of people getting infected with disease a.
245  FP part_b = 0.; // Part of people getting infected with disease b.
246  interact<Group, 0>(pop, y, t, dydt, &part_a, &part_b, 2);
247  // Split flow.
248  FP div_part_both = ((part_a + part_b) < Limits<FP>::zero_tolerance()) ? 0.0 : 1.0 / (part_a + part_b);
249 
250  // Start with derivatives of first infections (X_1a, X_1b).
251  // Calculate derivative of the Exposed_1x compartments, split flow from Susceptible.
252  // Exposed 1 a:
253  dydt[Ei_1a_first_index] = -dydt[first_index_group] * part_a * div_part_both;
254  for (size_t subcomp = 0;
255  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_1a>(); subcomp++) {
256  // Variable flow stores the value of the flow from one subcompartment to the next one.
257  // Ei_1a_first_index + subcomp is always the index of a (sub-)compartment of Exposed_1a and Ei_1a_first_index
258  // + subcomp + 1 can also be the index of the first (sub-)compartment of InfectedNoSymptoms.
259  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_1a>() *
260  (1 / params.template get<TimeExposed_a<FP>>()[Group]) * y[Ei_1a_first_index + subcomp];
261  // Subtract flow from dydt[Ei_1a_first_index + subcomp] and add to next subcompartment.
262  dydt[Ei_1a_first_index + subcomp] -= flow;
263  dydt[Ei_1a_first_index + subcomp + 1] = flow;
264  }
265  // Exposed 1 b:
266  dydt[Ei_1b_first_index] = -dydt[first_index_group] * part_b * div_part_both;
267  for (size_t subcomp = 0;
268  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_1b>(); subcomp++) {
269  // Variable flow stores the value of the flow from one subcompartment to the next one.
270  // Ei_1a_first_index + subcomp is always the index of a (sub-)compartment of Exposed_1a and Ei_1b_first_index
271  // + subcomp + 1 can also be the index of the first (sub-)compartment of InfectedNoSymptoms.
272  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_1b>() *
273  (1 / params.template get<TimeExposed_b<FP>>()[Group]) * y[Ei_1b_first_index + subcomp];
274  // Subtract flow from dydt[Ei_1b_first_index + subcomp] and add to next subcompartment.
275  dydt[Ei_1b_first_index + subcomp] -= flow;
276  dydt[Ei_1b_first_index + subcomp + 1] = flow;
277  }
278 
279  // Calculate derivative of the InfectedNoSymptoms_1a compartment
280  // flow from each subcompartment to the next.
281  for (size_t subcomp = 0;
282  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_1a>();
283  subcomp++) {
284  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_1a>() *
285  (1 / params.template get<TimeInfectedNoSymptoms_a<FP>>()[Group]) * y[INSi_1a_first_index + subcomp];
286  dydt[INSi_1a_first_index + subcomp] -= flow;
287  dydt[INSi_1a_first_index + subcomp + 1] = flow;
288  }
289  // Calculate derivative of the InfectedNoSymptoms_1b compartment
290  // flow from each subcompartment to the next.
291  for (size_t subcomp = 0;
292  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_1b>();
293  subcomp++) {
294  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_1b>() *
295  (1 / params.template get<TimeInfectedNoSymptoms_b<FP>>()[Group]) * y[INSi_1b_first_index + subcomp];
296  dydt[INSi_1b_first_index + subcomp] -= flow;
297  dydt[INSi_1b_first_index + subcomp + 1] = flow;
298  }
299 
300  // Calculate derivative of the InfectedSymptoms_1a compartment.
301  // Flow from last (sub-) compartment of InfectedNoSymptoms_1a must be split between
302  // the first subcompartment of InfectedSymptoms_1a and Recovered_1a.
303  dydt[Ri_a] = dydt[ISyi_1a_first_index] * params.template get<RecoveredPerInfectedNoSymptoms_a<FP>>()[Group];
304  dydt[ISyi_1a_first_index] =
305  dydt[ISyi_1a_first_index] * (1 - params.template get<RecoveredPerInfectedNoSymptoms_a<FP>>()[Group]);
306  for (size_t subcomp = 0;
307  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_1a>();
308  subcomp++) {
309  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_1a>() *
310  (1 / params.template get<TimeInfectedSymptoms_a<FP>>()[Group]) * y[ISyi_1a_first_index + subcomp];
311  dydt[ISyi_1a_first_index + subcomp] -= flow;
312  dydt[ISyi_1a_first_index + subcomp + 1] = flow;
313  }
314  // Calculate derivative of the InfectedSymptoms_1b compartment.
315  // Flow from last (sub-) compartment of InfectedNoSymptoms_1b must be split between
316  // the first subcompartment of InfectedSymptoms_1b and Recovered_1b.
317  dydt[Ri_b] = dydt[ISyi_1b_first_index] * params.template get<RecoveredPerInfectedNoSymptoms_b<FP>>()[Group];
318  dydt[ISyi_1b_first_index] =
319  dydt[ISyi_1b_first_index] * (1 - params.template get<RecoveredPerInfectedNoSymptoms_b<FP>>()[Group]);
320  for (size_t subcomp = 0;
321  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_1b>();
322  subcomp++) {
323  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_1b>() *
324  (1 / params.template get<TimeInfectedSymptoms_b<FP>>()[Group]) * y[ISyi_1b_first_index + subcomp];
325  dydt[ISyi_1b_first_index + subcomp] -= flow;
326  dydt[ISyi_1b_first_index + subcomp + 1] = flow;
327  }
328 
329  // Calculate derivative of the InfectedSevere_1a compartment.
330  // Again split the flow from the last subcompartment of InfectedSymptoms_1a.
331  dydt[Ri_a] += dydt[ISevi_1a_first_index] * (1 - params.template get<SeverePerInfectedSymptoms_a<FP>>()[Group]);
332  dydt[ISevi_1a_first_index] =
333  dydt[ISevi_1a_first_index] * params.template get<SeverePerInfectedSymptoms_a<FP>>()[Group];
334  for (size_t subcomp = 0;
335  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_1a>();
336  subcomp++) {
337  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_1a>() *
338  (1 / params.template get<TimeInfectedSevere_a<FP>>()[Group]) * y[ISevi_1a_first_index + subcomp];
339  dydt[ISevi_1a_first_index + subcomp] -= flow;
340  dydt[ISevi_1a_first_index + subcomp + 1] = flow;
341  }
342  // Calculate derivative of the InfectedSevere_1b compartment.
343  // Again split the flow from the last subcompartment of InfectedSymptoms_1b.
344  dydt[Ri_b] += dydt[ISevi_1b_first_index] * (1 - params.template get<SeverePerInfectedSymptoms_b<FP>>()[Group]);
345  dydt[ISevi_1b_first_index] =
346  dydt[ISevi_1b_first_index] * params.template get<SeverePerInfectedSymptoms_b<FP>>()[Group];
347  for (size_t subcomp = 0;
348  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_1b>();
349  subcomp++) {
350  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_1b>() *
351  (1 / params.template get<TimeInfectedSevere_b<FP>>()[Group]) * y[ISevi_1b_first_index + subcomp];
352  dydt[ISevi_1b_first_index + subcomp] -= flow;
353  dydt[ISevi_1b_first_index + subcomp + 1] = flow;
354  }
355 
356  // Calculate derivative of the InfectedCritical_1a compartment.
357  // Split flow from last subcompartment of InfectedSevere_1a between Recovered_1a, Dead1a and InfectedCritical_1a.
358  dydt[Ri_a] += dydt[ICri_1a_first_index] * (1 - params.template get<CriticalPerSevere_a<FP>>()[Group] -
359  params.template get<DeathsPerSevere_a<FP>>()[Group]);
360  dydt[Di_a] = dydt[ICri_1a_first_index] * params.template get<DeathsPerSevere_a<FP>>()[Group];
361  dydt[ICri_1a_first_index] = dydt[ICri_1a_first_index] * params.template get<CriticalPerSevere_a<FP>>()[Group];
362  for (size_t subcomp = 0;
363  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1a>() - 1;
364  subcomp++) {
365  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1a>() *
366  (1 / params.template get<TimeInfectedCritical_a<FP>>()[Group]) * y[ICri_1a_first_index + subcomp];
367  dydt[ICri_1a_first_index + subcomp] -= flow;
368  dydt[ICri_1a_first_index + subcomp + 1] = flow;
369  }
370  // Calculate derivative of the InfectedCritical_1b compartment.
371  // Split flow from last subcompartment of InfectedSevere_1b between Recovered_1b, Dead_1b and InfectedCritical_1b.
372  dydt[Ri_b] += dydt[ICri_1b_first_index] * (1 - params.template get<CriticalPerSevere_b<FP>>()[Group] -
373  params.template get<DeathsPerSevere_b<FP>>()[Group]);
374  dydt[Di_b] = dydt[ICri_1b_first_index] * params.template get<DeathsPerSevere_b<FP>>()[Group];
375  dydt[ICri_1b_first_index] = dydt[ICri_1b_first_index] * params.template get<CriticalPerSevere_b<FP>>()[Group];
376  for (size_t subcomp = 0;
377  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1b>() - 1;
378  subcomp++) {
379  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1b>() *
380  (1 / params.template get<TimeInfectedCritical_b<FP>>()[Group]) * y[ICri_1b_first_index + subcomp];
381  dydt[ICri_1b_first_index + subcomp] -= flow;
382  dydt[ICri_1b_first_index + subcomp + 1] = flow;
383  }
384 
385  // Flow from InfectedCritical compartments has to be divided between Recovered and Dead compartments.
386  // Must be calculated separately in order not to overwrite the already calculated values ​​for Recovered.
387  // Outflow from InfectedCritical_1a:
388  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1a>() *
389  (1 / params.template get<TimeInfectedCritical_a<FP>>()[Group]) *
390  y[ICri_1a_first_index +
391  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1a>() - 1];
392  dydt[ICri_1a_first_index +
393  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1a>() - 1] -= flow;
394  dydt[Ri_a] = dydt[Ri_a] + (1 - params.template get<DeathsPerCritical_a<FP>>()[Group]) * flow;
395  dydt[Di_a] = dydt[Di_a] + params.template get<DeathsPerCritical_a<FP>>()[Group] * flow;
396  // Outflow from InfectedCritical_1b:
397  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1b>() *
398  (1 / params.template get<TimeInfectedCritical_b<FP>>()[Group]) *
399  y[ICri_1b_first_index +
400  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1b>() - 1];
401  dydt[ICri_1b_first_index +
402  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_1b>() - 1] -= flow;
403  dydt[Ri_b] = dydt[Ri_b] + (1 - params.template get<DeathsPerCritical_b<FP>>()[Group]) * flow;
404  dydt[Di_b] = dydt[Di_b] + params.template get<DeathsPerCritical_b<FP>>()[Group] * flow;
405 
406  // Second Infection:
407  // Outflow from Recovered_1a and Recovered_1b (people getting infected for the second time).
408  double temp_Ra = dydt[Ri_a];
409  // Outflow from Recovered_1a is only affected by b.
410  interact<Group, 0>(pop, y, t, dydt, &part_a, &part_b, 1);
411  double temp_Rb = dydt[Ri_b];
412  // Outflow from Recovered_1b is only affected by a.
413  interact<Group, 0>(pop, y, t, dydt, &part_a, &part_b, 0);
414 
415  // Calculate derivative of the Exposed_2i compartments:
416  // Exposed_2a:
417  dydt[Ei_2a_first_index] = -(dydt[Ri_b] - temp_Rb);
418  for (size_t subcomp = 0;
419  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_2a>(); subcomp++) {
420  // Variable flow stores the value of the flow from one subcompartment to the next one.
421  // Ei_2a_first_index + subcomp is always the index of a (sub-)compartment of Exposed and Ei_2a_first_index
422  // + subcomp + 1 can also be the index of the first (sub-)compartment of InfectedNoSymptoms.
423  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_2a>() *
424  (1 / params.template get<TimeExposed_a<FP>>()[Group]) * y[Ei_2a_first_index + subcomp];
425  // Subtract flow from dydt[Ei_2a_first_index + subcomp] and add to next subcompartment.
426  dydt[Ei_2a_first_index + subcomp] -= flow;
427  dydt[Ei_2a_first_index + subcomp + 1] = flow;
428  }
429  // Exposed_2b:
430  dydt[Ei_2b_first_index] = -(dydt[Ri_a] - temp_Ra);
431  for (size_t subcomp = 0;
432  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_2b>(); subcomp++) {
433  // Variable flow stores the value of the flow from one subcompartment to the next one.
434  // Ei_2b_first_index + subcomp is always the index of a (sub-)compartment of Exposed and Ei_2b_first_index
435  // + subcomp + 1 can also be the index of the first (sub-)compartment of InfectedNoSymptoms.
436  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed_2b>() *
437  (1 / params.template get<TimeExposed_b<FP>>()[Group]) * y[Ei_2b_first_index + subcomp];
438  // Subtract flow from dydt[Ei_2b_first_index + subcomp] and add to next subcompartment.
439  dydt[Ei_2b_first_index + subcomp] -= flow;
440  dydt[Ei_2b_first_index + subcomp + 1] = flow;
441  }
442 
443  // Calculate derivative of the InfectedNoSymptoms_2a compartment.
444  for (size_t subcomp = 0;
445  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_2a>();
446  subcomp++) {
447  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_2a>() *
448  (1 / params.template get<TimeInfectedNoSymptoms_a<FP>>()[Group]) * y[INSi_2a_first_index + subcomp];
449  dydt[INSi_2a_first_index + subcomp] -= flow;
450  dydt[INSi_2a_first_index + subcomp + 1] = flow;
451  }
452  // Calculate derivative of the InfectedNoSymptoms_2b compartment.
453  for (size_t subcomp = 0;
454  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_2b>();
455  subcomp++) {
456  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_2b>() *
457  (1 / params.template get<TimeInfectedNoSymptoms_b<FP>>()[Group]) * y[INSi_2b_first_index + subcomp];
458  dydt[INSi_2b_first_index + subcomp] -= flow;
459  dydt[INSi_2b_first_index + subcomp + 1] = flow;
460  }
461 
462  // Calculate derivative of the InfectedSymptoms_2a compartment.
463  // Flow from last (sub-) compartment of InfectedNoSymptoms_2a must be split between
464  // the first subcompartment of InfectedSymptoms_2a and Recovered_2ab.
465  dydt[Ri_ab] += dydt[ISyi_2a_first_index] * params.template get<RecoveredPerInfectedNoSymptoms_a<FP>>()[Group];
466  dydt[ISyi_2a_first_index] =
467  dydt[ISyi_2a_first_index] * (1 - params.template get<RecoveredPerInfectedNoSymptoms_a<FP>>()[Group]);
468  for (size_t subcomp = 0;
469  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_2a>();
470  subcomp++) {
471  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_2a>() *
472  (1 / params.template get<TimeInfectedSymptoms_a<FP>>()[Group]) * y[ISyi_2a_first_index + subcomp];
473  dydt[ISyi_2a_first_index + subcomp] -= flow;
474  dydt[ISyi_2a_first_index + subcomp + 1] = flow;
475  }
476  // Calculate derivative of the InfectedSymptoms_2b compartment.
477  // Flow from last (sub-) compartment of InfectedNoSymptoms_2b must be split between
478  // the first subcompartment of InfectedSymptoms_2b and Recovered_2ab.
479  dydt[Ri_ab] += dydt[ISyi_2b_first_index] * params.template get<RecoveredPerInfectedNoSymptoms_b<FP>>()[Group];
480  dydt[ISyi_2b_first_index] =
481  dydt[ISyi_2b_first_index] * (1 - params.template get<RecoveredPerInfectedNoSymptoms_b<FP>>()[Group]);
482  for (size_t subcomp = 0;
483  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_2b>();
484  subcomp++) {
485  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms_2b>() *
486  (1 / params.template get<TimeInfectedSymptoms_b<FP>>()[Group]) * y[ISyi_2b_first_index + subcomp];
487  dydt[ISyi_2b_first_index + subcomp] -= flow;
488  dydt[ISyi_2b_first_index + subcomp + 1] = flow;
489  }
490 
491  // Calculate derivative of the InfectedSevere_2a compartment.
492  // Again split the flow from the last subcompartment of InfectedSymptoms_2a.
493  dydt[Ri_ab] += dydt[ISevi_2a_first_index] * (1 - params.template get<SeverePerInfectedSymptoms_a<FP>>()[Group]);
494  dydt[ISevi_2a_first_index] =
495  dydt[ISevi_2a_first_index] * params.template get<SeverePerInfectedSymptoms_a<FP>>()[Group];
496  for (size_t subcomp = 0;
497  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_2a>();
498  subcomp++) {
499  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_2a>() *
500  (1 / params.template get<TimeInfectedSevere_a<FP>>()[Group]) * y[ISevi_2a_first_index + subcomp];
501  dydt[ISevi_2a_first_index + subcomp] -= flow;
502  dydt[ISevi_2a_first_index + subcomp + 1] = flow;
503  }
504  // Calculate derivative of the InfectedSevere compartment.
505  // Again split the flow from the last subcompartment of InfectedSymptoms_2b.
506  dydt[Ri_ab] += dydt[ISevi_2b_first_index] * (1 - params.template get<SeverePerInfectedSymptoms_b<FP>>()[Group]);
507  dydt[ISevi_2b_first_index] =
508  dydt[ISevi_2b_first_index] * params.template get<SeverePerInfectedSymptoms_b<FP>>()[Group];
509  for (size_t subcomp = 0;
510  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_2b>();
511  subcomp++) {
512  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere_2b>() *
513  (1 / params.template get<TimeInfectedSevere_b<FP>>()[Group]) * y[ISevi_2b_first_index + subcomp];
514  dydt[ISevi_2b_first_index + subcomp] -= flow;
515  dydt[ISevi_2b_first_index + subcomp + 1] = flow;
516  }
517 
518  // Calculate derivative of the InfectedCritical compartment.
519  // Split flow from last subcompartment of InfectedSevere_2a between Recovered_2ab, Dead_2ab and InfectedCritical_2a.
520  dydt[Ri_ab] += dydt[ICri_2a_first_index] * (1 - params.template get<CriticalPerSevere_a<FP>>()[Group] -
521  params.template get<DeathsPerSevere_a<FP>>()[Group]);
522  dydt[Di_a] += dydt[ICri_2a_first_index] * params.template get<DeathsPerSevere_a<FP>>()[Group];
523  dydt[ICri_2a_first_index] = dydt[ICri_2a_first_index] * params.template get<CriticalPerSevere_a<FP>>()[Group];
524  for (size_t subcomp = 0;
525  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2a>() - 1;
526  subcomp++) {
527  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2a>() *
528  (1 / params.template get<TimeInfectedCritical_a<FP>>()[Group]) * y[ICri_2a_first_index + subcomp];
529  dydt[ICri_2a_first_index + subcomp] -= flow;
530  dydt[ICri_2a_first_index + subcomp + 1] = flow;
531  }
532  // Calculate derivative of the InfectedCritical compartment.
533  // Split flow from last subcompartment of InfectedSevere_2b between Recovered_2ab and InfectedCritical_2b.
534  dydt[Ri_ab] += dydt[ICri_2b_first_index] * (1 - params.template get<CriticalPerSevere_b<FP>>()[Group] -
535  params.template get<DeathsPerSevere_b<FP>>()[Group]);
536  dydt[Di_b] += dydt[ICri_2b_first_index] * params.template get<DeathsPerSevere_b<FP>>()[Group];
537  dydt[ICri_2b_first_index] = dydt[ICri_2b_first_index] * params.template get<CriticalPerSevere_b<FP>>()[Group];
538  for (size_t subcomp = 0;
539  subcomp < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2b>() - 1;
540  subcomp++) {
541  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2b>() *
542  (1 / params.template get<TimeInfectedCritical_b<FP>>()[Group]) * y[ICri_2b_first_index + subcomp];
543  dydt[ICri_2b_first_index + subcomp] -= flow;
544  dydt[ICri_2b_first_index + subcomp + 1] = flow;
545  }
546 
547  // Last flow from InfectedCritical has to be divided between Recovered and Dead.
548  // Must be calculated separately in order not to overwrite the already calculated values ​​for Recovered.
549  // Outflow from InfectedCritical_2a:
550  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2a>() *
551  (1 / params.template get<TimeInfectedCritical_a<FP>>()[Group]) *
552  y[ICri_2a_first_index +
553  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2a>() - 1];
554  dydt[ICri_2a_first_index +
555  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2a>() - 1] -= flow;
556  dydt[Ri_ab] = dydt[Ri_ab] + (1 - params.template get<DeathsPerCritical_a<FP>>()[Group]) * flow;
557  dydt[Di_a] = dydt[Di_a] + params.template get<DeathsPerCritical_a<FP>>()[Group] * flow;
558  // Outflow from InfectedCritical_2b:
559  flow = (FP)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2b>() *
560  (1 / params.template get<TimeInfectedCritical_b<FP>>()[Group]) *
561  y[ICri_2b_first_index +
562  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2b>() - 1];
563  dydt[ICri_2b_first_index +
564  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical_2b>() - 1] -= flow;
565  dydt[Ri_ab] = dydt[Ri_ab] + (1 - params.template get<DeathsPerCritical_b<FP>>()[Group]) * flow;
566  dydt[Di_b] = dydt[Di_b] + params.template get<DeathsPerCritical_b<FP>>()[Group] * flow;
567 
568  // Function call for next group if applicable.
569  if constexpr (Group + 1 < num_groups) {
570  get_derivatives_impl<Group + 1>(pop, y, t, dydt);
571  }
572  }
573 
589  template <size_t Group1, size_t Group2 = 0>
590  void interact(Eigen::Ref<const Eigen::VectorX<FP>> pop, Eigen::Ref<const Eigen::VectorX<FP>> y, FP t,
591  Eigen::Ref<Eigen::VectorX<FP>> dydt, double* part_a, double* part_b, int relevant_disease) const
592  {
593  static_assert((Group1 < num_groups) && (Group1 >= 0) && (Group2 < num_groups) && (Group2 >= 0),
594  "The template parameters Group1 & Group2 should be valid.");
595  using LctStateGroup1 = type_at_index_t<Group1, LctStates...>;
596  using LctStateGroup2 = type_at_index_t<Group2, LctStates...>;
597  FP infectedNoSymptoms_group2_a = 0;
598  FP infectedSymptoms_group2_a = 0;
599  FP infectedNoSymptoms_group2_b = 0;
600  FP infectedSymptoms_group2_b = 0;
601  const auto& params = this->parameters;
602 
603  size_t first_index_group1 = this->populations.template get_first_index_of_group<Group1>();
604  size_t first_index_group2 = this->populations.template get_first_index_of_group<Group2>();
605 
606  // Calculate sum of all subcompartments for InfectedNoSymptoms for disease a of Group2.
607  infectedNoSymptoms_group2_a =
608  pop.segment(first_index_group2 +
609  LctStateGroup2::template get_first_index<InfectionState::InfectedNoSymptoms_1a>(),
610  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_1a>())
611  .sum() +
612  pop.segment(first_index_group2 +
613  LctStateGroup2::template get_first_index<InfectionState::InfectedNoSymptoms_2a>(),
614  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_2a>())
615  .sum();
616  // Calculate sum of all subcompartments for InfectedSymptoms for disease a of Group2.
617  infectedSymptoms_group2_a =
618  pop.segment(first_index_group2 +
619  LctStateGroup2::template get_first_index<InfectionState::InfectedSymptoms_1a>(),
620  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedSymptoms_1a>())
621  .sum() +
622  pop.segment(first_index_group2 +
623  LctStateGroup2::template get_first_index<InfectionState::InfectedSymptoms_2a>(),
624  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedSymptoms_2a>())
625  .sum();
626  // Calculate sum of all subcompartments for InfectedNoSymptoms for disease b of Group2.
627  infectedNoSymptoms_group2_b =
628  pop.segment(first_index_group2 +
629  LctStateGroup2::template get_first_index<InfectionState::InfectedNoSymptoms_1b>(),
630  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_1b>())
631  .sum() +
632  pop.segment(first_index_group2 +
633  LctStateGroup2::template get_first_index<InfectionState::InfectedNoSymptoms_2b>(),
634  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedNoSymptoms_2b>())
635  .sum();
636  // Calculate sum of all subcompartments for InfectedSymptoms for disease b of Group2.
637  infectedSymptoms_group2_b =
638  pop.segment(first_index_group2 +
639  LctStateGroup2::template get_first_index<InfectionState::InfectedSymptoms_1b>(),
640  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedSymptoms_1b>())
641  .sum() +
642  pop.segment(first_index_group2 +
643  LctStateGroup2::template get_first_index<InfectionState::InfectedSymptoms_2b>(),
644  LctStateGroup2::template get_num_subcompartments<InfectionState::InfectedSymptoms_2b>())
645  .sum();
646  // Size of the subpopulation Group2 without dead people.
647  FP N_group2 = pop.segment(first_index_group2, LctStateGroup2::Count).sum(); // Sum over all compartments.
648  N_group2 = N_group2 - pop.segment(LctStateGroup2::template get_first_index<InfectionState::Dead_a>(), 1).sum() -
649  pop.segment(LctStateGroup2::template get_first_index<InfectionState::Dead_b>(), 1)
650  .sum(); // All people minus dead people.
651  const FP div_N_group2 = (N_group2 < Limits<FP>::zero_tolerance()) ? 0.0 : 1.0 / N_group2;
652  FP season_val = 1 + params.template get<Seasonality<FP>>() *
653  sin(3.141592653589793 * ((params.template get<StartDay<FP>>() + t) / 182.5 + 0.5));
654 
655  FP contact_patterns = params.template get<ContactPatterns<FP>>().get_cont_freq_mat().get_matrix_at(
656  SimulationTime<FP>(t))(static_cast<Eigen::Index>(Group1), static_cast<Eigen::Index>(Group2));
657  FP infection_a_per_person =
658  params.template get<TransmissionProbabilityOnContact_a<FP>>()[Group1] *
659  (params.template get<RelativeTransmissionNoSymptoms_a<FP>>()[Group2] * infectedNoSymptoms_group2_a +
660  params.template get<RiskOfInfectionFromSymptomatic_a<FP>>()[Group2] * infectedSymptoms_group2_a);
661  FP infection_b_per_person =
662  params.template get<TransmissionProbabilityOnContact_b<FP>>()[Group1] *
663  (params.template get<RelativeTransmissionNoSymptoms_b<FP>>()[Group2] * infectedNoSymptoms_group2_b +
664  params.template get<RiskOfInfectionFromSymptomatic_b<FP>>()[Group2] * infectedSymptoms_group2_b);
665 
666  if (relevant_disease == 0) { // Disease a.
667  // Get index for compartment Recovered_1b and calculate outflow driven by disease a.
668  size_t compartment_index =
669  first_index_group1 + LctStateGroup1::template get_first_index<InfectionState::Recovered_1b>();
670  dydt[compartment_index] +=
671  -y[compartment_index] * div_N_group2 * season_val * contact_patterns * infection_a_per_person;
672  }
673  else if (relevant_disease == 1) { // Disease b.
674  // Get index for compartment Recovered_1a and calculate outflow driven by disease b.
675  size_t compartment_index =
676  first_index_group1 + LctStateGroup1::template get_first_index<InfectionState::Recovered_1a>();
677  dydt[compartment_index] +=
678  -y[compartment_index] * div_N_group2 * season_val * contact_patterns * infection_b_per_person;
679  }
680  else if (relevant_disease == 2) { // Both diseases drive outflow from the Susceptible compartment.
681  size_t compartment_index = first_index_group1;
682  dydt[compartment_index] += -y[compartment_index] * div_N_group2 * season_val * contact_patterns *
683  (infection_a_per_person + infection_b_per_person);
684  }
685  // To split the outflow from Susceptible between Exposed_1a and Exposed_1b we need
686  // the proportion of people getting infected with disease a and b.
687  *part_a += infection_a_per_person;
688  *part_b += infection_b_per_person;
689 
690  if constexpr (Group2 + 1 < num_groups) {
691  interact<Group1, Group2 + 1>(pop, y, t, dydt, part_a, part_b, relevant_disease);
692  }
693  }
694 
702  template <size_t Group = 0>
704  {
705  static_assert((Group < num_groups) && (Group >= 0), "The template parameter Group should be valid.");
706  using LctStateGroup = type_at_index_t<Group, LctStates...>;
707 
708  if (LctStateGroup::template get_num_subcompartments<InfectionState::Susceptible>() != 1) {
709  log_warning("Constraint check: The number of subcompartments for Susceptibles of group {} should be one!",
710  Group);
711  return true;
712  }
713  if (LctStateGroup::template get_num_subcompartments<InfectionState::Recovered_1a>() != 1 ||
714  LctStateGroup::template get_num_subcompartments<InfectionState::Recovered_1b>() != 1 ||
715  LctStateGroup::template get_num_subcompartments<InfectionState::Recovered_2ab>() != 1) {
716  log_warning("Constraint check: The number of subcompartments for Recovered of group {} should be one!",
717  Group);
718  return true;
719  }
720  if (LctStateGroup::template get_num_subcompartments<InfectionState::Dead_a>() != 1 ||
721  LctStateGroup::template get_num_subcompartments<InfectionState::Dead_b>() != 1) {
722  log_warning("Constraint check: The number of subcompartments for Dead of group {} should be one!", Group);
723  return true;
724  }
725 
726  if constexpr (Group == num_groups - 1) {
727  return false;
728  }
729  else {
730  return check_constraints_impl<Group + 1>();
731  }
732  }
733 };
734 
735 } // namespace lsecir2d
736 } // namespace mio
737 #endif // LCT_SECIR_2_DISEASE_MODEL_H
A class template for compartment populations of LCT models.
Definition: lct_populations.h:57
size_t get_num_compartments() const
get_num_compartments Returns the number of compartments.
Definition: lct_populations.h:75
Typesafe wrapper for a floating-point simulation time value (in days).
Definition: damping.h:78
stores vectors of values at time points (or some other abstract variable) the value at each time poin...
Definition: time_series.h:58
Eigen::Index get_num_elements() const
number of elements of vector at each time point
Definition: time_series.h:205
Eigen::Index get_num_time_points() const
number of time points in the series
Definition: time_series.h:197
FP & get_time(Eigen::Index i)
time of time point at index i
Definition: time_series.h:272
Eigen::Ref< Vector > add_time_point()
add one uninitialized time point
Definition: time_series.h:221
Class that defines an LCT-SECIR-2-DISEASE model.
Definition: lct_secir_2_diseases/model.h:51
void interact(Eigen::Ref< const Eigen::VectorX< FP >> pop, Eigen::Ref< const Eigen::VectorX< FP >> y, FP t, Eigen::Ref< Eigen::VectorX< FP >> dydt, double *part_a, double *part_b, int relevant_disease) const
Calculates flows that are caused by people becoming infected (outflow from compartment S,...
Definition: lct_secir_2_diseases/model.h:590
void get_derivatives(Eigen::Ref< const Eigen::VectorX< FP >> pop, Eigen::Ref< const Eigen::VectorX< FP >> y, FP t, Eigen::Ref< Eigen::VectorX< FP >> dydt) const override
Evaluates the right-hand-side f of the ODE dydt = f(y, t).
Definition: lct_secir_2_diseases/model.h:86
TimeSeries< FP > calculate_compartments(const TimeSeries< FP > &subcompartments_ts) const
Cumulates a simulation result with subcompartments to produce a result that divides the population on...
Definition: lct_secir_2_diseases/model.h:107
Model()
Default constructor.
Definition: lct_secir_2_diseases/model.h:61
static constexpr size_t num_groups
Definition: lct_secir_2_diseases/model.h:57
void compress_vector(const Eigen::VectorX< FP > &subcompartments, Eigen::VectorX< FP > &compartments) const
Converts a vector with subcompartments in a vector without subcompartments by summing up subcompartme...
Definition: lct_secir_2_diseases/model.h:149
bool check_constraints_impl() const
Checks whether LctState of a group satisfies all constraints.
Definition: lct_secir_2_diseases/model.h:703
Model(const Populations &pop, const ParameterSet &params)
Constructor using Populations and ParameterSet.
Definition: lct_secir_2_diseases/model.h:70
void get_derivatives_impl(Eigen::Ref< const Eigen::VectorX< FP >> pop, Eigen::Ref< const Eigen::VectorX< FP >> y, FP t, Eigen::Ref< Eigen::VectorX< FP >> dydt) const
Evaluates the right-hand-side f of the ODE dydt = f(y, t) recursively for each group.
Definition: lct_secir_2_diseases/model.h:183
bool check_constraints() const
Checks that the model satisfies all constraints (e.g.
Definition: lct_secir_2_diseases/model.h:131
Parameters of an LCT-SECIR-2-DISEASES model.
Definition: lct_secir_2_diseases/parameters.h:538
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
InfectionState
The InfectionState enum describes the basic categories for the infection state of persons.
Definition: lct_secir_2_diseases/infection_state.h:34
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
void log_warning(spdlog::string_view_t fmt, const Args &... args)
Definition: logging.h:126
typename type_at_index< Index, Types... >::type type_at_index_t
The type at the Index-th position in the list Types.
Definition: metaprogramming.h:118
void log_error(spdlog::string_view_t fmt, const Args &... args)
Definition: logging.h:114
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
CompartmentalModel is a template for a compartmental model for an array of initial populations and a ...
Definition: compartmental_model.h:59
LctPopulations< FP, LctStates... > Populations
Definition: compartmental_model.h:62
Type specific limits for floating-points.
Definition: memilio/config.h:59
Collection of types. Each type is mapped to an index of type size_t.
Definition: type_list.h:32
The percentage of dead patients per hospitalized patients for disease a for each group.
Definition: lct_secir_2_diseases/parameters.h:377
The percentage of dead patients per hospitalized patients for disease b for each group.
Definition: lct_secir_2_diseases/parameters.h:457
The relative InfectedNoSymptoms infectability for disease a for each group.
Definition: lct_secir_2_diseases/parameters.h:265
The relative InfectedNoSymptoms infectability for disease b for each group.
Definition: lct_secir_2_diseases/parameters.h:297
The start day in the LCT-SECIR-2-DISEASES model.
Definition: lct_secir_2_diseases/parameters.h:492