LCOV - code coverage report
Current view: top level - models/ode_secir - parameters_io.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 124 143 86.7 %
Date: 2024-11-18 12:45:26 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /* 
       2             : * Copyright (C) 2020-2024 MEmilio
       3             : *
       4             : * Authors: Wadim Koslow, Daniel Abele, Martin J. Kuehn, Lena Ploetzke
       5             : *
       6             : * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
       7             : *
       8             : * Licensed under the Apache License, Version 2.0 (the "License");
       9             : * you may not use this file except in compliance with the License.
      10             : * You may obtain a copy of the License at
      11             : *
      12             : *     http://www.apache.org/licenses/LICENSE-2.0
      13             : *
      14             : * Unless required by applicable law or agreed to in writing, software
      15             : * distributed under the License is distributed on an "AS IS" BASIS,
      16             : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      17             : * See the License for the specific language governing permissions and
      18             : * limitations under the License.
      19             : */
      20             : 
      21             : #include "memilio/utils/compiler_diagnostics.h"
      22             : 
      23             : //see below for line that causes this warning
      24             : GCC_CLANG_DIAGNOSTIC(push)
      25             : GCC_CLANG_DIAGNOSTIC(ignored "-Wmaybe-uninitialized")
      26             : 
      27             : #include "memilio/config.h"
      28             : 
      29             : #ifdef MEMILIO_HAS_JSONCPP
      30             : 
      31             : #include "ode_secir/parameters_io.h"
      32             : #include "memilio/io/epi_data.h"
      33             : #include "memilio/io/io.h"
      34             : #include "memilio/utils/stl_util.h"
      35             : #include "memilio/utils/date.h"
      36             : 
      37             : namespace mio
      38             : {
      39             : 
      40             : namespace osecir
      41             : {
      42             : 
      43             : namespace details
      44             : {
      45             : //district, county or state id of a data entry if available, 0 (for whole country) otherwise
      46             : //used to compare data entries to integer ids in STL algorithms
      47             : template <class EpiDataEntry>
      48     1023604 : int get_region_id(const EpiDataEntry& entry)
      49             : {
      50     1023604 :     return entry.county_id
      51     2195352 :                ? entry.county_id->get()
      52     1455683 :                : (entry.state_id ? entry.state_id->get() : (entry.district_id ? entry.district_id->get() : 0));
      53             : }
      54             : //overload for integers, so the comparison of data entry to integers is symmetric (required by e.g. equal_range)
      55        1672 : int get_region_id(int id)
      56             : {
      57        1672 :     return id;
      58             : }
      59             : 
      60          88 : IOResult<void> read_confirmed_cases_data(
      61             :     std::vector<ConfirmedCasesDataEntry>& rki_data, std::vector<int> const& vregion, Date date,
      62             :     std::vector<std::vector<double>>& vnum_Exposed, std::vector<std::vector<double>>& vnum_InfectedNoSymptoms,
      63             :     std::vector<std::vector<double>>& vnum_InfectedSymptoms, std::vector<std::vector<double>>& vnum_InfectedSevere,
      64             :     std::vector<std::vector<double>>& vnum_icu, std::vector<std::vector<double>>& vnum_death,
      65             :     std::vector<std::vector<double>>& vnum_rec, const std::vector<std::vector<int>>& vt_Exposed,
      66             :     const std::vector<std::vector<int>>& vt_InfectedNoSymptoms,
      67             :     const std::vector<std::vector<int>>& vt_InfectedSymptoms, const std::vector<std::vector<int>>& vt_InfectedSevere,
      68             :     const std::vector<std::vector<int>>& vt_InfectedCritical, const std::vector<std::vector<double>>& vmu_C_R,
      69             :     const std::vector<std::vector<double>>& vmu_I_H, const std::vector<std::vector<double>>& vmu_H_U,
      70             :     const std::vector<double>& scaling_factor_inf)
      71             : {
      72          88 :     auto max_date_entry = std::max_element(rki_data.begin(), rki_data.end(), [](auto&& a, auto&& b) {
      73       48488 :         return a.date < b.date;
      74             :     });
      75          88 :     if (max_date_entry == rki_data.end()) {
      76           0 :         log_error("RKI data file is empty.");
      77           0 :         return failure(StatusCode::InvalidFileFormat, "RKI file is empty.");
      78             :     }
      79          88 :     auto max_date = max_date_entry->date;
      80          88 :     if (max_date < date) {
      81           0 :         log_error("Specified date does not exist in RKI data");
      82           0 :         return failure(StatusCode::OutOfRange, "Specified date does not exist in RKI data.");
      83             :     }
      84          88 :     auto days_surplus = std::min(get_offset_in_days(max_date, date) - 6, 0);
      85             : 
      86             :     //this statement causes maybe-uninitialized warning for some versions of gcc.
      87             :     //the error is reported in an included header, so the warning is disabled for the whole file
      88          88 :     std::sort(rki_data.begin(), rki_data.end(), [](auto&& a, auto&& b) {
      89      507792 :         return std::make_tuple(get_region_id(a), a.date) < std::make_tuple(get_region_id(b), b.date);
      90             :     });
      91             : 
      92         176 :     for (auto region_idx = size_t(0); region_idx < vregion.size(); ++region_idx) {
      93          88 :         auto region_entry_range_it =
      94        1760 :             std::equal_range(rki_data.begin(), rki_data.end(), vregion[region_idx], [](auto&& a, auto&& b) {
      95        1672 :                 return get_region_id(a) < get_region_id(b);
      96             :             });
      97          88 :         auto region_entry_range = make_range(region_entry_range_it);
      98          88 :         if (region_entry_range.begin() == region_entry_range.end()) {
      99           0 :             log_error("No entries found for region {}", vregion[region_idx]);
     100           0 :             return failure(StatusCode::InvalidFileFormat,
     101           0 :                            "No entries found for region " + std::to_string(vregion[region_idx]));
     102             :         }
     103       48664 :         for (auto&& region_entry : region_entry_range) {
     104             : 
     105       48576 :             auto& t_Exposed            = vt_Exposed[region_idx];
     106       48576 :             auto& t_InfectedNoSymptoms = vt_InfectedNoSymptoms[region_idx];
     107       48576 :             auto& t_InfectedSymptoms   = vt_InfectedSymptoms[region_idx];
     108       48576 :             auto& t_InfectedSevere     = vt_InfectedSevere[region_idx];
     109       48576 :             auto& t_InfectedCritical   = vt_InfectedCritical[region_idx];
     110             : 
     111       48576 :             auto& num_InfectedNoSymptoms = vnum_InfectedNoSymptoms[region_idx];
     112       48576 :             auto& num_InfectedSymptoms   = vnum_InfectedSymptoms[region_idx];
     113       48576 :             auto& num_rec                = vnum_rec[region_idx];
     114       48576 :             auto& num_Exposed            = vnum_Exposed[region_idx];
     115       48576 :             auto& num_InfectedSevere     = vnum_InfectedSevere[region_idx];
     116       48576 :             auto& num_death              = vnum_death[region_idx];
     117       48576 :             auto& num_icu                = vnum_icu[region_idx];
     118             : 
     119       48576 :             auto& mu_C_R = vmu_C_R[region_idx];
     120       48576 :             auto& mu_I_H = vmu_I_H[region_idx];
     121       48576 :             auto& mu_H_U = vmu_H_U[region_idx];
     122             : 
     123       48576 :             auto date_df = region_entry.date;
     124       48576 :             auto age     = size_t(region_entry.age_group);
     125             : 
     126       48576 :             bool read_icu = false; //params.populations.get({age, SecirCompartments::U}) == 0;
     127             : 
     128       48576 :             if (date_df == offset_date_by_days(date, 0)) {
     129         420 :                 num_InfectedSymptoms[age] += scaling_factor_inf[age] * region_entry.num_confirmed;
     130         420 :                 num_rec[age] += region_entry.num_confirmed;
     131             :             }
     132       48576 :             if (date_df == offset_date_by_days(date, days_surplus)) {
     133         420 :                 num_InfectedNoSymptoms[age] -=
     134         420 :                     1 / (1 - mu_C_R[age]) * scaling_factor_inf[age] * region_entry.num_confirmed;
     135             :             }
     136       48576 :             if (date_df == offset_date_by_days(date, t_InfectedNoSymptoms[age] + days_surplus)) {
     137         420 :                 num_InfectedNoSymptoms[age] +=
     138         420 :                     1 / (1 - mu_C_R[age]) * scaling_factor_inf[age] * region_entry.num_confirmed;
     139         420 :                 num_Exposed[age] -= 1 / (1 - mu_C_R[age]) * scaling_factor_inf[age] * region_entry.num_confirmed;
     140             :             }
     141       48576 :             if (date_df == offset_date_by_days(date, t_Exposed[age] + t_InfectedNoSymptoms[age] + days_surplus)) {
     142         420 :                 num_Exposed[age] += 1 / (1 - mu_C_R[age]) * scaling_factor_inf[age] * region_entry.num_confirmed;
     143             :             }
     144       48576 :             if (date_df == offset_date_by_days(date, -t_InfectedSymptoms[age])) {
     145         420 :                 num_InfectedSymptoms[age] -= scaling_factor_inf[age] * region_entry.num_confirmed;
     146         420 :                 num_InfectedSevere[age] += mu_I_H[age] * scaling_factor_inf[age] * region_entry.num_confirmed;
     147             :             }
     148       48576 :             if (date_df == offset_date_by_days(date, -t_InfectedSymptoms[age] - t_InfectedSevere[age])) {
     149         420 :                 num_InfectedSevere[age] -= mu_I_H[age] * scaling_factor_inf[age] * region_entry.num_confirmed;
     150         420 :                 if (read_icu) {
     151           0 :                     num_icu[age] += mu_I_H[age] * mu_H_U[age] * scaling_factor_inf[age] * region_entry.num_confirmed;
     152             :                 }
     153             :             }
     154       97152 :             if (date_df ==
     155       97152 :                 offset_date_by_days(date, -t_InfectedSymptoms[age] - t_InfectedSevere[age] - t_InfectedCritical[age])) {
     156         420 :                 num_death[age] += region_entry.num_deaths;
     157         420 :                 if (read_icu) {
     158           0 :                     num_icu[age] -= mu_I_H[age] * mu_H_U[age] * scaling_factor_inf[age] * region_entry.num_confirmed;
     159             :                 }
     160             :             }
     161             :         }
     162             :     }
     163             : 
     164         176 :     for (size_t region_idx = 0; region_idx < vregion.size(); ++region_idx) {
     165          88 :         auto region = vregion[region_idx];
     166             : 
     167          88 :         auto& num_InfectedNoSymptoms = vnum_InfectedNoSymptoms[region_idx];
     168          88 :         auto& num_InfectedSymptoms   = vnum_InfectedSymptoms[region_idx];
     169          88 :         auto& num_rec                = vnum_rec[region_idx];
     170          88 :         auto& num_Exposed            = vnum_Exposed[region_idx];
     171          88 :         auto& num_InfectedSevere     = vnum_InfectedSevere[region_idx];
     172          88 :         auto& num_death              = vnum_death[region_idx];
     173          88 :         auto& num_icu                = vnum_icu[region_idx];
     174             : 
     175         616 :         for (size_t i = 0; i < ConfirmedCasesDataEntry::age_group_names.size(); i++) {
     176         528 :             auto try_fix_constraints = [region, i](double& value, double error, auto str) {
     177        3696 :                 if (value < error) {
     178             :                     //this should probably return a failure
     179             :                     //but the algorithm is not robust enough to avoid large negative values and there are tests that rely on it
     180           0 :                     log_error("{:s} for age group {:s} is {:.4f} for region {:d}, exceeds expected negative value.",
     181           0 :                               str, ConfirmedCasesDataEntry::age_group_names[i], value, region);
     182           0 :                     value = 0.0;
     183             :                 }
     184        3696 :                 else if (value < 0) {
     185           0 :                     log_info("{:s} for age group {:s} is {:.4f} for region {:d}, automatically corrected", str,
     186           0 :                              ConfirmedCasesDataEntry::age_group_names[i], value, region);
     187           0 :                     value = 0.0;
     188             :                 }
     189        3696 :             };
     190             : 
     191         528 :             try_fix_constraints(num_InfectedSymptoms[i], -5, "InfectedSymptoms");
     192         528 :             try_fix_constraints(num_InfectedNoSymptoms[i], -5, "InfectedNoSymptoms");
     193         528 :             try_fix_constraints(num_Exposed[i], -5, "Exposed");
     194         528 :             try_fix_constraints(num_InfectedSevere[i], -5, "InfectedSevere");
     195         528 :             try_fix_constraints(num_death[i], -5, "Dead");
     196         528 :             try_fix_constraints(num_icu[i], -5, "InfectedCritical");
     197         528 :             try_fix_constraints(num_rec[i], -20, "Recovered");
     198             :         }
     199             :     }
     200             : 
     201         176 :     return success();
     202             : }
     203             : 
     204          70 : IOResult<void> read_divi_data(const std::string& path, const std::vector<int>& vregion, Date date,
     205             :                               std::vector<double>& vnum_icu)
     206             : {
     207          70 :     BOOST_OUTCOME_TRY(auto&& divi_data, mio::read_divi_data(path));
     208             : 
     209          70 :     auto max_date_entry = std::max_element(divi_data.begin(), divi_data.end(), [](auto&& a, auto&& b) {
     210        6370 :         return a.date < b.date;
     211             :     });
     212          70 :     if (max_date_entry == divi_data.end()) {
     213           0 :         log_error("DIVI data file is empty.");
     214           0 :         return failure(StatusCode::InvalidFileFormat, path + ", file is empty.");
     215             :     }
     216          70 :     auto max_date = max_date_entry->date;
     217          70 :     if (max_date < date) {
     218           0 :         log_error("Specified date does not exist in DIVI data.");
     219           0 :         return failure(StatusCode::OutOfRange, path + ", specified date does not exist in DIVI data.");
     220             :     }
     221             : 
     222        6510 :     for (auto&& entry : divi_data) {
     223       19136 :         auto it      = std::find_if(vregion.begin(), vregion.end(), [&entry](auto r) {
     224       19136 :             return r == 0 || r == get_region_id(entry);
     225             :         });
     226        6440 :         auto date_df = entry.date;
     227        6440 :         if (it != vregion.end() && date_df == date) {
     228          70 :             auto region_idx      = size_t(it - vregion.begin());
     229          70 :             vnum_icu[region_idx] = entry.num_icu;
     230             :         }
     231             :     }
     232             : 
     233         140 :     return success();
     234          70 : }
     235             : 
     236             : IOResult<std::vector<std::vector<double>>>
     237         105 : read_population_data(const std::string& path, const std::vector<int>& vregion, bool accumulate_age_groups)
     238             : {
     239         105 :     BOOST_OUTCOME_TRY(auto&& population_data, mio::read_population_data(path, !accumulate_age_groups));
     240             :     //if we set up the model for one age group, the population data should be read in with the
     241             :     //age groups given in the population data json file and are accumulated later
     242             :     //otherwise the populations are directly saved for the correct model age groups
     243         105 :     size_t age_group_size = accumulate_age_groups ? PopulationDataEntry::age_group_names.size()
     244          96 :                                                   : ConfirmedCasesDataEntry::age_group_names.size();
     245         420 :     std::vector<std::vector<double>> vnum_population(vregion.size(), std::vector<double>(age_group_size, 0.0));
     246             : 
     247       42210 :     for (auto&& entry : population_data) {
     248      246477 :         auto it = std::find_if(vregion.begin(), vregion.end(), [&entry](auto r) {
     249      200901 :             return r == 0 || (entry.county_id && regions::StateId(r) == regions::get_state_id(int(*entry.county_id))) ||
     250      246878 :                    (entry.county_id && regions::CountyId(r) == *entry.county_id) ||
     251      171412 :                    (entry.district_id && regions::DistrictId(r) == *entry.district_id);
     252             :         });
     253       42105 :         if (it != vregion.end()) {
     254         519 :             auto region_idx      = size_t(it - vregion.begin());
     255         519 :             auto& num_population = vnum_population[region_idx];
     256        3678 :             for (size_t age = 0; age < num_population.size(); age++) {
     257        3159 :                 num_population[age] += entry.population[AgeGroup(age)];
     258             :             }
     259             :         }
     260             :     }
     261         105 :     if (accumulate_age_groups) {
     262          36 :         std::vector<std::vector<double>> vnum_pop_acc(vregion.size(), std::vector<double>(1, 0));
     263          18 :         for (size_t region = 0; region < vregion.size(); ++region) {
     264           9 :             vnum_pop_acc[region][0] =
     265           9 :                 std::accumulate(vnum_population[region].begin(), vnum_population[region].end(), 0.0);
     266             :         }
     267           9 :         return success(vnum_pop_acc);
     268           9 :     }
     269             :     else {
     270          96 :         return success(vnum_population);
     271             :     }
     272         105 : }
     273             : 
     274             : } // namespace details
     275             : } // namespace osecir
     276             : } // namespace mio
     277             : 
     278             : #endif // MEMILIO_HAS_JSONCPP
     279             : 
     280             : GCC_CLANG_DIAGNOSTIC(pop)

Generated by: LCOV version 1.14