LCOV - code coverage report
Current view: top level - models/lct_secir - parameters_io.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 314 314 100.0 %
Date: 2025-01-17 12:16:22 Functions: 51 65 78.5 %

          Line data    Source code
       1             : /* 
       2             : * Copyright (C) 2020-2025 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"
      35             : #include "memilio/utils/metaprogramming.h"
      36             : #include "memilio/math/eigen.h"
      37             : #include "memilio/math/floating_point.h"
      38             : #include "memilio/epidemiology/lct_populations.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             : 
      51             : /**
      52             : * @brief Processes one entry of an RKI data set for the definition of an initial value vector for an LCT population.
      53             : *   
      54             : * Takes one entry of an RKI data vector and changes the value in populations accordingly. 
      55             : * This function provides sub-functionality of the set_initial_values_from_confirmed_cases() function.
      56             : *
      57             : * @param[out] populations The populations for which the inital data should be computed and set.
      58             : * @param[in] entry The entry of the RKI data set.
      59             : * @param[in] offset The offset between the date of the entry and the date for which the 
      60             : *   initial value vector is calculated.
      61             : * @param[in] staytimes A vector of the average time spent in each compartment defined in InfectionState
      62             : *    (for the group under consideration) for which the initial value vector is calculated.
      63             : * @param[in] inv_prob_SymptomsPerNoSymptoms The inverse of the probability InfectedSymptomsPerInfectedNoSymptoms
      64             : *    (for the group under consideration) for which the initial value vector is calculated.
      65             : * @param[in] severePerInfectedSymptoms Probability (for the group under consideration)
      66             : *    for which the initial value vector is calculated.
      67             : * @param[in] criticalPerSevere Probability (for the group under consideration)
      68             : *    for which the initial value vector is calculated.
      69             : * @param[in] scale_confirmed_cases Factor by which to scale the confirmed cases of RKI data to consider unreported cases.
      70             : * @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. 
      71             : *   This defines the number of age groups and the numbers of subcompartments.
      72             : * @tparam EntryType The type of the data entry of the RKI data.
      73             : * @tparam Group The age group of the entry the should be processed.
      74             : */
      75             : template <class Populations, class EntryType, size_t Group>
      76         394 : 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         394 :     size_t first_index_group = populations.template get_first_index_of_group<Group>();
      82             : 
      83             :     // Add confirmed cases at date to compartment Recovered.
      84         394 :     if (offset == 0) {
      85          72 :         populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] +=
      86          36 :             scale_confirmed_cases * entry.num_confirmed;
      87             :     }
      88             : 
      89             :     // Compute initial values for compartment InfectedNoSymptoms.
      90         394 :     if (offset >= 0 && offset <= std::ceil(staytimes[(size_t)InfectionState::InfectedNoSymptoms])) {
      91             :         // Mean stay time in each subcompartment.
      92         108 :         ScalarType time_INS_per_subcomp =
      93         108 :             staytimes[(size_t)InfectionState::InfectedNoSymptoms] /
      94             :             (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms>();
      95             :         // Index of the last subcompartment of InfectedNoSymptoms.
      96         108 :         size_t last_index_INS =
      97         108 :             first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedNoSymptoms>() +
      98         108 :             LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms>() - 1;
      99         360 :         for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedNoSymptoms>();
     100             :              i++) {
     101         252 :             if (offset == std::floor(i * time_INS_per_subcomp)) {
     102         168 :                 populations[last_index_INS - i] -=
     103          84 :                     (1 - (i * time_INS_per_subcomp - std::floor(i * time_INS_per_subcomp))) *
     104          84 :                     inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
     105             :             }
     106         252 :             if (offset == std::ceil(i * time_INS_per_subcomp)) {
     107         168 :                 populations[last_index_INS - i] -= (i * time_INS_per_subcomp - std::floor(i * time_INS_per_subcomp)) *
     108         168 :                                                    inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases *
     109          84 :                                                    entry.num_confirmed;
     110             :             }
     111         252 :             if (offset == std::floor((i + 1) * time_INS_per_subcomp)) {
     112         168 :                 populations[last_index_INS - i] +=
     113          84 :                     (1 - ((i + 1) * time_INS_per_subcomp - std::floor((i + 1) * time_INS_per_subcomp))) *
     114          84 :                     inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
     115             :             }
     116         252 :             if (offset == std::ceil((i + 1) * time_INS_per_subcomp)) {
     117         168 :                 populations[last_index_INS - i] +=
     118          84 :                     ((i + 1) * time_INS_per_subcomp - std::floor((i + 1) * time_INS_per_subcomp)) *
     119          84 :                     inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
     120             :             }
     121             :         }
     122             :     }
     123             : 
     124             :     // Compute initial values for compartment Exposed.
     125         536 :     if (offset >= std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms]) &&
     126         142 :         offset <= std::ceil(staytimes[(size_t)InfectionState::Exposed] +
     127             :                             staytimes[(size_t)InfectionState::InfectedNoSymptoms])) {
     128             :         // Mean stay time in each subcompartment.
     129         142 :         ScalarType time_Exposed_per_subcomp =
     130         142 :             staytimes[(size_t)InfectionState::Exposed] /
     131             :             (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed>();
     132             :         // Index of the last subcompartment of Exposed.
     133         284 :         size_t last_index_Exposed = first_index_group +
     134         142 :                                     LctStateGroup::template get_first_index<InfectionState::Exposed>() +
     135         142 :                                     LctStateGroup::template get_num_subcompartments<InfectionState::Exposed>() - 1;
     136         378 :         for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::Exposed>(); i++) {
     137         472 :             if (offset ==
     138         236 :                 std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp)) {
     139          60 :                 populations[last_index_Exposed - i] -=
     140          60 :                     (1 - (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp -
     141          60 :                           std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] +
     142          60 :                                      i * time_Exposed_per_subcomp))) *
     143          60 :                     inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
     144             :             }
     145         472 :             if (offset ==
     146         236 :                 std::ceil(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp)) {
     147          60 :                 populations[last_index_Exposed - i] -=
     148          60 :                     (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp -
     149          60 :                      std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + i * time_Exposed_per_subcomp)) *
     150          60 :                     inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
     151             :             }
     152         236 :             if (offset == std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] +
     153         236 :                                      (i + 1) * time_Exposed_per_subcomp)) {
     154          60 :                 populations[last_index_Exposed - i] +=
     155          60 :                     (1 - (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + (i + 1) * time_Exposed_per_subcomp -
     156          60 :                           std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] +
     157          60 :                                      (i + 1) * time_Exposed_per_subcomp))) *
     158          60 :                     inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
     159             :             }
     160         472 :             if (offset ==
     161         236 :                 std::ceil(staytimes[(size_t)InfectionState::InfectedNoSymptoms] + (i + 1) * time_Exposed_per_subcomp)) {
     162          58 :                 populations[last_index_Exposed - i] +=
     163          58 :                     (staytimes[(size_t)InfectionState::InfectedNoSymptoms] + (i + 1) * time_Exposed_per_subcomp -
     164          58 :                      std::floor(staytimes[(size_t)InfectionState::InfectedNoSymptoms] +
     165          58 :                                 (i + 1) * time_Exposed_per_subcomp)) *
     166          58 :                     inv_prob_SymptomsPerNoSymptoms * scale_confirmed_cases * entry.num_confirmed;
     167             :             }
     168             :         }
     169             :     }
     170             : 
     171             :     // Compute initial values for compartment InfectedSymptoms.
     172         394 :     if (offset >= std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms]) && offset <= 0) {
     173             :         // Mean stay time in each subcompartment.
     174         144 :         ScalarType time_IS_per_subcomp =
     175         144 :             staytimes[(size_t)InfectionState::InfectedSymptoms] /
     176             :             (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms>();
     177             :         // Index of the first subcompartment of InfectedSymptoms.
     178         144 :         size_t first_index_IS =
     179         144 :             first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms>();
     180         384 :         for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms>();
     181             :              i++) {
     182         240 :             if (offset == std::floor(-time_IS_per_subcomp * (i + 1))) {
     183         120 :                 populations[first_index_IS + i] -=
     184          60 :                     (1 - (-(i + 1.) * time_IS_per_subcomp - std::floor(-(i + 1) * time_IS_per_subcomp))) *
     185          60 :                     scale_confirmed_cases * entry.num_confirmed;
     186             :             }
     187         240 :             if (offset == std::ceil(-time_IS_per_subcomp * (i + 1))) {
     188         120 :                 populations[first_index_IS + i] -=
     189          60 :                     (-(i + 1) * time_IS_per_subcomp - std::floor(-(i + 1) * time_IS_per_subcomp)) *
     190          60 :                     scale_confirmed_cases * entry.num_confirmed;
     191             :             }
     192         240 :             if (offset == std::floor(-time_IS_per_subcomp * i)) {
     193         120 :                 populations[first_index_IS + i] +=
     194         120 :                     (1 - (-i * time_IS_per_subcomp - std::floor(-i * time_IS_per_subcomp))) * scale_confirmed_cases *
     195          60 :                     entry.num_confirmed;
     196             :             }
     197         240 :             if (offset == std::ceil(-time_IS_per_subcomp * i)) {
     198         120 :                 populations[first_index_IS + i] += (-i * time_IS_per_subcomp - std::floor(-i * time_IS_per_subcomp)) *
     199          60 :                                                    scale_confirmed_cases * entry.num_confirmed;
     200             :             }
     201             :         }
     202             :     }
     203             : 
     204             :     // Compute initial values for compartment InfectedSevere.
     205         394 :     if (offset >= std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     206         752 :                              staytimes[(size_t)InfectionState::InfectedSevere]) &&
     207         358 :         offset <= std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms])) {
     208             :         // Mean stay time in each subcompartment.
     209         144 :         ScalarType time_ISev_per_subcomp =
     210         144 :             staytimes[(size_t)InfectionState::InfectedSevere] /
     211             :             (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere>();
     212             :         // Index of the first subcompartment of InfectedSevere.
     213         144 :         size_t first_index_ISev =
     214         144 :             first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSevere>();
     215         384 :         for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere>();
     216             :              i++) {
     217         480 :             if (offset ==
     218         240 :                 std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * (i + 1))) {
     219          60 :                 populations[first_index_ISev + i] -=
     220          60 :                     severePerInfectedSymptoms *
     221          60 :                     (1 - (-staytimes[(size_t)InfectionState::InfectedSymptoms] - (i + 1) * time_ISev_per_subcomp -
     222          60 :                           std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     223          60 :                                      (i + 1) * time_ISev_per_subcomp))) *
     224          60 :                     scale_confirmed_cases * entry.num_confirmed;
     225             :             }
     226         480 :             if (offset ==
     227         240 :                 std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * (i + 1))) {
     228          60 :                 populations[first_index_ISev + i] -=
     229          60 :                     severePerInfectedSymptoms *
     230          60 :                     (-staytimes[(size_t)InfectionState::InfectedSymptoms] - (i + 1) * time_ISev_per_subcomp -
     231          60 :                      std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     232          60 :                                 (i + 1) * time_ISev_per_subcomp)) *
     233          60 :                     scale_confirmed_cases * entry.num_confirmed;
     234             :             }
     235         480 :             if (offset ==
     236         240 :                 std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * i)) {
     237          60 :                 populations[first_index_ISev + i] +=
     238          60 :                     severePerInfectedSymptoms *
     239          60 :                     (1 -
     240          60 :                      (-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp -
     241          60 :                       std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp))) *
     242          60 :                     scale_confirmed_cases * entry.num_confirmed;
     243             :             }
     244         240 :             if (offset == std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] - time_ISev_per_subcomp * i)) {
     245          60 :                 populations[first_index_ISev + i] +=
     246          60 :                     severePerInfectedSymptoms *
     247          60 :                     (-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp -
     248          60 :                      std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] - i * time_ISev_per_subcomp)) *
     249          60 :                     scale_confirmed_cases * entry.num_confirmed;
     250             :             }
     251             :         }
     252             :     }
     253             : 
     254             :     // Compute initial values for compartment InfectedCritical.
     255         788 :     if (offset >= std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     256         394 :                              staytimes[(size_t)InfectionState::InfectedSevere] -
     257         788 :                              staytimes[(size_t)InfectionState::InfectedCritical]) &&
     258         394 :         offset <= std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     259             :                             staytimes[(size_t)InfectionState::InfectedSevere])) {
     260             :         // Mean stay time in each subcompartment.
     261         108 :         ScalarType time_ICri_per_subcomp =
     262         108 :             staytimes[(size_t)InfectionState::InfectedCritical] /
     263             :             (ScalarType)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
     264             :         // Index of the first subcompartment of InfectedCritical.
     265         108 :         size_t first_index_ICri =
     266         108 :             first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical>();
     267         276 :         for (int i = 0; i < (int)LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
     268             :              i++) {
     269         336 :             if (offset ==
     270         168 :                 std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     271         168 :                            staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * (i + 1))) {
     272          56 :                 populations[first_index_ICri + i] -=
     273         112 :                     severePerInfectedSymptoms * criticalPerSevere *
     274          56 :                     (1 - (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     275         112 :                           staytimes[(size_t)InfectionState::InfectedSevere] - (i + 1) * time_ICri_per_subcomp -
     276          56 :                           std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     277             :                                      staytimes[(size_t)InfectionState::InfectedSevere] -
     278          56 :                                      (i + 1) * time_ICri_per_subcomp))) *
     279          56 :                     scale_confirmed_cases * entry.num_confirmed;
     280             :             }
     281         336 :             if (offset ==
     282         168 :                 std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     283         168 :                           staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * (i + 1))) {
     284          56 :                 populations[first_index_ICri + i] -=
     285         112 :                     severePerInfectedSymptoms * criticalPerSevere *
     286          56 :                     (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     287         112 :                      staytimes[(size_t)InfectionState::InfectedSevere] - (i + 1) * time_ICri_per_subcomp -
     288          56 :                      std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     289          56 :                                 staytimes[(size_t)InfectionState::InfectedSevere] - (i + 1) * time_ICri_per_subcomp)) *
     290          56 :                     scale_confirmed_cases * entry.num_confirmed;
     291             :             }
     292         168 :             if (offset == std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     293         168 :                                      staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * i)) {
     294          56 :                 populations[first_index_ICri + i] +=
     295         112 :                     severePerInfectedSymptoms * criticalPerSevere *
     296          56 :                     (1 - (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     297         112 :                           staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp -
     298          56 :                           std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     299          56 :                                      staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp))) *
     300          56 :                     scale_confirmed_cases * entry.num_confirmed;
     301             :             }
     302         168 :             if (offset == std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     303         168 :                                     staytimes[(size_t)InfectionState::InfectedSevere] - time_ICri_per_subcomp * i)) {
     304          56 :                 populations[first_index_ICri + i] +=
     305         112 :                     severePerInfectedSymptoms * criticalPerSevere *
     306          56 :                     (-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     307         112 :                      staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp -
     308          56 :                      std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     309          56 :                                 staytimes[(size_t)InfectionState::InfectedSevere] - i * time_ICri_per_subcomp)) *
     310          56 :                     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         788 :     if (offset == std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     318         394 :                              staytimes[(size_t)InfectionState::InfectedSevere] -
     319             :                              staytimes[(size_t)InfectionState::InfectedCritical])) {
     320          36 :         populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead>()] +=
     321          36 :             (1 -
     322          36 :              (-staytimes[(size_t)InfectionState::InfectedSymptoms] - staytimes[(size_t)InfectionState::InfectedSevere] -
     323          36 :               staytimes[(size_t)InfectionState::InfectedCritical] -
     324          72 :               std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     325          36 :                          staytimes[(size_t)InfectionState::InfectedSevere] -
     326          36 :                          staytimes[(size_t)InfectionState::InfectedCritical]))) *
     327          36 :             entry.num_deaths;
     328             :     }
     329         788 :     if (offset == std::ceil(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     330         394 :                             staytimes[(size_t)InfectionState::InfectedSevere] -
     331             :                             staytimes[(size_t)InfectionState::InfectedCritical])) {
     332          36 :         populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead>()] +=
     333          36 :             (-staytimes[(size_t)InfectionState::InfectedSymptoms] - staytimes[(size_t)InfectionState::InfectedSevere] -
     334          36 :              staytimes[(size_t)InfectionState::InfectedCritical] -
     335          72 :              std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     336          36 :                         staytimes[(size_t)InfectionState::InfectedSevere] -
     337          36 :                         staytimes[(size_t)InfectionState::InfectedCritical])) *
     338          36 :             entry.num_deaths;
     339             :     }
     340         394 : }
     341             : 
     342             : /**
     343             : * @brief Computes an initialization vector for an LCT population with case data from RKI recursively for each age group
     344             : *    (or for one age group in the case without age resolution).
     345             : *   
     346             : * Please use the set_initial_values_from_reported_data() function, which calls this function automatically!
     347             : * This function calculates a segment referring to the defined age group of the initial value vector with 
     348             : *   subcompartments using the rki_data and the parameters.
     349             : * The values for the whole initial value vector stored in populations are calculated recursively.
     350             : *
     351             : * @param[in] rki_data Vector with the RKI data.
     352             : * @param[out] populations The populations for which the inital data should be computed and set.
     353             : * @param[in] parameters The parameters that should be used to calculate the initial values. 
     354             : *   Probabilities and mean stay times are used.
     355             : * @param[in] date Date for which the initial values should be computed. date is the start date of the simulation.
     356             : * @param[in] total_population Total size of the population of Germany or of every age group. 
     357             : * @param[in] scale_confirmed_cases Factor(s for each age group) by which to scale the confirmed cases of the rki data 
     358             : *   to consider unreported cases.
     359             : * @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. 
     360             : *   This defines the number of age groups and the numbers of subcompartments.
     361             : * @tparam EntryType is expected to be ConfirmedCasesNoAgeEntry for data that is not age resolved and 
     362             : *   ConfirmedCasesDataEntry for age resolved data. See also epi_data.h.
     363             : * @tparam Group The age group for which the initial values should be calculated. The function is called recursively 
     364             : *   such that the initial values are calculated for every age group if Group is zero at the beginning.
     365             : * @returns Any io errors that happen during data processing.
     366             : */
     367             : template <class Populations, class EntryType, size_t Group = 0>
     368          36 : IOResult<void> set_initial_values_from_confirmed_cases(Populations& populations, const std::vector<EntryType>& rki_data,
     369             :                                                        const Parameters& 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          36 :     size_t first_index_group = populations.template get_first_index_of_group<Group>();
     376             : 
     377             :     // Define variables for parameters.
     378          72 :     std::vector<ScalarType> staytimes((size_t)InfectionState::Count, -1.);
     379          36 :     staytimes[(size_t)InfectionState::Exposed]            = parameters.template get<TimeExposed>()[Group];
     380          36 :     staytimes[(size_t)InfectionState::InfectedNoSymptoms] = parameters.template get<TimeInfectedNoSymptoms>()[Group];
     381          36 :     staytimes[(size_t)InfectionState::InfectedSymptoms]   = parameters.template get<TimeInfectedSymptoms>()[Group];
     382          36 :     staytimes[(size_t)InfectionState::InfectedSevere]     = parameters.template get<TimeInfectedSevere>()[Group];
     383          36 :     staytimes[(size_t)InfectionState::InfectedCritical]   = parameters.template get<TimeInfectedCritical>()[Group];
     384          36 :     ScalarType inv_prob_SymptomsPerNoSymptoms =
     385          36 :         1 / (1 - parameters.template get<RecoveredPerInfectedNoSymptoms>()[Group]);
     386          36 :     ScalarType prob_SeverePerInfectedSymptoms = parameters.template get<SeverePerInfectedSymptoms>()[Group];
     387          36 :     ScalarType prob_CriticalPerSevere         = parameters.template get<CriticalPerSevere>()[Group];
     388             : 
     389          72 :     ScalarType min_offset_needed = std::floor(-staytimes[(size_t)InfectionState::InfectedSymptoms] -
     390          36 :                                               staytimes[(size_t)InfectionState::InfectedSevere] -
     391             :                                               staytimes[(size_t)InfectionState::InfectedCritical]);
     392             :     ScalarType max_offset_needed =
     393          36 :         std::ceil(staytimes[(size_t)InfectionState::Exposed] + staytimes[(size_t)InfectionState::InfectedNoSymptoms]);
     394             : 
     395          36 :     bool min_offset_needed_avail = false;
     396          36 :     bool max_offset_needed_avail = false;
     397             : 
     398             :     // Go through the entries of rki_data and check if the entry is age resolved and is referring to
     399             :     // the age_group Group in the case with age resolution. If the date is
     400             :     // needed for calculation, another function to handle the entry is called. Confirmed cases are scaled by scale_confirmed_cases.
     401        2388 :     for (auto&& entry : rki_data) {
     402             :         if constexpr (std::is_same_v<EntryType, ConfirmedCasesDataEntry>) {
     403        2304 :             if (!((size_t)entry.age_group == Group)) {
     404        1920 :                 continue;
     405             :             }
     406             :         }
     407         432 :         int offset = get_offset_in_days(entry.date, date);
     408         432 :         if ((offset >= min_offset_needed) && (offset <= max_offset_needed)) {
     409         394 :             if (offset == max_offset_needed) {
     410          34 :                 max_offset_needed_avail = true;
     411             :             }
     412         394 :             if (offset == min_offset_needed) {
     413          36 :                 min_offset_needed_avail = true;
     414             :             }
     415         394 :             process_entry<Populations, EntryType, Group>(populations, entry, offset, staytimes,
     416             :                                                          inv_prob_SymptomsPerNoSymptoms, prob_SeverePerInfectedSymptoms,
     417             :                                                          prob_CriticalPerSevere, scale_confirmed_cases[Group]);
     418             :         }
     419             :     }
     420             : 
     421             :     // Compute Recovered.
     422          36 :     populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] -=
     423             :         populations.get_compartments()
     424         108 :             .segment(first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedSymptoms>(),
     425          36 :                      LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSymptoms>() +
     426          36 :                          LctStateGroup::template get_num_subcompartments<InfectionState::InfectedSevere>() +
     427          36 :                          LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>())
     428          72 :             .sum();
     429          36 :     populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] -=
     430          36 :         populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Dead>()];
     431             : 
     432             :     // Compute Susceptibles.
     433          72 :     populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Susceptible>()] =
     434          36 :         total_population[Group] -
     435          72 :         populations.get_compartments().segment(first_index_group + 1, LctStateGroup::Count - 1).sum();
     436             : 
     437             :     // Check whether all necessary dates are available.
     438          36 :     if (!max_offset_needed_avail || !min_offset_needed_avail) {
     439           2 :         log_error(
     440             :             "Necessary range of dates needed to compute initial values does not exist in RKI data for group {:d}.",
     441             :             Group);
     442             :         return failure(StatusCode::OutOfRange,
     443           2 :                        "Necessary range of dates needed to compute initial values does not exist in RKI data.");
     444             :     }
     445             : 
     446             :     // Check if all values for populations are valid.
     447         130 :     for (size_t i = first_index_group; i < LctStateGroup::Count; i++) {
     448          98 :         if (floating_point_less<ScalarType>((ScalarType)populations[i], 0., Limits<ScalarType>::zero_tolerance())) {
     449           2 :             log_error("Something went wrong in the initialization of group {:d}. At least one entry is negative.",
     450             :                       Group);
     451             :             return failure(StatusCode::InvalidValue,
     452           2 :                            "Something went wrong in the initialization as at least one entry is negative.");
     453             :         }
     454             :     }
     455             : 
     456             :     if constexpr (Group + 1 < Populations::num_groups) {
     457             :         return set_initial_values_from_confirmed_cases<Populations, EntryType, Group + 1>(
     458          25 :             populations, rki_data, parameters, date, total_population, scale_confirmed_cases);
     459             :     }
     460             :     else {
     461          14 :         return success();
     462             :     }
     463          36 : }
     464             : 
     465             : /**
     466             : * @brief Computes the total number of patients in Intensive Care Units (in all groups and subcompartments) 
     467             : *       in the provided Population.
     468             : *   
     469             : * This function calculates the total number of individuals within the compartment InfectedCritical 
     470             : * for the Populations-data provided, irrespective of their subcompartment or group. 
     471             : * This total number can be used to scale the entries so that the total number in InfectedCritical is equal to 
     472             : * the number of ICU patients reported in the DIVI data. 
     473             : * Please use the set_initial_values_from_reported_data() function, which calls this function automatically!
     474             : *
     475             : * @param[in] populations The populations for which the total number in InfectedCritical should be computed.
     476             : * @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. 
     477             : *   This defines the number of age groups and the numbers of subcompartments.
     478             : * @tparam Group The age group for which the total number should be calculated. The function is called recursively 
     479             : *   such that the total number in InfectedCritical within all groups is calculated if Group is zero at the beginning.
     480             : * @returns The total number of patients in Intensive Care Units (in all groups and subcompartments).
     481             : */
     482             : template <class Populations, size_t Group = 0>
     483          24 : ScalarType get_total_InfectedCritical_from_populations(const Populations& populations)
     484             : {
     485             :     using LctStateGroup      = type_at_index_t<Group, typename Populations::LctStatesGroups>;
     486          24 :     size_t first_index_group = populations.template get_first_index_of_group<Group>();
     487             : 
     488          24 :     ScalarType infectedCritical_Group =
     489             :         populations.get_compartments()
     490          48 :             .segment(first_index_group + LctStateGroup::template get_first_index<InfectionState::InfectedCritical>(),
     491             :                      LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>())
     492          24 :             .sum();
     493             : 
     494             :     if constexpr (Group + 1 < Populations::num_groups) {
     495             :         return infectedCritical_Group +
     496          20 :                get_total_InfectedCritical_from_populations<Populations, Group + 1>(populations);
     497             :     }
     498             :     else {
     499           4 :         return infectedCritical_Group;
     500             :     }
     501             : }
     502             : 
     503             : /**
     504             : * @brief Extract the reported number of patients in ICU for a specific date from DIVI data.
     505             : *
     506             : * @param[in] divi_data Vector with reported DIVI data.
     507             : * @param[in] date Date for which the reported number of patients in ICU should be extracted.
     508             : * @returns The reported number of patients in ICU or any io errors that happen during data processing.
     509             : */
     510           4 : IOResult<ScalarType> get_icu_from_divi_data(const std::vector<DiviEntry>& divi_data, const Date date)
     511             : {
     512          28 :     for (auto&& entry : divi_data) {
     513          27 :         int offset = get_offset_in_days(entry.date, date);
     514          27 :         if (offset == 0) {
     515           3 :             return success(entry.num_icu);
     516             :         }
     517             :     }
     518           1 :     log_error("Specified date does not exist in DIVI data.");
     519           1 :     return failure(StatusCode::OutOfRange, "Specified date does not exist in DIVI data.");
     520             : }
     521             : 
     522             : /**
     523             : * @brief Rescales the entries for InfectedCritical in populations such that the total number 
     524             : *   equals the reported number.
     525             : *   
     526             : * This function rescales the entries for InfectedCritical in the given population for every group and subcompartment 
     527             : * such that the total number in all InfectedCritical compartments equals the reported number infectedCritical_reported.
     528             : *
     529             : * If the total number of individuals in InfectedCritical in populations is zero and the reported number is not,
     530             : * the reported number is distributed uniformly across the groups. 
     531             : * Within the groups, the number is distributed uniformly to the subcompartments. 
     532             : * Note that especially the uniform distribution across groups is not necessarily realistic, 
     533             : * because the need for intensive care can differ by group.
     534             : *
     535             : * @param[in,out] populations The populations for which the entries of the InfectedCritical compartments are rescaled.
     536             : * @param[in] infectedCritical_reported The reported number for patients in ICU. The total number of individuals in the 
     537             : *   InfectedCritical compartment in populations will be equal to this value afterward.
     538             : *   You can calculate this value with the get_icu_from_divi_data() function.
     539             : * @param[in] infectedCritical_populations The current total number of individuals in the InfectedCritical compartment 
     540             : *   in populations. You can calculate this value with the get_total_InfectedCritical_from_populations() function.
     541             : * @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. 
     542             : *   This defines the number of age groups and the numbers of subcompartments.
     543             : * @tparam Group The age group for which the entries of InfectedCritical should be scaled. 
     544             : *   The function is called recursively for the groups. The total number in the InfectedCritical compartments is only 
     545             : *   equal to infectedCritical_reported after the function call if Group is set to zero in the beginning.
     546             : * @returns Any io errors that happen during the scaling.
     547             : */
     548             : template <class Populations, size_t Group = 0>
     549          14 : IOResult<void> rescale_to_divi_data(Populations& populations, const ScalarType infectedCritical_reported,
     550             :                                     const ScalarType infectedCritical_populations)
     551             : {
     552          14 :     if (floating_point_less<ScalarType>(infectedCritical_reported, 0., Limits<ScalarType>::zero_tolerance())) {
     553           1 :         log_error("The provided reported number of InfectedCritical is negative. Please check the data.");
     554             :         return failure(StatusCode::InvalidValue,
     555           1 :                        "The provided reported number of InfectedCritical is negative. Please check the data.");
     556             :     }
     557             : 
     558             :     using LctStateGroup      = type_at_index_t<Group, typename Populations::LctStatesGroups>;
     559          13 :     size_t first_index_group = populations.template get_first_index_of_group<Group>();
     560             : 
     561          13 :     if (floating_point_equal<ScalarType>(infectedCritical_populations, 0., Limits<ScalarType>::zero_tolerance())) {
     562           7 :         if (!(floating_point_equal<ScalarType>(infectedCritical_reported, 0., Limits<ScalarType>::zero_tolerance()))) {
     563           7 :             log_info("The calculated number of patients in intensive care is zero, although the reported number is "
     564             :                      "not. The reported number is uniformly distributed across groups and subcompartments. Note "
     565             :                      "that this is not necessarily realistic.");
     566           7 :             size_t num_InfectedCritical =
     567             :                 LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
     568           7 :             ScalarType num_age_groups = (ScalarType)Populations::num_groups;
     569             :             // Distribute reported number uniformly to age groups and subcompartments.
     570          30 :             for (size_t subcompartment = 0;
     571          30 :                  subcompartment < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
     572             :                  subcompartment++) {
     573          23 :                 populations[first_index_group +
     574          23 :                             LctStateGroup::template get_first_index<InfectionState::InfectedCritical>() +
     575          23 :                             subcompartment] =
     576          23 :                     infectedCritical_reported / ((ScalarType)num_InfectedCritical * num_age_groups);
     577             :             }
     578             :             // Adjust Recovered compartment.
     579           7 :             populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] -=
     580           7 :                 infectedCritical_reported / num_age_groups;
     581             :             // Number of Susceptibles is not affected because Recovered is adjusted accordingly.
     582             :         }
     583             :     }
     584             :     else {
     585             :         // Adjust number of Recovered by adding the old number in InfectedCritical
     586             :         // and subtracting the new number (= scaling_factor * old number).
     587           6 :         ScalarType scaling_factor_infectedCritical = infectedCritical_reported / infectedCritical_populations;
     588           6 :         populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()] +=
     589           6 :             (1 - scaling_factor_infectedCritical) *
     590             :             populations.get_compartments()
     591          18 :                 .segment(first_index_group +
     592           6 :                              LctStateGroup::template get_first_index<InfectionState::InfectedCritical>(),
     593             :                          LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>())
     594           6 :                 .sum();
     595             :         // Adjust InfectedCritical.
     596           6 :         for (size_t subcompartment = 0;
     597          15 :              subcompartment < LctStateGroup::template get_num_subcompartments<InfectionState::InfectedCritical>();
     598             :              subcompartment++) {
     599          18 :             populations[first_index_group +
     600          18 :                         LctStateGroup::template get_first_index<InfectionState::InfectedCritical>() + subcompartment] *=
     601             :                 scaling_factor_infectedCritical;
     602             :         }
     603             :         // Number of Susceptibles is not affected because Recovered is adjusted accordingly.
     604             :     }
     605          26 :     if (floating_point_less<ScalarType>(
     606          13 :             (ScalarType)
     607          13 :                 populations[first_index_group + LctStateGroup::template get_first_index<InfectionState::Recovered>()],
     608             :             0., Limits<ScalarType>::zero_tolerance())) {
     609           1 :         log_error(
     610             :             "Scaling with reported DIVI data led to a negative entry in the Recovered compartment for group {:d}.",
     611             :             Group);
     612             :         return failure(StatusCode::InvalidValue,
     613           1 :                        "Scaling with reported DIVI data led to a negative entry in a Recovered compartment.");
     614             :     }
     615             :     if constexpr (Group + 1 < Populations::num_groups) {
     616             :         return rescale_to_divi_data<Populations, Group + 1>(populations, infectedCritical_reported,
     617          10 :                                                             infectedCritical_populations);
     618             :     }
     619             :     else {
     620           4 :         return success();
     621             :     }
     622             : }
     623             : } // namespace details
     624             : 
     625             : /**
     626             : * @brief Computes an initialization vector for an LCT population with case data from RKI (and possibly DIVI data).
     627             : *   
     628             : * Use just one group in the definition of the populations to not divide between age groups.
     629             : * Otherwise, the number of groups has to match the number of RKI age groups.
     630             : * The function calculates an initial value vector referring to an LCT population and updates the initial value vector
     631             : * in the populations class.
     632             : * For the computation expected stay times in the subcompartments defined in the parameters variable are used.
     633             : * To calculate the initial values, we assume for simplicity that individuals stay in the subcompartment 
     634             : * for exactly the expected time.
     635             : * The RKI data are linearly interpolated within one day to match the expected stay time in a subcompartment.
     636             : * The RKI data should contain data for each needed day with or without division of age groups, 
     637             : *   the completeness of the dates is not verified.
     638             : * Data can be downloaded e.g. with the file pycode/memilio-epidata/memilio/epidata/getCaseData.py, which creates files
     639             : * named e.g. cases_all_germany.json for no groups or cases_all_age.json with division in age groups or similar names.
     640             : * One should set impute_dates=True so that missing dates are imputed. 
     641             : * To read the data into a vector, use the functionality from epi_data.h.
     642             : * The data and the number of entries in the total_population and scale_confirmed_cases vectors have to match the 
     643             : *   number of groups used in Populations.
     644             : *
     645             : * Additionally, one can scale the result from the calculation with the RKI data to match the reported number of 
     646             : * patients in ICUs. The patient numbers are provided by DIVI and can be downloaded e.g. using 
     647             : * pycode/memilio-epidata/memilio/epidata/getDIVIData.py (One should also set impute_dates=True so that missing dates
     648             : * are imputed.). Again, to read the data into a vector, use the functionality from epi_data.h.
     649             : *
     650             : * @param[in] rki_data Vector with the RKI data.
     651             : * @param[out] populations The populations for which the inital data should be computed and set.
     652             : * @param[in] parameters The parameters that should be used to calculate the initial values. 
     653             : *   Probabilities and mean stay times are used.
     654             : * @param[in] date Date for which the initial values should be computed. date is the start date of the simulation.
     655             : * @param[in] total_population Total size of the population of Germany or of every age group. 
     656             : * @param[in] scale_confirmed_cases Factor(s for each age group) by which to scale the confirmed cases of the rki data 
     657             : *   to consider unreported cases.
     658             : * @param[in] divi_data Vector with DIVI data used to scale the number of individuals in the InfectedCritical 
     659             : *   compartments in populations so that the total number match the reported number. 
     660             : *   For the default value (an empty vector), the calculated populations using the RKI data is not scaled.
     661             : * @tparam Populations is expected to be an LctPopulations defined in epidemiology/lct_populations. 
     662             : *   This defines the number of age groups and the numbers of subcompartments.
     663             : * @tparam EntryType is expected to be ConfirmedCasesNoAgeEntry for data that is not age resolved and 
     664             : *   ConfirmedCasesDataEntry for age resolved data. See also epi_data.h.
     665             : * @returns Any io errors that happen during data processing.
     666             : */
     667             : template <class Populations, class EntryType>
     668          15 : IOResult<void> set_initial_values_from_reported_data(const std::vector<EntryType>& rki_data, Populations& populations,
     669             :                                                      const Parameters& parameters, const Date date,
     670             :                                                      const std::vector<ScalarType>& total_population,
     671             :                                                      const std::vector<ScalarType>& scale_confirmed_cases,
     672             :                                                      const std::vector<DiviEntry>& divi_data = std::vector<DiviEntry>())
     673             : { // Check if the inputs are matching.
     674          15 :     assert(total_population.size() == Populations::num_groups);
     675          15 :     assert(scale_confirmed_cases.size() == Populations::num_groups);
     676             :     if constexpr (Populations::num_groups > 1) {
     677             :         static_assert(std::is_same_v<EntryType, ConfirmedCasesDataEntry>);
     678           9 :         assert(ConfirmedCasesDataEntry::age_group_names.size() == Populations::num_groups);
     679             :     }
     680             :     else {
     681             :         static_assert(std::is_same_v<EntryType, ConfirmedCasesNoAgeEntry>);
     682             :     }
     683             :     // Check if RKI data vector is valid.
     684         638 :     auto max_date_entry = std::max_element(rki_data.begin(), rki_data.end(), [](auto&& a, auto&& b) {
     685         623 :         return a.date < b.date;
     686             :     });
     687          15 :     if (max_date_entry == rki_data.end()) {
     688           2 :         log_error("RKI data file is empty.");
     689           2 :         return failure(StatusCode::InvalidFileFormat, "RKI data is empty.");
     690             :     }
     691          13 :     auto max_date = max_date_entry->date;
     692          13 :     if (max_date < date) {
     693           2 :         log_error("Specified date does not exist in RKI data.");
     694           2 :         return failure(StatusCode::OutOfRange, "Specified date does not exist in RKI data.");
     695             :     }
     696             :     // Initially set populations to zero.
     697         579 :     for (size_t i = 0; i < populations.get_num_compartments(); i++) {
     698         568 :         populations[i] = 0;
     699             :     }
     700             :     // Set populations using the RKI data.
     701          11 :     IOResult<void> ioresult_confirmedcases = details::set_initial_values_from_confirmed_cases<Populations, EntryType>(
     702             :         populations, rki_data, parameters, date, total_population, scale_confirmed_cases);
     703          11 :     if (!(ioresult_confirmedcases)) {
     704           4 :         return ioresult_confirmedcases;
     705             :     }
     706             : 
     707             :     // Check if DIVI data is provided and scale the result in populations accordingly.
     708           7 :     if (!divi_data.empty()) {
     709             :         ScalarType infectedCritical_populations =
     710           2 :             details::get_total_InfectedCritical_from_populations<Populations>(populations);
     711           2 :         auto infectedCritical_reported = details::get_icu_from_divi_data(divi_data, date);
     712           2 :         if (!(infectedCritical_reported)) {
     713           1 :             return infectedCritical_reported.error();
     714             :         }
     715           1 :         return details::rescale_to_divi_data<Populations>(populations, infectedCritical_reported.value(),
     716           1 :                                                           infectedCritical_populations);
     717           2 :     }
     718             : 
     719          10 :     return success();
     720          11 : }
     721             : 
     722             : } // namespace lsecir
     723             : } // namespace mio
     724             : #endif // MEMILIO_HAS_JSONCPP
     725             : 
     726             : #endif // LCTSECIR_PARAMETERS_IO_H

Generated by: LCOV version 1.14