LCOV - code coverage report
Current view: top level - models/ode_secirts - parameters_io.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 461 499 92.4 %
Date: 2025-01-17 12:16:22 Functions: 23 23 100.0 %

          Line data    Source code
       1             : /* 
       2             : * Copyright (C) 2020-2025 MEmilio
       3             : *
       4             : * Authors: Henrik Zunker, Wadim Koslow, Daniel Abele, Martin J. Kühn
       5             : *
       6             : * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
       7             : *
       8             : * Licensed under the Apache License, Version 2.0 (the "License");
       9             : * you may not use this file except in compliance with the License.
      10             : * You may obtain a copy of the License at
      11             : *
      12             : *     http://www.apache.org/licenses/LICENSE-2.0
      13             : *
      14             : * Unless required by applicable law or agreed to in writing, software
      15             : * distributed under the License is distributed on an "AS IS" BASIS,
      16             : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      17             : * See the License for the specific language governing permissions and
      18             : * limitations under the License.
      19             : */
      20             : #ifndef MIO_ODE_SECIRTS_PARAMETERS_IO_H
      21             : #define MIO_ODE_SECIRTS_PARAMETERS_IO_H
      22             : 
      23             : #include "memilio/config.h"
      24             : 
      25             : #ifdef MEMILIO_HAS_JSONCPP
      26             : 
      27             : #include "ode_secirts/model.h"
      28             : #include "ode_secirts/analyze_result.h"
      29             : #include "memilio/math/eigen_util.h"
      30             : #include "memilio/mobility/graph.h"
      31             : #include "memilio/mobility/metapopulation_mobility_instant.h"
      32             : #include "memilio/io/epi_data.h"
      33             : #include "memilio/io/io.h"
      34             : #include "memilio/io/json_serializer.h"
      35             : #include "memilio/io/result_io.h"
      36             : #include "memilio/utils/logging.h"
      37             : #include "memilio/utils/date.h"
      38             : 
      39             : namespace mio
      40             : {
      41             : namespace osecirts
      42             : {
      43             : 
      44             : namespace details
      45             : {
      46             : 
      47             : /**
      48             :  * @brief Gets the region ID (county, state, or district) of an EpiDataEntry.
      49             :  * 
      50             :  * If none are available, it defaults to 0 which is representing the whole country.
      51             :  * 
      52             :  * @tparam EpiDataEntry The type of the data entry.
      53             :  * @param data_entry The (RKI) data entry to extract the region ID from.
      54             :  * @return The region ID as integer, or 0 if no specific region information is available.
      55             :  */
      56             : template <class EpiDataEntry>
      57       52164 : int get_region_id(const EpiDataEntry& data_entry)
      58             : {
      59      104328 :     if (data_entry.county_id) {
      60       36432 :         return data_entry.county_id->get();
      61             :     }
      62       31464 :     if (data_entry.state_id) {
      63           0 :         return data_entry.state_id->get();
      64             :     }
      65       31464 :     if (data_entry.district_id) {
      66       15732 :         return data_entry.district_id->get();
      67             :     }
      68           0 :     return 0;
      69             : }
      70             : 
      71             : /**
      72             :  * @brief Computes the distribution of confirmed cases across infection states based on Case (RKI) data.
      73             :  *
      74             :  * This function processes case data for given regions and distributes the cases across different 
      75             :  * infection states, considering the corresponding transition times and probabilities defined in the model. 
      76             :  *
      77             :  * @tparam Model The type of the model used.
      78             :  * @tparam FP Floating point type (default: double).
      79             :  *
      80             :  * @param[in] case_data Vector of confirmed case data entries (defined in epi_data.h).
      81             :  * @param[out] vnum_Exposed Output vector for the number of exposed individuals per age group and region.
      82             :  * @param[out] vnum_InfectedNoSymptoms Output vector for the number of infected individuals without symptoms.
      83             :  * @param[out] vnum_InfectedSymptoms Output vector for the number of infected individuals with symptoms.
      84             :  * @param[out] vnum_InfectedSevere Output vector for the number of severely infected individuals.
      85             :  * @param[out] vnum_icu Output vector for the number of individuals in critical condition (ICU).
      86             :  * @param[out] vnum_death Output vector for the number of deaths.
      87             :  * @param[out] vnum_timm_i Output vector for the number of individuals in temporary immunity state.
      88             :  * @param[in] vregion Vector of region IDs representing the regions in the model vector.
      89             :  * @param[in] date Date for which the simulation starts.
      90             :  * @param[in] model Vector of models, each representing a region and containing the parameters.
      91             :  * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases for 
      92             :  * @param[in] layer Specifies the immunity layer: 0 (Naive), 1 (Partial Immunity), 2 (Improved Immunity).
      93             :  *
      94             :  * @return An IOResult showing success or failure.
      95             :  */
      96             : template <class Model, typename FP = double>
      97         216 : IOResult<void> compute_confirmed_cases_data(
      98             :     const std::vector<ConfirmedCasesDataEntry>& case_data, std::vector<std::vector<FP>>& vnum_Exposed,
      99             :     std::vector<std::vector<FP>>& vnum_InfectedNoSymptoms, std::vector<std::vector<FP>>& vnum_InfectedSymptoms,
     100             :     std::vector<std::vector<FP>>& vnum_InfectedSevere, std::vector<std::vector<FP>>& vnum_icu,
     101             :     std::vector<std::vector<FP>>& vnum_death, std::vector<std::vector<FP>>& vnum_timm_i,
     102             :     std::vector<int> const& vregion, Date date, const std::vector<Model>& model,
     103             :     const std::vector<FP>& scaling_factor_inf, const size_t layer, bool read_icu = false)
     104             : {
     105         216 :     auto max_date_entry = std::max_element(case_data.begin(), case_data.end(), [](auto&& a, auto&& b) {
     106      114057 :         return a.date < b.date;
     107             :     });
     108         216 :     if (max_date_entry == case_data.end()) {
     109           9 :         log_error("Case data file is empty.");
     110           9 :         return failure(StatusCode::InvalidValue, "Case data is empty.");
     111             :     }
     112         207 :     auto max_date = max_date_entry->date;
     113         207 :     if (max_date < date) {
     114           9 :         log_error("Specified date does not exist in case data");
     115           9 :         return failure(StatusCode::OutOfRange, "Case data does not contain specified date.");
     116             :     }
     117             : 
     118             :     // shifts the initilization to the recent past if simulation starts
     119             :     // around current day and data of the future would be required.
     120             :     // Only needed for preinfection compartments, exposed and InfectedNoSymptoms.
     121         198 :     auto days_surplus = get_offset_in_days(max_date, date) - 6; // 6 > T_E + T_C
     122         198 :     if (days_surplus > 0) {
     123         198 :         days_surplus = 0;
     124             :     }
     125             : 
     126      109494 :     for (auto&& entry : case_data) {
     127      158976 :         auto it = std::find_if(vregion.begin(), vregion.end(), [&entry](auto r) {
     128      158976 :             return r == 0 || get_region_id(entry) == r;
     129             :         });
     130      109296 :         if (it != vregion.end()) {
     131      109296 :             auto region_idx = size_t(it - vregion.begin());
     132             : 
     133      109296 :             auto params_region           = model[region_idx].parameters;
     134      109296 :             auto& num_InfectedNoSymptoms = vnum_InfectedNoSymptoms[region_idx];
     135      109296 :             auto& num_InfectedSymptoms   = vnum_InfectedSymptoms[region_idx];
     136      109296 :             auto& num_Exposed            = vnum_Exposed[region_idx];
     137      109296 :             auto& num_InfectedSevere     = vnum_InfectedSevere[region_idx];
     138      109296 :             auto& num_death              = vnum_death[region_idx];
     139      109296 :             auto& num_icu                = vnum_icu[region_idx];
     140      109296 :             auto& num_imm                = vnum_timm_i[region_idx];
     141             : 
     142      109296 :             auto age = (size_t)entry.age_group;
     143             :             // (rounded) transition times
     144      109296 :             const int t_exposed =
     145      109296 :                 static_cast<int>(std::round(params_region.template get<TimeExposed<FP>>()[entry.age_group]));
     146      109296 :             int t_InfectedNoSymptoms =
     147      109296 :                 static_cast<int>(std::round(params_region.template get<TimeInfectedNoSymptoms<FP>>()[entry.age_group]));
     148      109296 :             int t_InfectedSymptoms =
     149      109296 :                 static_cast<int>(std::round(params_region.template get<TimeInfectedSymptoms<FP>>()[entry.age_group]));
     150      109296 :             const int t_InfectedSevere =
     151      109296 :                 static_cast<int>(std::round(params_region.template get<TimeInfectedSevere<FP>>()[entry.age_group]));
     152      109296 :             const int t_InfectedCritical =
     153      109296 :                 static_cast<int>(std::round(params_region.template get<TimeInfectedCritical<FP>>()[entry.age_group]));
     154      109296 :             const int t_imm_interval_i = static_cast<int>(
     155      109296 :                 std::round(params_region.template get<TimeTemporaryImmunityPI<FP>>()[entry.age_group]));
     156             : 
     157             :             // transition probabilities
     158      109296 :             FP recoveredPerInfectedNoSymptoms =
     159      109296 :                 params_region.template get<RecoveredPerInfectedNoSymptoms<FP>>()[entry.age_group];
     160      109296 :             FP severePerInfectedSymptoms = params_region.template get<SeverePerInfectedSymptoms<FP>>()[entry.age_group];
     161      109296 :             FP criticalPerSevere         = params_region.template get<CriticalPerSevere<FP>>()[entry.age_group];
     162             : 
     163             :             // if we select a layer with better immunity (layer > 0), we need to adjust the times and transition rates
     164      109296 :             if (layer > 0) {
     165       69552 :                 t_InfectedNoSymptoms = static_cast<int>(std::round(
     166       69552 :                     t_InfectedNoSymptoms * params_region.template get<ReducTimeInfectedMild<FP>>()[entry.age_group]));
     167       69552 :                 t_InfectedSymptoms   = static_cast<int>(std::round(
     168       69552 :                     t_InfectedSymptoms * params_region.template get<ReducTimeInfectedMild<FP>>()[entry.age_group]));
     169             : 
     170       69552 :                 const FP red_fact_exp =
     171       69552 :                     layer == 1 ? params_region.template get<ReducExposedPartialImmunity<FP>>()[entry.age_group]
     172       34776 :                                : params_region.template get<ReducExposedImprovedImmunity<FP>>()[entry.age_group];
     173             : 
     174       69552 :                 const FP red_fact_inf =
     175             :                     layer == 1
     176       69552 :                         ? params_region.template get<ReducInfectedSymptomsPartialImmunity<FP>>()[entry.age_group]
     177       34776 :                         : params_region.template get<ReducInfectedSymptomsImprovedImmunity<FP>>()[entry.age_group];
     178             : 
     179       69552 :                 const FP red_fact_sev =
     180             :                     layer == 1
     181       69552 :                         ? params_region
     182       34776 :                               .template get<ReducInfectedSevereCriticalDeadPartialImmunity<FP>>()[entry.age_group]
     183             :                         : params_region
     184       34776 :                               .template get<ReducInfectedSevereCriticalDeadImprovedImmunity<FP>>()[entry.age_group];
     185             : 
     186       69552 :                 recoveredPerInfectedNoSymptoms = 1 - red_fact_inf / red_fact_exp * (1 - recoveredPerInfectedNoSymptoms);
     187       69552 :                 severePerInfectedSymptoms      = red_fact_sev / red_fact_inf * severePerInfectedSymptoms;
     188             :             }
     189             : 
     190      109296 :             if (entry.date == offset_date_by_days(date, 0)) {
     191        1188 :                 num_InfectedSymptoms[age] += scaling_factor_inf[age] * entry.num_confirmed;
     192        1188 :                 num_imm[age] += entry.num_confirmed;
     193             :             }
     194      109296 :             if (entry.date == offset_date_by_days(date, t_InfectedNoSymptoms + days_surplus)) {
     195        1188 :                 num_InfectedNoSymptoms[age] +=
     196        1188 :                     1 / (1 - recoveredPerInfectedNoSymptoms) * scaling_factor_inf[age] * entry.num_confirmed;
     197        1188 :                 num_Exposed[age] -=
     198        1188 :                     1 / (1 - recoveredPerInfectedNoSymptoms) * scaling_factor_inf[age] * entry.num_confirmed;
     199             :             }
     200      109296 :             if (entry.date == offset_date_by_days(date, days_surplus)) {
     201        1188 :                 num_InfectedNoSymptoms[age] -=
     202        1188 :                     1 / (1 - recoveredPerInfectedNoSymptoms) * scaling_factor_inf[age] * entry.num_confirmed;
     203             :             }
     204      109296 :             if (entry.date == offset_date_by_days(date, t_exposed + t_InfectedNoSymptoms + days_surplus)) {
     205        1188 :                 num_Exposed[age] +=
     206        1188 :                     1 / (1 - recoveredPerInfectedNoSymptoms) * scaling_factor_inf[age] * entry.num_confirmed;
     207             :             }
     208      109296 :             if (entry.date == offset_date_by_days(date, -t_InfectedSymptoms)) {
     209        1188 :                 num_InfectedSymptoms[age] -= scaling_factor_inf[age] * entry.num_confirmed;
     210        1188 :                 num_InfectedSevere[age] += severePerInfectedSymptoms * scaling_factor_inf[age] * entry.num_confirmed;
     211             :             }
     212      109296 :             if (entry.date == offset_date_by_days(date, -t_InfectedSymptoms - t_InfectedSevere)) {
     213        1188 :                 num_InfectedSevere[age] -= severePerInfectedSymptoms * scaling_factor_inf[age] * entry.num_confirmed;
     214        1188 :                 if (read_icu) {
     215          54 :                     num_icu[age] +=
     216          54 :                         severePerInfectedSymptoms * criticalPerSevere * scaling_factor_inf[age] * entry.num_confirmed;
     217             :                 }
     218             :             }
     219      109296 :             if (entry.date == offset_date_by_days(date, -t_InfectedSymptoms - t_InfectedSevere - t_InfectedCritical)) {
     220        1188 :                 num_death[age] += entry.num_deaths;
     221        1188 :                 if (read_icu) {
     222          54 :                     num_icu[age] -=
     223          54 :                         severePerInfectedSymptoms * criticalPerSevere * scaling_factor_inf[age] * entry.num_confirmed;
     224             :                 }
     225             :             }
     226      109296 :             if (entry.date == offset_date_by_days(date, 0 - t_imm_interval_i)) {
     227        1188 :                 num_imm[age] -= entry.num_confirmed;
     228             :             }
     229      109296 :         }
     230             :     }
     231             : 
     232         396 :     for (size_t region_idx = 0; region_idx < vregion.size(); ++region_idx) {
     233         198 :         auto region = vregion[region_idx];
     234             : 
     235         198 :         auto& num_InfectedNoSymptoms = vnum_InfectedNoSymptoms[region_idx];
     236         198 :         auto& num_InfectedSymptoms   = vnum_InfectedSymptoms[region_idx];
     237         198 :         auto& num_Exposed            = vnum_Exposed[region_idx];
     238         198 :         auto& num_InfectedSevere     = vnum_InfectedSevere[region_idx];
     239         198 :         auto& num_death              = vnum_death[region_idx];
     240         198 :         auto& num_icu                = vnum_icu[region_idx];
     241         198 :         auto& num_timm_i             = vnum_timm_i[region_idx];
     242             : 
     243         198 :         size_t num_groups = ConfirmedCasesDataEntry::age_group_names.size();
     244        1386 :         for (size_t i = 0; i < num_groups; i++) {
     245        9504 :             auto try_fix_constraints = [region, i](FP& value, FP error, auto str) {
     246        8316 :                 if (value < error) {
     247             :                     // this should probably return a failure
     248             :                     // but the algorithm is not robust enough to avoid large negative
     249             :                     // values and there are tests that rely on it
     250           0 :                     log_error("{:s} for age group {:s} is {:.4f} for region {:d}, "
     251             :                               "exceeds expected negative value.",
     252           0 :                               str, ConfirmedCasesDataEntry::age_group_names[i], value, region);
     253           0 :                     value = 0.0;
     254             :                 }
     255        8316 :                 else if (value < 0) {
     256           0 :                     log_info("{:s} for age group {:s} is {:.4f} for region {:d}, "
     257             :                              "automatically corrected",
     258           0 :                              str, ConfirmedCasesDataEntry::age_group_names[i], value, region);
     259           0 :                     value = 0.0;
     260             :                 }
     261             :             };
     262             : 
     263        1188 :             const FP tol_error = -1e-8;
     264        1188 :             try_fix_constraints(num_InfectedSymptoms[i], tol_error, "InfectedSymptoms");
     265        1188 :             try_fix_constraints(num_InfectedNoSymptoms[i], tol_error, "InfectedNoSymptoms");
     266        1188 :             try_fix_constraints(num_Exposed[i], tol_error, "Exposed");
     267        1188 :             try_fix_constraints(num_InfectedSevere[i], tol_error, "InfectedSevere");
     268        1188 :             try_fix_constraints(num_death[i], tol_error, "Dead");
     269        1188 :             try_fix_constraints(num_icu[i], tol_error, "InfectedCritical");
     270        1188 :             try_fix_constraints(num_timm_i[i], tol_error, "Recently Recovered or Vaccinated");
     271             :         }
     272             :     }
     273             : 
     274         396 :     return success();
     275             : }
     276             : 
     277             : /**
     278             :  * @brief Reads confirmed case data from a file and computes the distribution of cases across infection states.
     279             :  *
     280             :  * This function reads transformed RKI data from a specified file and processes the confirmed cases 
     281             :  * to distribute them across different infection states and age groups.
     282             :  *
     283             :  * @tparam Model The type of the model used.
     284             :  * @tparam FP Floating point type (default: double).
     285             :  *
     286             :  * @param[in] path Path to the file containing transformed case (rki) data.
     287             :  * @param[in] vregion Vector of region IDs to process.
     288             :  * @param[in] date Date for which the simulation starts.
     289             :  * @param[out] vnum_Exposed Output vector for the number of exposed individuals per age group and region.
     290             :  * @param[out] vnum_InfectedNoSymptoms Output vector for the number of infected individuals without symptoms.
     291             :  * @param[out] vnum_InfectedSymptoms Output vector for the number of infected individuals with symptoms.
     292             :  * @param[out] vnum_InfectedSevere Output vector for the number of severely infected individuals (Hospitalized).
     293             :  * @param[out] vnum_icu Output vector for the number of individuals in critical condition (ICU).
     294             :  * @param[out] vnum_death Output vector for the number of deaths.
     295             :  * @param[out] vnum_timm_i Output vector for the number of individuals in a temporary immunity state.
     296             :  * @param[in] model Vector of models, each representing a region and containing the parameters.
     297             :  * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases.
     298             :  * @param[in] layer Specifies the immunity layer: 0 (Naive), 1 (Partial Immunity), 2 (Improved Immunity).
     299             :  *
     300             :  * @return An IOResult indicating success or failure.
     301             :  */
     302             : template <class Model, typename FP = double>
     303          18 : IOResult<void> read_confirmed_cases_data(
     304             :     std::string const& path, std::vector<int> const& vregion, Date date, std::vector<std::vector<FP>>& vnum_Exposed,
     305             :     std::vector<std::vector<FP>>& vnum_InfectedNoSymptoms, std::vector<std::vector<FP>>& vnum_InfectedSymptoms,
     306             :     std::vector<std::vector<FP>>& vnum_InfectedSevere, std::vector<std::vector<FP>>& vnum_icu,
     307             :     std::vector<std::vector<FP>>& vnum_death, std::vector<std::vector<FP>>& vnum_timm_i,
     308             :     const std::vector<Model>& model, const std::vector<FP>& scaling_factor_inf, const size_t layer,
     309             :     bool read_icu = false)
     310             : {
     311          18 :     BOOST_OUTCOME_TRY(auto&& case_data, mio::read_confirmed_cases_data(path));
     312             :     return compute_confirmed_cases_data(case_data, vnum_Exposed, vnum_InfectedNoSymptoms, vnum_InfectedSymptoms,
     313             :                                         vnum_InfectedSevere, vnum_icu, vnum_death, vnum_timm_i, vregion, date, model,
     314          18 :                                         scaling_factor_inf, layer, read_icu);
     315          18 : }
     316             : 
     317             : /**
     318             :  * @brief Sets the confirmed cases data in the model considering different immunity layers.
     319             :  *
     320             :  * This function distributes confirmed case data across infection states for regions and age groups 
     321             :  * in the model. It considers different levels of immunity (naive, partial, and improved).
     322             :  *
     323             :  * @tparam Model The type of the model used.
     324             :  * @tparam FP Floating point type (default: double).
     325             :  *
     326             :  * @param[in,out] model Vector of models, each representing a region, where the compartments are updated.
     327             :  * @param[in] case_data Vector of confirmed case data entries.
     328             :  * @param[in] region Vector of region IDs for which the data is processed.
     329             :  * @param[in] date Date for which the confirmed cases are set in the model.
     330             :  * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases.
     331             :  * @param[in] immunity_population Vector containing the immunity distribution for naive, partial, and improved immunity layers.
     332             :  *
     333             :  * @return An IOResult indicating success or failure.
     334             :  */
     335             : template <class Model, typename FP = double>
     336             : IOResult<void>
     337          63 : set_confirmed_cases_data(std::vector<Model>& model, const std::vector<ConfirmedCasesDataEntry>& case_data,
     338             :                          std::vector<int> const& region, Date date, const std::vector<FP>& scaling_factor_inf,
     339             :                          const std::vector<std::vector<FP>> immunity_population)
     340             : {
     341          63 :     auto num_age_groups = (size_t)model[0].parameters.get_num_groups();
     342          63 :     assert(scaling_factor_inf.size() == num_age_groups); //TODO: allow vector or scalar valued scaling factors
     343          63 :     assert(ConfirmedCasesDataEntry::age_group_names.size() == num_age_groups);
     344             : 
     345         126 :     std::vector<std::vector<FP>> num_InfectedSymptoms(model.size());
     346         126 :     std::vector<std::vector<FP>> num_death(model.size());
     347         126 :     std::vector<std::vector<FP>> num_Exposed(model.size());
     348         126 :     std::vector<std::vector<FP>> num_InfectedNoSymptoms(model.size());
     349         126 :     std::vector<std::vector<FP>> num_InfectedSevere(model.size());
     350         126 :     std::vector<std::vector<FP>> num_icu(model.size());
     351         126 :     std::vector<std::vector<FP>> num_timm1(model.size());
     352         126 :     std::vector<std::vector<FP>> num_timm2(model.size());
     353             : 
     354         126 :     std::vector<FP> denom_E(num_age_groups, 0.0);
     355         126 :     std::vector<FP> denom_I_NS(num_age_groups, 0.0);
     356         126 :     std::vector<FP> denom_I_Sy(num_age_groups, 0.0);
     357         126 :     std::vector<FP> denom_I_Sev(num_age_groups, 0.0);
     358             : 
     359             :     /*----------- Naive immunity -----------*/
     360         126 :     for (size_t county = 0; county < model.size(); county++) {
     361          63 :         num_InfectedSymptoms[county]   = std::vector<FP>(num_age_groups, 0.0);
     362          63 :         num_death[county]              = std::vector<FP>(num_age_groups, 0.0);
     363          63 :         num_Exposed[county]            = std::vector<FP>(num_age_groups, 0.0);
     364          63 :         num_InfectedNoSymptoms[county] = std::vector<FP>(num_age_groups, 0.0);
     365          63 :         num_InfectedSevere[county]     = std::vector<FP>(num_age_groups, 0.0);
     366          63 :         num_icu[county]                = std::vector<FP>(num_age_groups, 0.0);
     367          63 :         num_timm1[county]              = std::vector<FP>(num_age_groups, 0.0);
     368          63 :         num_timm2[county]              = std::vector<FP>(num_age_groups, 0.0);
     369         441 :         for (size_t group = 0; group < num_age_groups; group++) {
     370             :             // calculate the denominators to split the reported case numbers to the different immunity layers.
     371         378 :             denom_E[group] =
     372         378 :                 1 / (immunity_population[0][group] +
     373         756 :                      immunity_population[1][group] *
     374         756 :                          model[county].parameters.template get<ReducExposedPartialImmunity<FP>>()[(AgeGroup)group] +
     375         756 :                      immunity_population[2][group] *
     376         756 :                          model[county].parameters.template get<ReducExposedImprovedImmunity<FP>>()[(AgeGroup)group]);
     377             : 
     378         378 :             denom_I_NS[group] =
     379         378 :                 1 / (immunity_population[0][group] +
     380         756 :                      immunity_population[1][group] *
     381         756 :                          model[county].parameters.template get<ReducExposedPartialImmunity<FP>>()[(AgeGroup)group] +
     382         756 :                      immunity_population[2][group] *
     383         756 :                          model[county].parameters.template get<ReducExposedImprovedImmunity<FP>>()[(AgeGroup)group]);
     384             : 
     385         378 :             denom_I_Sy[group] =
     386         378 :                 1 / (immunity_population[0][group] +
     387         756 :                      immunity_population[1][group] *
     388         378 :                          model[county]
     389         756 :                              .parameters.template get<ReducInfectedSymptomsPartialImmunity<FP>>()[(AgeGroup)group] +
     390         756 :                      immunity_population[2][group] *
     391         378 :                          model[county]
     392         756 :                              .parameters.template get<ReducInfectedSymptomsImprovedImmunity<FP>>()[(AgeGroup)group]);
     393             : 
     394         378 :             denom_I_Sev[group] =
     395         378 :                 1 / (immunity_population[0][group] +
     396         756 :                      immunity_population[1][group] *
     397         756 :                          model[county].parameters.template get<ReducInfectedSevereCriticalDeadPartialImmunity<FP>>()[(
     398         378 :                              AgeGroup)group] +
     399         756 :                      immunity_population[2][group] *
     400         756 :                          model[county].parameters.template get<ReducInfectedSevereCriticalDeadImprovedImmunity<FP>>()[(
     401             :                              AgeGroup)group]);
     402             :         }
     403             :     }
     404             : 
     405          63 :     BOOST_OUTCOME_TRY(compute_confirmed_cases_data(case_data, num_Exposed, num_InfectedNoSymptoms, num_InfectedSymptoms,
     406             :                                                    num_InfectedSevere, num_icu, num_death, num_timm1, region, date,
     407             :                                                    model, scaling_factor_inf, 0));
     408             : 
     409         126 :     for (size_t county = 0; county < model.size(); county++) {
     410          63 :         size_t num_groups = (size_t)model[county].parameters.get_num_groups();
     411         441 :         for (size_t i = 0; i < num_groups; i++) {
     412         378 :             model[county].populations[{AgeGroup(i), InfectionState::ExposedNaive}] =
     413         378 :                 immunity_population[0][i] * denom_E[i] * num_Exposed[county][i];
     414         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedNoSymptomsNaive}] =
     415         378 :                 immunity_population[0][i] * denom_I_NS[i] * num_InfectedNoSymptoms[county][i];
     416         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedNoSymptomsNaiveConfirmed}] = 0;
     417         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSymptomsNaive}] =
     418         378 :                 immunity_population[0][i] * denom_I_Sy[i] * num_InfectedSymptoms[county][i];
     419         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSymptomsNaiveConfirmed}] = 0;
     420         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSevereNaive}] =
     421         378 :                 immunity_population[0][i] * denom_I_Sev[i] * num_InfectedSevere[county][i];
     422             :         }
     423          63 :         if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) {
     424           0 :             log_warning("No infections for unvaccinated reported on date " + std::to_string(date.year) + "-" +
     425             :                         std::to_string(date.month) + "-" + std::to_string(date.day) + " for region " +
     426           0 :                         std::to_string(region[county]) + ". Population data has not been set.");
     427             :         }
     428             :     }
     429             : 
     430             :     /*----------- PARTIAL Immunity -----------*/
     431         126 :     for (size_t county = 0; county < model.size(); county++) {
     432          63 :         num_InfectedSymptoms[county]   = std::vector<FP>(num_age_groups, 0.0);
     433          63 :         num_death[county]              = std::vector<FP>(num_age_groups, 0.0);
     434          63 :         num_Exposed[county]            = std::vector<FP>(num_age_groups, 0.0);
     435          63 :         num_InfectedNoSymptoms[county] = std::vector<FP>(num_age_groups, 0.0);
     436          63 :         num_InfectedSevere[county]     = std::vector<FP>(num_age_groups, 0.0);
     437          63 :         num_icu[county]                = std::vector<FP>(num_age_groups, 0.0);
     438             :     }
     439             : 
     440          63 :     BOOST_OUTCOME_TRY(compute_confirmed_cases_data(case_data, num_Exposed, num_InfectedNoSymptoms, num_InfectedSymptoms,
     441             :                                                    num_InfectedSevere, num_icu, num_death, num_timm1, region, date,
     442             :                                                    model, scaling_factor_inf, 1));
     443             : 
     444         126 :     for (size_t county = 0; county < model.size(); county++) {
     445          63 :         size_t num_groups = (size_t)model[county].parameters.get_num_groups();
     446         441 :         for (size_t i = 0; i < num_groups; i++) {
     447         378 :             model[county].populations[{AgeGroup(i), InfectionState::ExposedPartialImmunity}] =
     448         756 :                 immunity_population[1][i] *
     449        1134 :                 model[county].parameters.template get<ReducExposedPartialImmunity<FP>>()[(AgeGroup)i] * denom_E[i] *
     450         378 :                 num_Exposed[county][i];
     451         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedNoSymptomsPartialImmunity}] =
     452         756 :                 immunity_population[1][i] *
     453        1134 :                 model[county].parameters.template get<ReducExposedPartialImmunity<FP>>()[(AgeGroup)i] * denom_I_NS[i] *
     454         378 :                 num_InfectedNoSymptoms[county][i];
     455         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedNoSymptomsPartialImmunityConfirmed}] = 0;
     456         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSymptomsPartialImmunity}] =
     457         756 :                 immunity_population[1][i] *
     458        1134 :                 model[county].parameters.template get<ReducInfectedSymptomsPartialImmunity<FP>>()[(AgeGroup)i] *
     459         756 :                 denom_I_Sy[i] * num_InfectedSymptoms[county][i];
     460         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSymptomsPartialImmunityConfirmed}] = 0;
     461         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSeverePartialImmunity}] =
     462         756 :                 immunity_population[1][i] *
     463         378 :                 model[county]
     464        1134 :                     .parameters.template get<ReducInfectedSevereCriticalDeadPartialImmunity<FP>>()[(AgeGroup)i] *
     465         756 :                 denom_I_Sev[i] * num_InfectedSevere[county][i];
     466             :             // the += is necessary because we already set the previous vaccinated individuals
     467         378 :             model[county].populations[{AgeGroup(i), InfectionState::TemporaryImmunePartialImmunity}] +=
     468         756 :                 immunity_population[1][i] *
     469        1512 :                 model[county].parameters.template get<ReducExposedPartialImmunity<FP>>()[(AgeGroup)i] * denom_E[i] *
     470         378 :                 num_timm1[county][i];
     471             :         }
     472          63 :         if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) {
     473           0 :             log_warning("No infections for partially vaccinated reported on date " + std::to_string(date.year) + "-" +
     474             :                         std::to_string(date.month) + "-" + std::to_string(date.day) + " for region " +
     475           0 :                         std::to_string(region[county]) + ". Population data has not been set.");
     476             :         }
     477             :     }
     478             : 
     479             :     /*----------- Improved Immunity -----------*/
     480         126 :     for (size_t county = 0; county < model.size(); county++) {
     481          63 :         num_InfectedSymptoms[county]   = std::vector<FP>(num_age_groups, 0.0);
     482          63 :         num_death[county]              = std::vector<FP>(num_age_groups, 0.0);
     483          63 :         num_Exposed[county]            = std::vector<FP>(num_age_groups, 0.0);
     484          63 :         num_InfectedNoSymptoms[county] = std::vector<FP>(num_age_groups, 0.0);
     485          63 :         num_InfectedSevere[county]     = std::vector<FP>(num_age_groups, 0.0);
     486          63 :         num_icu[county]                = std::vector<FP>(num_age_groups, 0.0);
     487             :     }
     488             : 
     489          63 :     BOOST_OUTCOME_TRY(compute_confirmed_cases_data(case_data, num_Exposed, num_InfectedNoSymptoms, num_InfectedSymptoms,
     490             :                                                    num_InfectedSevere, num_icu, num_death, num_timm2, region, date,
     491             :                                                    model, scaling_factor_inf, 2));
     492             : 
     493         126 :     for (size_t county = 0; county < model.size(); county++) {
     494          63 :         size_t num_groups = (size_t)model[county].parameters.get_num_groups();
     495         441 :         for (size_t i = 0; i < num_groups; i++) {
     496         378 :             model[county].populations[{AgeGroup(i), InfectionState::ExposedImprovedImmunity}] =
     497         756 :                 immunity_population[2][i] *
     498        1134 :                 model[county].parameters.template get<ReducExposedImprovedImmunity<FP>>()[(AgeGroup)i] * denom_E[i] *
     499         378 :                 num_Exposed[county][i];
     500         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedNoSymptomsImprovedImmunity}] =
     501         756 :                 immunity_population[2][i] *
     502        1134 :                 model[county].parameters.template get<ReducExposedImprovedImmunity<FP>>()[(AgeGroup)i] * denom_I_NS[i] *
     503         378 :                 num_InfectedNoSymptoms[county][i];
     504         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed}] = 0;
     505         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSymptomsImprovedImmunity}] =
     506         756 :                 immunity_population[2][i] *
     507        1134 :                 model[county].parameters.template get<ReducInfectedSymptomsImprovedImmunity<FP>>()[(AgeGroup)i] *
     508         756 :                 denom_I_Sy[i] * num_InfectedSymptoms[county][i];
     509         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSymptomsImprovedImmunityConfirmed}] = 0;
     510         378 :             model[county].populations[{AgeGroup(i), InfectionState::InfectedSevereImprovedImmunity}] =
     511         756 :                 immunity_population[2][i] *
     512         378 :                 model[county]
     513        1134 :                     .parameters.template get<ReducInfectedSevereCriticalDeadImprovedImmunity<FP>>()[(AgeGroup)i] *
     514         756 :                 denom_I_Sev[i] * num_InfectedSevere[county][i];
     515             :             // the += is necessary because we already set the previous vaccinated individuals
     516         378 :             model[county].populations[{AgeGroup(i), InfectionState::TemporaryImmuneImprovedImmunity}] +=
     517         756 :                 immunity_population[2][i] *
     518        1512 :                 model[county].parameters.template get<ReducExposedImprovedImmunity<FP>>()[(AgeGroup)i] * denom_E[i] *
     519         378 :                 num_timm2[county][i];
     520             :         }
     521          63 :         if (std::accumulate(num_InfectedSymptoms[county].begin(), num_InfectedSymptoms[county].end(), 0.0) == 0) {
     522           0 :             log_warning("No infections for vaccinated reported on date " + std::to_string(date.year) + "-" +
     523             :                         std::to_string(date.month) + "-" + std::to_string(date.day) + " for region " +
     524           0 :                         std::to_string(region[county]) + ". Population data has not been set.");
     525             :         }
     526             :     }
     527         126 :     return success();
     528          63 : }
     529             : 
     530             : /**
     531             :  * @brief Reads confirmed case data from a file and sets it in the model.
     532             :  *
     533             :  * This function reads transformed RKI data from the specified file and distributes the confirmed case data 
     534             :  * across different infection states for regions and age groups in the model. It considers naive, partial, 
     535             :  * and improved immunity layers.
     536             :  *
     537             :  * @tparam Model The type of the model used.
     538             :  * @tparam FP Floating point type (default: double).
     539             :  *
     540             :  * @param[in,out] model Vector of models, each representing a region, where the compartments are updated.
     541             :  * @param[in] path Path to the file containing case (RKI) data.
     542             :  * @param[in] region Vector of region IDs for which the data is processed.
     543             :  * @param[in] date Date for which the confirmed cases are set in the model.
     544             :  * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases.
     545             :  * @param[in] immunity_population Vector containing the immunity distribution for naive, partial, and improved immunity layers.
     546             :  *
     547             :  * @return An IOResult indicating success or failure.
     548             :  */
     549             : template <class Model, typename FP = double>
     550          36 : IOResult<void> set_confirmed_cases_data(std::vector<Model>& model, const std::string& path,
     551             :                                         std::vector<int> const& region, Date date,
     552             :                                         const std::vector<FP>& scaling_factor_inf,
     553             :                                         const std::vector<std::vector<FP>> immunity_population)
     554             : {
     555          36 :     BOOST_OUTCOME_TRY(auto&& case_data, mio::read_confirmed_cases_data(path));
     556          72 :     BOOST_OUTCOME_TRY(
     557             :         set_confirmed_cases_data(model, case_data, region, date, scaling_factor_inf, immunity_population));
     558          72 :     return success();
     559          36 : }
     560             : 
     561             : /**
     562             :  * @brief Extracts the number of individuals in critical condition (ICU) for each region 
     563             :  * on a specified date from the provided DIVI data.
     564             :  *
     565             :  * @tparam FP Floating point type (default: double).
     566             :  *
     567             :  * @param[in] divi_data Vector of DIVI data entries containing date, region, and ICU information.
     568             :  * @param[in] vregion Vector of region IDs for which the data is computed.
     569             :  * @param[in] date Date for which the ICU data is computed.
     570             :  * @param[out] vnum_icu Output vector containing the number of ICU cases for each region.
     571             :  *
     572             :  * @return An IOResult indicating success or failure.
     573             :  */
     574             : template <typename FP = double>
     575          63 : IOResult<void> compute_divi_data(const std::vector<DiviEntry>& divi_data, const std::vector<int>& vregion, Date date,
     576             :                                  std::vector<FP>& vnum_icu)
     577             : {
     578          63 :     auto max_date_entry = std::max_element(divi_data.begin(), divi_data.end(), [](auto&& a, auto&& b) {
     579        5733 :         return a.date < b.date;
     580             :     });
     581          63 :     if (max_date_entry == divi_data.end()) {
     582           0 :         log_error("DIVI data is empty.");
     583           0 :         return failure(StatusCode::InvalidValue, "DIVI data is empty.");
     584             :     }
     585          63 :     auto max_date = max_date_entry->date;
     586          63 :     if (max_date < date) {
     587           0 :         log_error("DIVI data does not contain the specified date.");
     588           0 :         return failure(StatusCode::OutOfRange, "DIVI data does not contain the specified date.");
     589             :     }
     590             : 
     591        5859 :     for (auto&& entry : divi_data) {
     592        8280 :         auto it      = std::find_if(vregion.begin(), vregion.end(), [&entry](auto r) {
     593        8280 :             return r == 0 || r == get_region_id(entry);
     594             :         });
     595        5796 :         auto date_df = entry.date;
     596        5796 :         if (it != vregion.end() && date_df == date) {
     597          63 :             auto region_idx      = size_t(it - vregion.begin());
     598          63 :             vnum_icu[region_idx] = entry.num_icu;
     599             :         }
     600             :     }
     601             : 
     602         126 :     return success();
     603             : }
     604             : 
     605             : /**
     606             :  * @brief Reads DIVI data from a file and computes the ICU data for specified regions and date.
     607             :  *
     608             :  * @tparam FP Floating point type (default: double).
     609             :  *
     610             :  * @param[in] path Path to the file containing DIVI data.
     611             :  * @param[in] vregion Vector of region IDs for which the data is computed.
     612             :  * @param[in] date Date for which the ICU data is computed.
     613             :  * @param[out] vnum_icu Output vector containing the number of ICU cases for each region.
     614             :  *
     615             :  * @return An IOResult indicating success or failure.
     616             :  */
     617             : template <typename FP = double>
     618          63 : IOResult<void> read_divi_data(const std::string& path, const std::vector<int>& vregion, Date date,
     619             :                               std::vector<FP>& vnum_icu)
     620             : {
     621          63 :     BOOST_OUTCOME_TRY(auto&& divi_data, mio::read_divi_data(path));
     622          63 :     return compute_divi_data(divi_data, vregion, date, vnum_icu);
     623          63 : }
     624             : 
     625             : /**
     626             :  * @brief Sets ICU data from DIVI data into the a vector of models, distributed across age groups.
     627             :  *
     628             :  * This function reads DIVI data from a file, computes the number of individuals in critical condition (ICU) 
     629             :  * for each region, and sets these values in the model. The ICU cases are distributed across age groups 
     630             :  * using the transition probabilities from severe to critical.
     631             :  *
     632             :  * @tparam Model The type of the model used.
     633             :  * @tparam FP Floating point type (default: double).
     634             :  *
     635             :  * @param[in,out] model Vector of models, each representing a region, where the ICU population is updated.
     636             :  * @param[in] path Path to the file containing DIVI data.
     637             :  * @param[in] vregion Vector of region IDs for which the data is computed.
     638             :  * @param[in] date Date for which the ICU data is computed.
     639             :  * @param[in] scaling_factor_icu Scaling factor for reported ICU cases.
     640             :  *
     641             :  * @return An IOResult indicating success or failure.
     642             :  */
     643             : template <class Model, typename FP = double>
     644          63 : IOResult<void> set_divi_data(std::vector<Model>& model, const std::string& path, const std::vector<int>& vregion,
     645             :                              Date date, FP scaling_factor_icu)
     646             : {
     647         126 :     std::vector<FP> sum_mu_I_U(vregion.size(), 0);
     648         126 :     std::vector<std::vector<FP>> mu_I_U{model.size()};
     649         126 :     for (size_t region = 0; region < vregion.size(); region++) {
     650          63 :         auto num_groups = model[region].parameters.get_num_groups();
     651         441 :         for (auto i = AgeGroup(0); i < num_groups; i++) {
     652         756 :             sum_mu_I_U[region] += model[region].parameters.template get<CriticalPerSevere<FP>>()[i] *
     653         378 :                                   model[region].parameters.template get<SeverePerInfectedSymptoms<FP>>()[i];
     654         756 :             mu_I_U[region].push_back(model[region].parameters.template get<CriticalPerSevere<FP>>()[i] *
     655         378 :                                      model[region].parameters.template get<SeverePerInfectedSymptoms<FP>>()[i]);
     656             :         }
     657             :     }
     658         126 :     std::vector<FP> num_icu(model.size(), 0.0);
     659          63 :     BOOST_OUTCOME_TRY(read_divi_data(path, vregion, date, num_icu));
     660             : 
     661         126 :     for (size_t region = 0; region < vregion.size(); region++) {
     662          63 :         auto num_groups = model[region].parameters.get_num_groups();
     663         441 :         for (auto i = AgeGroup(0); i < num_groups; i++) {
     664         378 :             model[region].populations[{i, InfectionState::InfectedCriticalNaive}] =
     665         378 :                 scaling_factor_icu * num_icu[region] * mu_I_U[region][(size_t)i] / sum_mu_I_U[region];
     666             :         }
     667             :     }
     668             : 
     669         126 :     return success();
     670          63 : }
     671             : 
     672             : /**
     673             :  * @brief Reads population data from census data.
     674             :  * 
     675             :  * @param[in] path Path to the population data file.
     676             :  * @param[in] vregion Vector of keys representing the regions of interest.
     677             :  * @return An IOResult containing a vector of vectors, where each inner vector represents the population
     678             :  *         distribution across age groups for a specific region, or an error if the function fails.
     679             :  * @see mio::read_population_data
     680             :  */
     681             : IOResult<std::vector<std::vector<double>>> read_population_data(const std::string& path,
     682             :                                                                 const std::vector<int>& vregion);
     683             : 
     684             : /**
     685             :  * @brief Reads population data from a vector of population data entries.
     686             :  * 
     687             :  * @param[in] population_data Vector of population data entries.
     688             :  * @param[in] vregion Vector of keys representing the regions of interest.
     689             :  * @return An IOResult containing a vector of vectors, where each inner vector represents the population
     690             :  *         distribution across age groups for a specific region, or an error if the function fails.
     691             :  * @see mio::read_population_data
     692             :  */
     693             : IOResult<std::vector<std::vector<double>>> read_population_data(const std::vector<PopulationDataEntry>& population_data,
     694             :                                                                 const std::vector<int>& vregion);
     695             : 
     696             : /**
     697             :  * @brief Sets the population data for the given models based on the provided population distribution and immunity levels.
     698             :  *
     699             :  * @tparam Model The type of the model used.
     700             :  * @tparam FP Floating point type (default: double).
     701             :  * 
     702             :  * @param[in,out] model A vector of models for which population data will be set.
     703             :  * @param[in] num_population A 2D vector where each row represents the age group population distribution for a specific region.
     704             :  * @param[in] vregion A vector of region identifiers corresponding to the population data.
     705             :  * @param[in] immunity_population A 2D vector where each row represents the immunity distribution for a specific region 
     706             :  *                                 across different levels of immunity (e.g., naive, partial, improved immunity).
     707             :  * 
     708             :  * @return An IOResult indicating success or failure.
     709             :  */
     710             : template <class Model, typename FP = double>
     711          63 : IOResult<void> set_population_data(std::vector<Model>& model, const std::vector<std::vector<FP>>& num_population,
     712             :                                    const std::vector<int>& vregion,
     713             :                                    const std::vector<std::vector<FP>> immunity_population)
     714             : {
     715         126 :     for (size_t region = 0; region < vregion.size(); region++) {
     716          63 :         if (std::accumulate(num_population[region].begin(), num_population[region].end(), 0.0) > 0) {
     717          63 :             auto num_groups = model[region].parameters.get_num_groups();
     718         441 :             for (auto i = AgeGroup(0); i < num_groups; i++) {
     719             : 
     720         378 :                 FP SN  = num_population[region][size_t(i)] * immunity_population[0][size_t(i)];
     721         378 :                 FP SPI = num_population[region][size_t(i)] * immunity_population[1][size_t(i)];
     722         378 :                 FP SII = num_population[region][size_t(i)] - SN - SPI;
     723             : 
     724         378 :                 model[region].populations[{i, InfectionState::SusceptibleImprovedImmunity}] = std::max(
     725         756 :                     0.0,
     726         756 :                     FP(SII -
     727        1134 :                        (model[region].populations[{i, InfectionState::ExposedImprovedImmunity}] +
     728        1134 :                         model[region].populations[{i, InfectionState::InfectedNoSymptomsImprovedImmunity}] +
     729        1134 :                         model[region].populations[{i, InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed}] +
     730        1134 :                         model[region].populations[{i, InfectionState::InfectedSymptomsImprovedImmunity}] +
     731        1134 :                         model[region].populations[{i, InfectionState::InfectedSymptomsImprovedImmunityConfirmed}] +
     732        1134 :                         model[region].populations[{i, InfectionState::InfectedSevereImprovedImmunity}] +
     733        1134 :                         model[region].populations[{i, InfectionState::InfectedCriticalImprovedImmunity}] +
     734        1134 :                         model[region].populations[{i, InfectionState::DeadImprovedImmunity}] +
     735         756 :                         model[region].populations[{i, InfectionState::TemporaryImmuneImprovedImmunity}])));
     736             : 
     737         378 :                 model[region].populations[{i, InfectionState::SusceptiblePartialImmunity}] = std::max(
     738         756 :                     0.0,
     739        1134 :                     SPI - model[region].populations[{i, InfectionState::ExposedPartialImmunity}] -
     740        1134 :                         model[region].populations[{i, InfectionState::InfectedNoSymptomsPartialImmunity}] -
     741        1134 :                         model[region].populations[{i, InfectionState::InfectedNoSymptomsPartialImmunityConfirmed}] -
     742        1134 :                         model[region].populations[{i, InfectionState::InfectedSymptomsPartialImmunity}] -
     743        1134 :                         model[region].populations[{i, InfectionState::InfectedSymptomsPartialImmunityConfirmed}] -
     744        1134 :                         model[region].populations[{i, InfectionState::InfectedSeverePartialImmunity}] -
     745        1134 :                         model[region].populations[{i, InfectionState::InfectedCriticalPartialImmunity}] -
     746        1890 :                         model[region].populations[{i, InfectionState::DeadPartialImmunity}] -
     747         756 :                         model[region].populations[{i, InfectionState::TemporaryImmunePartialImmunity}]);
     748             : 
     749         756 :                 model[region].populations.template set_difference_from_group_total<AgeGroup>(
     750         378 :                     {i, InfectionState::SusceptibleNaive}, num_population[region][size_t(i)]);
     751             :             }
     752             : 
     753         441 :             for (auto i = AgeGroup(0); i < AgeGroup(6); i++) {
     754       11340 :                 for (auto j = Index<InfectionState>(0); j < InfectionState::Count; ++j) {
     755       10962 :                     if (model[region].populations[{i, j}] < 0) {
     756           0 :                         log_warning("Compartment at age group {}, infection state {}, is negative: {}", size_t(i),
     757           0 :                                     size_t(j), model[region].populations[{i, j}]);
     758             :                     }
     759             :                 }
     760             :             }
     761             :         }
     762             :         else {
     763           0 :             log_warning("No population data available for region " + std::to_string(region) +
     764             :                         ". Population data has not been set.");
     765             :         }
     766             :     }
     767             : 
     768         126 :     return success();
     769             : }
     770             : 
     771             : /**
     772             :  * @brief Reads population data from a file and sets it for the each given model.
     773             :  *
     774             :  * @tparam Model The type of the model used.
     775             :  * 
     776             :  * @param[in,out] model A vector of models for which population data will be set.
     777             :  * @param[in] path The file path to the population data.
     778             :  * @param[in] vregion A vector of region identifiers corresponding to the population data.
     779             :  * @param[in] immunity_population A 2D vector where each row represents the immunity distribution for a specific region 
     780             :  *                                 across different levels of immunity (e.g., naive, partial, improved).
     781             :  * 
     782             :  * @return An IOResult indicating success or failure.
     783             :  */
     784             : template <class Model>
     785          36 : IOResult<void> set_population_data(std::vector<Model>& model, const std::string& path, const std::vector<int>& vregion,
     786             :                                    const std::vector<std::vector<double>> immunity_population)
     787             : {
     788          36 :     BOOST_OUTCOME_TRY(auto&& num_population, details::read_population_data(path, vregion));
     789          72 :     BOOST_OUTCOME_TRY(set_population_data(model, num_population, vregion, immunity_population));
     790          72 :     return success();
     791          36 : }
     792             : 
     793             : /**
     794             :  * @brief Sets vaccination data for the given models using provided vaccination (partial, full, and booster) data.
     795             :  *
     796             :  *
     797             :  * @tparam FP Floating point type (default: double).
     798             :  *
     799             :  * @param[in,out] model A vector of models for which vaccination data will be set.
     800             :  * @param[in] vacc_data A vector of VaccinationDataEntry objects containing the vaccination data.
     801             :  * @param[in] date The starting date for the simulation.
     802             :  * @param[in] vregion A vector of region identifiers corresponding to the vaccination data.
     803             :  * @param[in] num_days The number of days for which the simulation runs.
     804             :  *
     805             :  * @return An IOResult indicating success or failure.
     806             :  */
     807             : template <typename FP = double>
     808          64 : IOResult<void> set_vaccination_data(std::vector<Model<FP>>& model, const std::vector<VaccinationDataEntry>& vacc_data,
     809             :                                     Date date, const std::vector<int>& vregion, int num_days)
     810             : {
     811          64 :     auto num_groups = model[0].parameters.get_num_groups();
     812             : 
     813          64 :     auto days_until_effective_n =
     814          64 :         (int)(double)model[0].parameters.template get<DaysUntilEffectivePartialVaccination<FP>>()[AgeGroup(0)];
     815          64 :     auto days_until_effective_pi =
     816          64 :         (int)(double)model[0].parameters.template get<DaysUntilEffectiveImprovedVaccination<FP>>()[AgeGroup(0)];
     817          64 :     auto days_until_effective_ii =
     818          64 :         (int)(double)model[0].parameters.template get<DaysUntilEffectiveBoosterImmunity<FP>>()[AgeGroup(0)];
     819             :     // iterate over regions (e.g., counties)
     820         128 :     for (size_t i = 0; i < model.size(); ++i) {
     821             :         // iterate over age groups in region
     822         443 :         for (auto g = AgeGroup(0); g < num_groups; ++g) {
     823             : 
     824         379 :             model[i].parameters.template get<DailyPartialVaccinations<FP>>().resize(SimulationDay(num_days + 1));
     825         379 :             model[i].parameters.template get<DailyFullVaccinations<FP>>().resize(SimulationDay(num_days + 1));
     826         379 :             model[i].parameters.template get<DailyBoosterVaccinations<FP>>().resize(SimulationDay(num_days + 1));
     827        2819 :             for (auto d = SimulationDay(0); d < SimulationDay(num_days + 1); ++d) {
     828        2440 :                 model[i].parameters.template get<DailyPartialVaccinations<FP>>()[{g, d}] = 0.0;
     829        2440 :                 model[i].parameters.template get<DailyFullVaccinations<FP>>()[{g, d}]    = 0.0;
     830        2440 :                 model[i].parameters.template get<DailyBoosterVaccinations<FP>>()[{g, d}] = 0.0;
     831             :             }
     832             :         }
     833             :     }
     834             : 
     835          64 :     auto max_date_entry = std::max_element(vacc_data.begin(), vacc_data.end(), [](auto&& a, auto&& b) {
     836       32815 :         return a.date < b.date;
     837             :     });
     838          64 :     if (max_date_entry == vacc_data.end()) {
     839           0 :         return failure(StatusCode::InvalidFileFormat, "Vaccination data file is empty.");
     840             :     }
     841          64 :     auto max_date = max_date_entry->date;
     842          64 :     if (max_date < offset_date_by_days(date, num_days)) {
     843           1 :         log_error("Vaccination data does not contain all dates. After the last day the data, vaccinations per day are "
     844             :                   "set to 0.");
     845             :     }
     846             : 
     847       32943 :     for (auto&& vacc_data_entry : vacc_data) {
     848      104779 :         auto it      = std::find_if(vregion.begin(), vregion.end(), [&vacc_data_entry](auto&& r) {
     849      100310 :             return r == 0 || (vacc_data_entry.county_id && vacc_data_entry.county_id == regions::CountyId(r)) ||
     850       92715 :                    (vacc_data_entry.state_id && vacc_data_entry.state_id == regions::StateId(r)) ||
     851       88213 :                    (vacc_data_entry.district_id && vacc_data_entry.district_id == regions::DistrictId(r));
     852             :         });
     853       32879 :         auto date_df = vacc_data_entry.date;
     854       32879 :         if (it != vregion.end()) {
     855       32879 :             auto region_idx = size_t(it - vregion.begin());
     856       32879 :             AgeGroup age    = vacc_data_entry.age_group;
     857             : 
     858             :             // get daily vaccinations for each layer
     859      239377 :             for (size_t d = 0; d < (size_t)num_days + 1; ++d) {
     860      206498 :                 auto offset_first_date = offset_date_by_days(date, (int)d - days_until_effective_n);
     861      206498 :                 if (max_date >= offset_first_date) {
     862      206487 :                     if (date_df == offset_first_date) {
     863        2250 :                         model[region_idx]
     864        2250 :                             .parameters.template get<DailyPartialVaccinations<FP>>()[{age, SimulationDay(d)}] =
     865        2250 :                             vacc_data_entry.num_vaccinations_partial;
     866             :                     }
     867             :                 }
     868             :                 else {
     869          11 :                     if (date_df == offset_first_date) {
     870           0 :                         model[region_idx]
     871           0 :                             .parameters.template get<DailyPartialVaccinations<FP>>()[{age, SimulationDay(d)}] = 0;
     872             :                     }
     873             :                 }
     874             : 
     875      206498 :                 auto offset_full_date = offset_date_by_days(date, (int)d - days_until_effective_pi);
     876      206498 :                 if (max_date >= offset_full_date) {
     877      206498 :                     if (date_df == offset_full_date) {
     878        2440 :                         model[region_idx]
     879        2440 :                             .parameters.template get<DailyFullVaccinations<FP>>()[{age, SimulationDay(d)}] =
     880        2440 :                             vacc_data_entry.num_vaccinations_completed;
     881             :                     }
     882             :                 }
     883             :                 else {
     884           0 :                     if (date_df == offset_first_date) {
     885           0 :                         model[region_idx]
     886           0 :                             .parameters.template get<DailyFullVaccinations<FP>>()[{age, SimulationDay(d)}] = 0;
     887             :                     }
     888             :                 }
     889             : 
     890      206498 :                 auto offset_booster_date = offset_date_by_days(date, (int)d - days_until_effective_ii);
     891      206498 :                 if (max_date >= offset_booster_date) {
     892      206487 :                     if (date_df == offset_booster_date) {
     893        2439 :                         model[region_idx]
     894        2439 :                             .parameters.template get<DailyBoosterVaccinations<FP>>()[{age, SimulationDay(d)}] =
     895        2439 :                             vacc_data_entry.num_vaccinations_refreshed_first +
     896        2439 :                             vacc_data_entry.num_vaccinations_refreshed_additional;
     897             :                     }
     898             :                 }
     899             :                 else {
     900          11 :                     if (date_df == offset_first_date) {
     901           0 :                         model[region_idx]
     902           0 :                             .parameters.template get<DailyBoosterVaccinations<FP>>()[{age, SimulationDay(d)}] = 0;
     903             :                     }
     904             :                 }
     905             :             }
     906             :         }
     907             :     }
     908         128 :     return success();
     909             : }
     910             : 
     911             : /**
     912             :  * @brief Sets vaccination data for the given models using vaccination data from a file.
     913             :  *
     914             :  * This function reads vaccination data from a specified file, and assigns daily vaccination numbers
     915             :  * (partial, full, and booster) to each region and age group in the models.
     916             :  *
     917             :  * @tparam FP Floating point type (default: double).
     918             :  *
     919             :  * @param[in,out] model A vector of models for which vaccination data will be set.
     920             :  * @param[in] path The file path to the vaccination data.
     921             :  * @param[in] date The starting date for the simulation.
     922             :  * @param[in] vregion A vector of region identifiers corresponding to the vaccination data.
     923             :  * @param[in] num_days The number of days for which the simulation runs.
     924             :  *
     925             :  * @return An IOResult indicating success or failure.
     926             :  */
     927             : template <typename FP = double>
     928          37 : IOResult<void> set_vaccination_data(std::vector<Model<FP>>& model, const std::string& path, Date date,
     929             :                                     const std::vector<int>& vregion, int num_days)
     930             : {
     931          37 :     BOOST_OUTCOME_TRY(auto&& vacc_data, read_vaccination_data(path));
     932          37 :     BOOST_OUTCOME_TRY(set_vaccination_data(model, vacc_data, date, vregion, num_days));
     933          74 :     return success();
     934          37 : }
     935             : 
     936             : } // namespace details
     937             : 
     938             : #ifdef MEMILIO_HAS_HDF5
     939             : 
     940             : /**
     941             :  * @brief Exports extrapolated real-world data time series for specified regions.
     942             :  *
     943             :  * This function generates and exports time series data based on the extrapolation and approximation methods
     944             :  * used to initialize the model with real-world data. The resulting data represents the initialized states of
     945             :  * the model over the specified time range.
     946             :  *
     947             :  * @tparam Model Type of the model used.
     948             :  *
     949             :  * @param[in] models A vector of models for which the extrapolated data is set.
     950             :  * @param[in] results_dir Path to the directory where the extrapolated results will be saved in a h5 file.
     951             :  * @param[in] counties A vector of region identifiers for which the time series will be exported.
     952             :  * @param[in] date The starting date of the time series.
     953             :  * @param[in] scaling_factor_inf A vector of scaling factors applied to confirmed cases.
     954             :  * @param[in] scaling_factor_icu A scaling factor applied to ICU cases.
     955             :  * @param[in] num_days The number of days for which will be extrapolated.
     956             :  * @param[in] divi_data_path Path to the DIVI ICU data file.
     957             :  * @param[in] confirmed_cases_path Path to the confirmed cases data file.
     958             :  * @param[in] population_data_path Path to the population data file.
     959             :  * @param[in] immunity_population A vector of vectors specifying immunity for each age group and immunity layer.
     960             :  * @param[in] vaccination_data_path Path to the vaccination data file (optional).
     961             :  *
     962             :  * @return An IOResult indicating success or failure.
     963             :  */
     964             : template <class Model>
     965           9 : IOResult<void> export_input_data_county_timeseries(
     966             :     std::vector<Model> models, const std::string& results_dir, const std::vector<int>& counties, Date date,
     967             :     const std::vector<double>& scaling_factor_inf, const double scaling_factor_icu, const int num_days,
     968             :     const std::string& divi_data_path, const std::string& confirmed_cases_path, const std::string& population_data_path,
     969             :     const std::vector<std::vector<double>> immunity_population, const std::string& vaccination_data_path = "")
     970             : {
     971           9 :     const auto num_groups = (size_t)models[0].parameters.get_num_groups();
     972           9 :     assert(scaling_factor_inf.size() == num_groups);
     973           9 :     assert(num_groups == ConfirmedCasesDataEntry::age_group_names.size());
     974           9 :     assert(models.size() == counties.size());
     975          36 :     std::vector<TimeSeries<double>> extrapolated_data(
     976           9 :         models.size(), TimeSeries<double>::zero(num_days + 1, (size_t)InfectionState::Count * num_groups));
     977             : 
     978           9 :     BOOST_OUTCOME_TRY(auto&& case_data, read_confirmed_cases_data(confirmed_cases_path));
     979           9 :     BOOST_OUTCOME_TRY(auto&& population_data, details::read_population_data(population_data_path, counties));
     980             : 
     981             :     // empty vector if set_vaccination_data is not set
     982           9 :     std::vector<VaccinationDataEntry> vacc_data;
     983           9 :     if (!vaccination_data_path.empty()) {
     984           9 :         BOOST_OUTCOME_TRY(vacc_data, read_vaccination_data(vaccination_data_path));
     985           9 :     }
     986             : 
     987          63 :     for (int t = 0; t <= num_days; ++t) {
     988          27 :         auto offset_day = offset_date_by_days(date, t);
     989             : 
     990          27 :         if (!vaccination_data_path.empty()) {
     991          27 :             BOOST_OUTCOME_TRY(details::set_vaccination_data(models, vacc_data, offset_day, counties, num_days));
     992          27 :         }
     993             : 
     994             :         // TODO: Reuse more code, e.g., set_divi_data (in secir) and a set_divi_data (here) only need a different ModelType.
     995             :         // TODO: add option to set ICU data from confirmed cases if DIVI or other data is not available.
     996          27 :         if (offset_day > Date(2020, 4, 23)) {
     997          27 :             BOOST_OUTCOME_TRY(details::set_divi_data(models, divi_data_path, counties, offset_day, scaling_factor_icu));
     998          27 :         }
     999             :         else {
    1000           0 :             log_warning("No DIVI data available for for date: {}-{}-{}", offset_day.day, offset_day.month,
    1001             :                         offset_day.year);
    1002             :         }
    1003             : 
    1004          54 :         BOOST_OUTCOME_TRY(details::set_confirmed_cases_data(models, case_data, counties, offset_day, scaling_factor_inf,
    1005             :                                                             immunity_population));
    1006             : 
    1007          54 :         BOOST_OUTCOME_TRY(details::set_population_data(models, population_data, counties, immunity_population));
    1008             : 
    1009          54 :         for (size_t r = 0; r < counties.size(); r++) {
    1010          27 :             extrapolated_data[r][t] = models[r].get_initial_values();
    1011             :             // in set_population_data the number of death individuals is subtracted from the SusceptibleImprovedImmunity compartment.
    1012             :             // Since we should be independent whether we consider them or not, we add them back here before we save the data.
    1013         189 :             for (size_t age = 0; age < num_groups; age++) {
    1014         162 :                 extrapolated_data[r][t][(size_t)InfectionState::SusceptibleImprovedImmunity +
    1015         324 :                                         age * (size_t)InfectionState::Count] +=
    1016         162 :                     extrapolated_data[r][t][(size_t)InfectionState::DeadNaive + age * (size_t)InfectionState::Count];
    1017             :             }
    1018             :         }
    1019             :     }
    1020          18 :     BOOST_OUTCOME_TRY(save_result(extrapolated_data, counties, static_cast<int>(num_groups),
    1021             :                                   path_join(results_dir, "Results_rki.h5")));
    1022             : 
    1023          45 :     auto extrapolated_rki_data_sum = sum_nodes(std::vector<std::vector<TimeSeries<double>>>{extrapolated_data});
    1024          72 :     BOOST_OUTCOME_TRY(save_result({extrapolated_rki_data_sum[0][0]}, {0}, static_cast<int>(num_groups),
    1025             :                                   path_join(results_dir, "Results_rki_sum.h5")));
    1026             : 
    1027          18 :     return success();
    1028           9 : }
    1029             : 
    1030             : #else
    1031             : template <class Model>
    1032             : IOResult<void> export_input_data_county_timeseries(std::vector<Model>, const std::string&, const std::vector<int>&,
    1033             :                                                    Date, const std::vector<double>&, const double, const int,
    1034             :                                                    const std::string&, const std::string&, const std::string&,
    1035             :                                                    const std::vector<std::vector<double>>, const std::string&)
    1036             : {
    1037             :     mio::log_warning("HDF5 not available. Cannot export time series of extrapolated real data.");
    1038             :     return success();
    1039             : }
    1040             : 
    1041             : #endif //MEMILIO_HAS_HDF5
    1042             : 
    1043             : /**
    1044             :  * @brief Reads input data for specified counties and initializes the model accordingly.
    1045             :  *
    1046             :  * This function loads real-world data for specified counties and initializes
    1047             :  * the corresponding model compartments. Optionally, it can extrapolate real-world data
    1048             :  * using the export_input_data_county_timeseries function.
    1049             :  *
    1050             :  * @tparam Model The model type used.
    1051             :  *
    1052             :  * @param[in,out] model Vector of model objects that will be initialized with the input data.
    1053             :  * @param[in] date The starting date for the simulation.
    1054             :  * @param[in] county Vector of county IDs for which the data is read.
    1055             :  * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases.
    1056             :  * @param[in] scaling_factor_icu Scaling factor for ICU data.
    1057             :  * @param[in] dir Path to the directory containing input data files.
    1058             :  * @param[in] num_days The number of days for which the simulation runs.
    1059             :  * @param[in] immunity_population Vector of vectors representing immunity proportions for each age group and immunity layer.
    1060             :  * @param[in] export_time_series Boolean flag indicating whether to export time series of extrapolated data (default: false).
    1061             :  *
    1062             :  * @return An IOResult indicating success or failure.
    1063             :  */
    1064             : template <class Model>
    1065          18 : IOResult<void> read_input_data_county(std::vector<Model>& model, Date date, const std::vector<int>& county,
    1066             :                                       const std::vector<double>& scaling_factor_inf, double scaling_factor_icu,
    1067             :                                       const std::string& dir, int num_days,
    1068             :                                       const std::vector<std::vector<double>> immunity_population,
    1069             :                                       bool export_time_series = false)
    1070             : {
    1071          36 :     BOOST_OUTCOME_TRY(details::set_vaccination_data(
    1072             :         model, path_join(dir, "pydata/Germany", "all_county_ageinf_vacc_ma7.json"), date, county, num_days));
    1073             : 
    1074             :     // TODO: Reuse more code, e.g., set_divi_data (in secir) and a set_divi_data (here) only need a different ModelType.
    1075             :     // TODO: add option to set ICU data from confirmed cases if DIVI or other data is not available.
    1076          18 :     if (date > Date(2020, 4, 23)) {
    1077          36 :         BOOST_OUTCOME_TRY(details::set_divi_data(model, path_join(dir, "pydata/Germany", "county_divi_ma7.json"),
    1078             :                                                  county, date, scaling_factor_icu));
    1079          18 :     }
    1080             :     else {
    1081           0 :         log_warning("No DIVI data available for this date");
    1082             :     }
    1083             : 
    1084          54 :     BOOST_OUTCOME_TRY(
    1085             :         details::set_confirmed_cases_data(model, path_join(dir, "pydata/Germany", "cases_all_county_age_ma7.json"),
    1086             :                                           county, date, scaling_factor_inf, immunity_population));
    1087          54 :     BOOST_OUTCOME_TRY(details::set_population_data(
    1088             :         model, path_join(dir, "pydata/Germany", "county_current_population.json"), county, immunity_population));
    1089             : 
    1090          18 :     if (export_time_series) {
    1091             :         // Use only if extrapolated real data is needed for comparison. EXPENSIVE !
    1092             :         // Run time equals run time of the previous functions times the num_days !
    1093             :         // (This only represents the vectorization of the previous function over all simulation days...)
    1094           0 :         log_info("Exporting time series of extrapolated real data. This may take some minutes. "
    1095             :                  "For simulation runs over the same time period, deactivate it.");
    1096           0 :         BOOST_OUTCOME_TRY(export_input_data_county_timeseries(
    1097             :             model, dir, county, date, scaling_factor_inf, scaling_factor_icu, num_days,
    1098             :             path_join(dir, "pydata/Germany", "county_divi_ma7.json"),
    1099             :             path_join(dir, "pydata/Germany", "cases_all_county_age_ma7.json"),
    1100             :             path_join(dir, "pydata/Germany", "county_current_population.json"), immunity_population,
    1101             :             path_join(dir, "pydata/Germany", "all_county_ageinf_vacc_ma7.json")));
    1102           0 :     }
    1103             : 
    1104          36 :     return success();
    1105          18 : }
    1106             : 
    1107             : /**
    1108             :  * @brief Reads compartments for geographic units at a specified date from data files.
    1109             :  *
    1110             :  * This function estimates all compartments from available data using the provided model parameters.
    1111             :  *
    1112             :  * @tparam Model The type of tmodel used.
    1113             :  *
    1114             :  * @param[in,out] model Vector of models, one per county, to be initialized with data.
    1115             :  * @param[in] date Date for which the data should be read.
    1116             :  * @param[in] node_ids Vector of IDs of the units for which data is read.
    1117             :  * @param[in] scaling_factor_inf Vector of scaling factors for confirmed cases.
    1118             :  * @param[in] scaling_factor_icu Scaling factor for ICU cases.
    1119             :  * @param[in] data_dir Directory containing the input data files.
    1120             :  * @param[in] num_days Number of days to simulate.
    1121             :  * @param[in] immunity_population Matrix containing immunity proportions for each age group and immunity layer.
    1122             :  * @param[in] export_time_series Boolean flag indicating whether to export time series of extrapolated data (default: false).
    1123             :  *
    1124             :  * @return An IOResult indicating success or failure.
    1125             :     */
    1126             : template <class Model>
    1127          18 : IOResult<void> read_input_data(std::vector<Model>& model, Date date, const std::vector<int>& node_ids,
    1128             :                                const std::vector<double>& scaling_factor_inf, double scaling_factor_icu,
    1129             :                                const std::string& data_dir, int num_days,
    1130             :                                const std::vector<std::vector<double>> immunity_population,
    1131             :                                bool export_time_series = false)
    1132             : {
    1133             : 
    1134          36 :     BOOST_OUTCOME_TRY(
    1135             :         details::set_vaccination_data(model, path_join(data_dir, "vaccination_data.json"), date, node_ids, num_days));
    1136             : 
    1137             :     // TODO: Reuse more code, e.g., set_divi_data (in secir) and a set_divi_data (here) only need a different ModelType.
    1138             :     // TODO: add option to set ICU data from confirmed cases if DIVI or other data is not available.
    1139          18 :     if (date > Date(2020, 4, 23)) {
    1140          36 :         BOOST_OUTCOME_TRY(details::set_divi_data(model, path_join(data_dir, "critical_cases.json"), node_ids, date,
    1141             :                                                  scaling_factor_icu));
    1142          18 :     }
    1143             :     else {
    1144           0 :         log_warning("No DIVI data available for this date");
    1145             :     }
    1146             : 
    1147          54 :     BOOST_OUTCOME_TRY(details::set_confirmed_cases_data(model, path_join(data_dir, "confirmed_cases.json"), node_ids,
    1148             :                                                         date, scaling_factor_inf, immunity_population));
    1149          54 :     BOOST_OUTCOME_TRY(details::set_population_data(model, path_join(data_dir, "population_data.json"), node_ids,
    1150             :                                                    immunity_population));
    1151             : 
    1152          18 :     if (export_time_series) {
    1153             :         // Use only if extrapolated real data is needed for comparison. EXPENSIVE !
    1154             :         // Run time equals run time of the previous functions times the num_days !
    1155             :         // (This only represents the vectorization of the previous function over all simulation days...)
    1156           0 :         log_info("Exporting time series of extrapolated real data. This may take some minutes. "
    1157             :                  "For simulation runs over the same time period, deactivate it.");
    1158           0 :         BOOST_OUTCOME_TRY(export_input_data_county_timeseries(
    1159             :             model, data_dir, node_ids, date, scaling_factor_inf, scaling_factor_icu, num_days,
    1160             :             path_join(data_dir, "critical_cases.json"), path_join(data_dir, "confirmed_cases.json"),
    1161             :             path_join(data_dir, "population_data.json"), immunity_population,
    1162             :             path_join(data_dir, "vaccination_data.json")));
    1163           0 :     }
    1164             : 
    1165          36 :     return success();
    1166          18 : }
    1167             : 
    1168             : } // namespace osecirts
    1169             : } // namespace mio
    1170             : 
    1171             : #endif // MEMILIO_HAS_JSONCPP
    1172             : 
    1173             : #endif // MIO_ODE_SECIRTS_PARAMETERS_IO_H

Generated by: LCOV version 1.14