parameters_io.h Source File

CPP API: parameters_io.h Source File
models/lct_secir/parameters_io.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: 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 LCTSECIR_PARAMETERS_IO_H
22 #define LCTSECIR_PARAMETERS_IO_H
23 
24 #include "memilio/config.h"
25 
26 #ifdef MEMILIO_HAS_JSONCPP
27 
28 #include "lct_secir/model.h"
29 #include "lct_secir/infection_state.h"
30 #include "lct_secir/parameters.h"
31 #include "memilio/io/epi_data.h"
32 #include "memilio/io/io.h"
33 #include "memilio/utils/date.h"
34 #include "memilio/utils/logging.h"
36 #include "memilio/math/eigen.h"
39 
40 #include <string>
41 #include <vector>
42 #include <type_traits>
43 namespace mio
44 {
45 namespace lsecir
46 {
47 
48 namespace details
49 { // Use namespace to hide functions that are not intended to be used outside this file.
50 
75 template <class Populations, class EntryType, size_t Group>
76 void process_entry(Populations& populations, const EntryType& entry, int offset,
77  const std::vector<ScalarType> staytimes, ScalarType inv_prob_SymptomsPerNoSymptoms,
78  ScalarType severePerInfectedSymptoms, ScalarType criticalPerSevere, ScalarType scale_confirmed_cases)
79 {
80  using LctStateGroup = type_at_index_t<Group, typename Populations::LctStatesGroups>;
81  size_t first_index_group = populations.template get_first_index_of_group<Group>();
82 
83  // Add confirmed cases at date to compartment Recovered.
84  if (offset == 0) {
85  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] +=
86  scale_confirmed_cases * entry.num_confirmed;
87  }
88 
89  // Compute initial values for compartment InfectedNoSymptoms.
90  if (offset >= 0 && offset <= std::ceil(staytimes[(size_t)InfectionState::InfectedNoSymptoms])) {
91  // Mean stay time in each subcompartment.
92  ScalarType time_INS_per_subcomp =
93  staytimes[(size_t)InfectionState::InfectedNoSymptoms] /
94  (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms>();
95  // Index of the last subcompartment of InfectedNoSymptoms.
96  size_t last_index_INS =
97  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedNoSymptoms>() +
98  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms>() - 1;
99  for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms>();
100  i++) {
101  if (offset == std::floor(i * time_INS_per_subcomp)) {
102  populations[last_index_INS - i] -=
103  (1 - (i * time_INS_per_subcomp - std::floor(i * time_INS_per_subcomp))) *
104  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
105  }
106  if (offset == std::ceil(i * time_INS_per_subcomp)) {
107  populations[last_index_INS - i] -= (i * time_INS_per_subcomp - std::floor(i * time_INS_per_subcomp)) *
108  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases *
109  entry.num_confirmed;
110  }
111  if (offset == std::floor((i + 1) * time_INS_per_subcomp)) {
112  populations[last_index_INS - i] +=
113  (1 - ((i + 1) * time_INS_per_subcomp - std::floor((i + 1) * time_INS_per_subcomp))) *
114  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
115  }
116  if (offset == std::ceil((i + 1) * time_INS_per_subcomp)) {
117  populations[last_index_INS - i] +=
118  ((i + 1) * time_INS_per_subcomp - std::floor((i + 1) * time_INS_per_subcomp)) *
119  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
120  }
121  }
122  }
123 
124  // Compute initial values for compartment Exposed.
125  if (offset >= std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms]) &&
126  offset <= std::ceil(staytimes[(size_t)InfectionState::Exposed] +
127  staytimes[(size_t)InfectionState::InfectedNoSymptoms])) {
128  // Mean stay time in each subcompartment.
129  ScalarType time_Exposed_per_subcomp =
130  staytimes[(size_t)InfectionState::Exposed] /
131  (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed>();
132  // Index of the last subcompartment of Exposed.
133  size_t last_index_Exposed = first_index_group +
134  LctStateGroup::template get_first_index<InfectionState::Exposed>() +
135  LctStateGroup::template get_num_subcompartments<InfectionState::Exposed>() - 1;
136  for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed>(); i++) {
137  if (offset ==
138  std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp)) {
139  populations[last_index_Exposed - i] -=
140  (1 - (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp -
142  i * time_Exposed_per_subcomp))) *
143  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
144  }
145  if (offset ==
146  std::ceil(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp)) {
147  populations[last_index_Exposed - i] -=
148  (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp -
149  std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp)) *
150  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
151  }
152  if (offset == std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] +
153  (i + 1) * time_Exposed_per_subcomp)) {
154  populations[last_index_Exposed - i] +=
155  (1 - (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + (i + 1) * time_Exposed_per_subcomp -
157  (i + 1) * time_Exposed_per_subcomp))) *
158  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
159  }
160  if (offset ==
161  std::ceil(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + (i + 1) * time_Exposed_per_subcomp)) {
162  populations[last_index_Exposed - i] +=
163  (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + (i + 1) * time_Exposed_per_subcomp -
165  (i + 1) * time_Exposed_per_subcomp)) *
166  inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
167  }
168  }
169  }
170 
171  // Compute initial values for compartment InfectedSymptoms.
172  if (offset >= std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms]) && offset <= 0) {
173  // Mean stay time in each subcompartment.
174  ScalarType time_IS_per_subcomp =
175  staytimes[(size_t)InfectionState::InfectedSymptoms] /
176  (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms>();
177  // Index of the first subcompartment of InfectedSymptoms.
178  size_t first_index_IS =
179  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms>();
180  for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms>();
181  i++) {
182  if (offset == std::floor(-time_IS_per_subcomp * (i + 1))) {
183  populations[first_index_IS + i] -=
184  (1 - (-(i + 1.) * time_IS_per_subcomp - std::floor(-(i + 1) * time_IS_per_subcomp))) *
185  scale_confirmed_cases * entry.num_confirmed;
186  }
187  if (offset == std::ceil(-time_IS_per_subcomp * (i + 1))) {
188  populations[first_index_IS + i] -=
189  (-(i + 1) * time_IS_per_subcomp - std::floor(-(i + 1) * time_IS_per_subcomp)) *
190  scale_confirmed_cases * entry.num_confirmed;
191  }
192  if (offset == std::floor(-time_IS_per_subcomp * i)) {
193  populations[first_index_IS + i] +=
194  (1 - (-i * time_IS_per_subcomp - std::floor(-i * time_IS_per_subcomp))) * scale_confirmed_cases *
195  entry.num_confirmed;
196  }
197  if (offset == std::ceil(-time_IS_per_subcomp * i)) {
198  populations[first_index_IS + i] += (-i * time_IS_per_subcomp - std::floor(-i * time_IS_per_subcomp)) *
199  scale_confirmed_cases * entry.num_confirmed;
200  }
201  }
202  }
203 
204  // Compute initial values for compartment InfectedSevere.
205  if (offset >= std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
206  staytimes[(size_t)InfectionState::InfectedSevere]) &&
207  offset <= std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms])) {
208  // Mean stay time in each subcompartment.
209  ScalarType time_ISev_per_subcomp =
210  staytimes[(size_t)InfectionState::InfectedSevere] /
211  (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere>();
212  // Index of the first subcompartment of InfectedSevere.
213  size_t first_index_ISev =
214  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSevere>();
215  for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere>();
216  i++) {
217  if (offset ==
218  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * (i + 1))) {
219  populations[first_index_ISev + i] -=
220  severePerInfectedSymptoms *
221  (1 - (-staytimes[(size_t)InfectionState::InfectedSymptoms] - (i + 1) * time_ISev_per_subcomp -
222  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
223  (i + 1) * time_ISev_per_subcomp))) *
224  scale_confirmed_cases * entry.num_confirmed;
225  }
226  if (offset ==
227  std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * (i + 1))) {
228  populations[first_index_ISev + i] -=
229  severePerInfectedSymptoms *
230  (-staytimes[(size_t)InfectionState::InfectedSymptoms] - (i + 1) * time_ISev_per_subcomp -
231  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
232  (i + 1) * time_ISev_per_subcomp)) *
233  scale_confirmed_cases * entry.num_confirmed;
234  }
235  if (offset ==
236  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * i)) {
237  populations[first_index_ISev + i] +=
238  severePerInfectedSymptoms *
239  (1 -
240  (-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp -
241  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp))) *
242  scale_confirmed_cases * entry.num_confirmed;
243  }
244  if (offset == std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * i)) {
245  populations[first_index_ISev + i] +=
246  severePerInfectedSymptoms *
247  (-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp -
248  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp)) *
249  scale_confirmed_cases * entry.num_confirmed;
250  }
251  }
252  }
253 
254  // Compute initial values for compartment InfectedCritical.
255  if (offset >= std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
256  staytimes[(size_t)InfectionState::InfectedSevere] -
257  staytimes[(size_t)InfectionState::InfectedCritical]) &&
258  offset <= std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
259  staytimes[(size_t)InfectionState::InfectedSevere])) {
260  // Mean stay time in each subcompartment.
261  ScalarType time_ICri_per_subcomp =
262  staytimes[(size_t)InfectionState::InfectedCritical] /
263  (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
264  // Index of the first subcompartment of InfectedCritical.
265  size_t first_index_ICri =
266  first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical>();
267  for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
268  i++) {
269  if (offset ==
270  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
271  staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * (i + 1))) {
272  populations[first_index_ICri + i] -=
273  severePerInfectedSymptoms * criticalPerSevere *
274  (1 - (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
275  staytimes[(size_t)InfectionState::InfectedSevere] - (i + 1) * time_ICri_per_subcomp -
276  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
277  staytimes[(size_t)InfectionState::InfectedSevere] -
278  (i + 1) * time_ICri_per_subcomp))) *
279  scale_confirmed_cases * entry.num_confirmed;
280  }
281  if (offset ==
282  std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
283  staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * (i + 1))) {
284  populations[first_index_ICri + i] -=
285  severePerInfectedSymptoms * criticalPerSevere *
286  (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
287  staytimes[(size_t)InfectionState::InfectedSevere] - (i + 1) * time_ICri_per_subcomp -
288  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
289  staytimes[(size_t)InfectionState::InfectedSevere] - (i + 1) * time_ICri_per_subcomp)) *
290  scale_confirmed_cases * entry.num_confirmed;
291  }
292  if (offset == std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
293  staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * i)) {
294  populations[first_index_ICri + i] +=
295  severePerInfectedSymptoms * criticalPerSevere *
296  (1 - (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
297  staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp -
298  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
299  staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp))) *
300  scale_confirmed_cases * entry.num_confirmed;
301  }
302  if (offset == std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
303  staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * i)) {
304  populations[first_index_ICri + i] +=
305  severePerInfectedSymptoms * criticalPerSevere *
306  (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
307  staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp -
308  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
309  staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp)) *
310  scale_confirmed_cases * entry.num_confirmed;
311  }
312  }
313  }
314 
315  // Compute Dead by shifting RKI data according to mean stay times.
316  // This is because the RKI reports deaths with the date of the positive test, not the date of death.
317  if (offset == std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
318  staytimes[(size_t)InfectionState::InfectedSevere] -
319  staytimes[(size_t)InfectionState::InfectedCritical])) {
320  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead>()] +=
321  (1 -
322  (-staytimes[(size_t)InfectionState::InfectedSymptoms] - staytimes[(size_t)InfectionState::InfectedSevere] -
323  staytimes[(size_t)InfectionState::InfectedCritical] -
324  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
325  staytimes[(size_t)InfectionState::InfectedSevere] -
326  staytimes[(size_t)InfectionState::InfectedCritical]))) *
327  entry.num_deaths;
328  }
329  if (offset == std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
330  staytimes[(size_t)InfectionState::InfectedSevere] -
331  staytimes[(size_t)InfectionState::InfectedCritical])) {
332  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead>()] +=
333  (-staytimes[(size_t)InfectionState::InfectedSymptoms] - staytimes[(size_t)InfectionState::InfectedSevere] -
334  staytimes[(size_t)InfectionState::InfectedCritical] -
335  std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
336  staytimes[(size_t)InfectionState::InfectedSevere] -
337  staytimes[(size_t)InfectionState::InfectedCritical])) *
338  entry.num_deaths;
339  }
340 }
341 
367 template <class Populations, class EntryType, size_t Group = 0>
368 IOResult<void> set_initial_values_from_confirmed_cases(Populations& populations, const std::vector<EntryType>& rki_data,
369  const Parameters<ScalarType>& parameters, const Date date,
370  const std::vector<ScalarType>& total_population,
371  const std::vector<ScalarType>& scale_confirmed_cases)
372 {
373  static_assert((Group < Populations::num_groups) && (Group >= 0), "The template parameter Group should be valid.");
374  using LctStateGroup = type_at_index_t<Group, typename Populations::LctStatesGroups>;
375  size_t first_index_group = populations.template get_first_index_of_group<Group>();
376 
377  // Define variables for parameters.
378  std::vector<ScalarType> staytimes((size_t)InfectionState::Count, -1.);
379  staytimes[(size_t)InfectionState::Exposed] = parameters.template get<TimeExposed<ScalarType>>()[Group];
380  staytimes[(size_t)InfectionState::InfectedNoSymptoms] =
381  parameters.template get<TimeInfectedNoSymptoms<ScalarType>>()[Group];
382  staytimes[(size_t)InfectionState::InfectedSymptoms] =
383  parameters.template get<TimeInfectedSymptoms<ScalarType>>()[Group];
384  staytimes[(size_t)InfectionState::InfectedSevere] =
385  parameters.template get<TimeInfectedSevere<ScalarType>>()[Group];
386  staytimes[(size_t)InfectionState::InfectedCritical] =
387  parameters.template get<TimeInfectedCritical<ScalarType>>()[Group];
388  ScalarType inv_prob_SymptomsPerNoSymptoms =
389  1 / (1 - parameters.template get<RecoveredPerInfectedNoSymptoms<ScalarType>>()[Group]);
390  ScalarType prob_SeverePerInfectedSymptoms = parameters.template get<SeverePerInfectedSymptoms<ScalarType>>()[Group];
391  ScalarType prob_CriticalPerSevere = parameters.template get<CriticalPerSevere<ScalarType>>()[Group];
392 
393  ScalarType min_offset_needed = std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
394  staytimes[(size_t)InfectionState::InfectedSevere] -
395  staytimes[(size_t)InfectionState::InfectedCritical]);
396  ScalarType max_offset_needed =
397  std::ceil(staytimes[(size_t)InfectionState::Exposed] + staytimes[(size_t)InfectionState::InfectedNoSymptoms]);
398 
399  bool min_offset_needed_avail = false;
400  bool max_offset_needed_avail = false;
401 
402  // Go through the entries of rki_data and check if the entry is age resolved and is referring to
403  // the age_group Group in the case with age resolution. If the date is
404  // needed for calculation, another function to handle the entry is called. Confirmed cases are scaled by scale_confirmed_cases.
405  for (auto&& entry : rki_data) {
406  if constexpr (std::is_same_v<EntryType, ConfirmedCasesDataEntry>) {
407  if (!((size_t)entry.age_group == Group)) {
408  continue;
409  }
410  }
411  int offset = get_offset_in_days(entry.date, date);
412  if ((offset >= min_offset_needed) && (offset <= max_offset_needed)) {
413  if (offset == max_offset_needed) {
414  max_offset_needed_avail = true;
415  }
416  if (offset == min_offset_needed) {
417  min_offset_needed_avail = true;
418  }
419  process_entry<Populations, EntryType, Group>(populations, entry, offset, staytimes,
420  inv_prob_SymptomsPerNoSymptoms, prob_SeverePerInfectedSymptoms,
421  prob_CriticalPerSevere, scale_confirmed_cases[Group]);
422  }
423  }
424 
425  // Compute Recovered.
426  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] -=
427  populations.get_compartments()
428  .segment(first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms>(),
429  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms>() +
430  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere>() +
431  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>())
432  .sum();
433  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] -=
434  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead>()];
435 
436  // Compute Susceptibles.
437  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Susceptible>()] =
438  total_population[Group] -
439  populations.get_compartments().segment(first_index_group + 1, LctStateGroup::Count - 1).sum();
440 
441  // Check whether all necessary dates are available.
442  if (!max_offset_needed_avail || !min_offset_needed_avail) {
443  log_error(
444  "Necessary range of dates needed to compute initial values does not exist in RKI data for group {:d}.",
445  Group);
447  "Necessary range of dates needed to compute initial values does not exist in RKI data.");
448  }
449 
450  // Check if all values for populations are valid.
451  for (size_t i = first_index_group; i < LctStateGroup::Count; i++) {
452  if (floating_point_less<ScalarType>((ScalarType)populations[i], 0., Limits<ScalarType>::zero_tolerance())) {
453  log_error("Something went wrong in the initialization of group {:d}. At least one entry is negative.",
454  Group);
456  "Something went wrong in the initialization as at least one entry is negative.");
457  }
458  }
459 
460  if constexpr (Group + 1 < Populations::num_groups) {
461  return set_initial_values_from_confirmed_cases<Populations, EntryType, Group + 1>(
462  populations, rki_data, parameters, date, total_population, scale_confirmed_cases);
463  }
464  else {
465  return success();
466  }
467 }
468 
486 template <class Populations, size_t Group = 0>
487 ScalarType get_total_InfectedCritical_from_populations(const Populations& populations)
488 {
489  using LctStateGroup = type_at_index_t<Group, typename Populations::LctStatesGroups>;
490  size_t first_index_group = populations.template get_first_index_of_group<Group>();
491 
492  ScalarType infectedCritical_Group =
493  populations.get_compartments()
494  .segment(first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical>(),
495  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>())
496  .sum();
497 
498  if constexpr (Group + 1 < Populations::num_groups) {
499  return infectedCritical_Group +
500  get_total_InfectedCritical_from_populations<Populations, Group + 1>(populations);
501  }
502  else {
503  return infectedCritical_Group;
504  }
505 }
506 
514 IOResult<ScalarType> get_icu_from_divi_data(const std::vector<DiviEntry>& divi_data, const Date date)
515 {
516  for (auto&& entry : divi_data) {
517  int offset = get_offset_in_days(entry.date, date);
518  if (offset == 0) {
519  return success(entry.num_icu);
520  }
521  }
522  log_error("Specified date does not exist in DIVI data.");
523  return failure(StatusCode::OutOfRange, "Specified date does not exist in DIVI data.");
524 }
525 
552 template <class Populations, size_t Group = 0>
553 IOResult<void> rescale_to_divi_data(Populations& populations, const ScalarType infectedCritical_reported,
554  const ScalarType infectedCritical_populations)
555 {
556  if (floating_point_less<ScalarType>(infectedCritical_reported, 0., Limits<ScalarType>::zero_tolerance())) {
557  log_error("The provided reported number of InfectedCritical is negative. Please check the data.");
559  "The provided reported number of InfectedCritical is negative. Please check the data.");
560  }
561 
562  using LctStateGroup = type_at_index_t<Group, typename Populations::LctStatesGroups>;
563  size_t first_index_group = populations.template get_first_index_of_group<Group>();
564 
565  if (floating_point_equal<ScalarType>(infectedCritical_populations, 0., Limits<ScalarType>::zero_tolerance())) {
566  if (!(floating_point_equal<ScalarType>(infectedCritical_reported, 0., Limits<ScalarType>::zero_tolerance()))) {
567  log_info("The calculated number of patients in intensive care is zero, although the reported number is "
568  "not. The reported number is uniformly distributed across groups and subcompartments. Note "
569  "that this is not necessarily realistic.");
570  size_t num_InfectedCritical =
571  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
572  ScalarType num_age_groups = (ScalarType)Populations::num_groups;
573  // Distribute reported number uniformly to age groups and subcompartments.
574  for (size_t subcompartment = 0;
575  subcompartment < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
576  subcompartment++) {
577  populations[first_index_group +
578  LctStateGroup::template get_first_index<InfectionState::InfectedCritical>() +
579  subcompartment] =
580  infectedCritical_reported / ((ScalarType)num_InfectedCritical * num_age_groups);
581  }
582  // Adjust Recovered compartment.
583  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] -=
584  infectedCritical_reported / num_age_groups;
585  // Number of Susceptibles is not affected because Recovered is adjusted accordingly.
586  }
587  }
588  else {
589  // Adjust number of Recovered by adding the old number in InfectedCritical
590  // and subtracting the new number (= scaling_factor * old number).
591  ScalarType scaling_factor_infectedCritical = infectedCritical_reported / infectedCritical_populations;
592  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] +=
593  (1 - scaling_factor_infectedCritical) *
594  populations.get_compartments()
595  .segment(first_index_group +
596  LctStateGroup::template get_first_index<InfectionState::InfectedCritical>(),
597  LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>())
598  .sum();
599  // Adjust InfectedCritical.
600  for (size_t subcompartment = 0;
601  subcompartment < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
602  subcompartment++) {
603  populations[first_index_group +
604  LctStateGroup::template get_first_index<InfectionState::InfectedCritical>() + subcompartment] *=
605  scaling_factor_infectedCritical;
606  }
607  // Number of Susceptibles is not affected because Recovered is adjusted accordingly.
608  }
609  if (floating_point_less<ScalarType>(
610  (ScalarType)
611  populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()],
613  log_error(
614  "Scaling with reported DIVI data led to a negative entry in the Recovered compartment for group {:d}.",
615  Group);
617  "Scaling with reported DIVI data led to a negative entry in a Recovered compartment.");
618  }
619  if constexpr (Group + 1 < Populations::num_groups) {
620  return rescale_to_divi_data<Populations, Group + 1>(populations, infectedCritical_reported,
621  infectedCritical_populations);
622  }
623  else {
624  return success();
625  }
626 }
627 } // namespace details
628 
671 template <class Populations, class EntryType>
672 IOResult<void> set_initial_values_from_reported_data(const std::vector<EntryType>& rki_data, Populations& populations,
673  const Parameters<ScalarType>& parameters, const Date date,
674  const std::vector<ScalarType>& total_population,
675  const std::vector<ScalarType>& scale_confirmed_cases,
676  const std::vector<DiviEntry>& divi_data = std::vector<DiviEntry>())
677 { // Check if the inputs are matching.
678  assert(total_population.size() == Populations::num_groups);
679  assert(scale_confirmed_cases.size() == Populations::num_groups);
680  if constexpr (Populations::num_groups > 1) {
681  static_assert(std::is_same_v<EntryType, ConfirmedCasesDataEntry>);
682  assert(ConfirmedCasesDataEntry::age_group_names.size() == Populations::num_groups);
683  }
684  else {
685  static_assert(std::is_same_v<EntryType, ConfirmedCasesNoAgeEntry>);
686  }
687  // Check if RKI data vector is valid.
688  auto max_date_entry = std::max_element(rki_data.begin(), rki_data.end(), [](auto&& a, auto&& b) {
689  return a.date < b.date;
690  });
691  if (max_date_entry == rki_data.end()) {
692  log_error("RKI data file is empty.");
693  return failure(StatusCode::InvalidFileFormat, "RKI data is empty.");
694  }
695  auto max_date = max_date_entry->date;
696  if (max_date < date) {
697  log_error("Specified date does not exist in RKI data.");
698  return failure(StatusCode::OutOfRange, "Specified date does not exist in RKI data.");
699  }
700  // Initially set populations to zero.
701  for (size_t i = 0; i < populations.get_num_compartments(); i++) {
702  populations[i] = 0;
703  }
704  // Set populations using the RKI data.
705  IOResult<void> ioresult_confirmedcases = details::set_initial_values_from_confirmed_cases<Populations, EntryType>(
706  populations, rki_data, parameters, date, total_population, scale_confirmed_cases);
707  if (!(ioresult_confirmedcases)) {
708  return ioresult_confirmedcases;
709  }
710 
711  // Check if DIVI data is provided and scale the result in populations accordingly.
712  if (!divi_data.empty()) {
713  ScalarType infectedCritical_populations =
714  details::get_total_InfectedCritical_from_populations<Populations>(populations);
715  auto infectedCritical_reported = details::get_icu_from_divi_data(divi_data, date);
716  if (!(infectedCritical_reported)) {
717  return infectedCritical_reported.error();
718  }
719  return details::rescale_to_divi_data<Populations>(populations, infectedCritical_reported.value(),
720  infectedCritical_populations);
721  }
722 
723  return success();
724 }
725 
726 } // namespace lsecir
727 } // namespace mio
728 #endif // MEMILIO_HAS_JSONCPP
729 
730 #endif // LCTSECIR_PARAMETERS_IO_H
double ScalarType
Configuration of memilio library.
Definition: memilio/config.h:30
static double floor(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &x)
Definition: ad.hpp:2451
static double ceil(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &x)
Definition: ad.hpp:2449
int size(Comm comm)
Return the size of the given communicator.
Definition: miompi.cpp:75
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
auto failure(const IOStatus &s)
Create an object that is implicitly convertible to an error IOResult<T>.
Definition: io.h:380
auto i
Definition: io.h:809
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:359
int get_offset_in_days(Date date1, Date date2)
Computes the offset in days given two dates: first date minus second date.
Definition: date.h:310
void log_error(spdlog::string_view_t fmt, const Args &... args)
Definition: logging.h:100
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:294
void log_info(spdlog::string_view_t fmt, const Args &... args)
Definition: logging.h:94
static constexpr FP zero_tolerance()=delete