epi_data.h Source File

CPP API: epi_data.h Source File
epi_data.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Daniel Abele
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 MEMILIO_IO_EPI_DATA_H
21 #define MEMILIO_IO_EPI_DATA_H
22 
23 #include "memilio/config.h"
24 
25 #ifdef MEMILIO_HAS_JSONCPP
26 
29 #include "memilio/io/io.h"
32 #include "memilio/utils/date.h"
33 
34 #include "json/value.h"
35 #include <string>
36 #include <vector>
37 
38 namespace mio
39 {
40 
44 class StringDate : public Date
45 {
46 public:
47  using Date::Date;
48 
49  StringDate(const Date& other)
50  : Date(other)
51  {
52  }
53 
54  template <class IoContext>
55  static IOResult<StringDate> deserialize(IoContext& io)
56  {
57  auto str = mio::deserialize(io, Tag<std::string>{});
58  return apply(
59  io,
60  [](auto&& str_) -> IOResult<StringDate> {
61  BOOST_OUTCOME_TRY(auto&& date, parse_date(str_));
62  return success(date);
63  },
64  str);
65  }
66 };
67 
73 class ConfirmedCasesNoAgeEntry
74 {
75 public:
76  ScalarType num_confirmed;
77  ScalarType num_recovered;
78  ScalarType num_deaths;
79  Date date;
80 
81  template <class IOContext>
82  static IOResult<ConfirmedCasesNoAgeEntry> deserialize(IOContext& io)
83  {
84  auto obj = io.expect_object("ConfirmedCasesNoAgeEntry");
85  auto num_confirmed = obj.expect_element("Confirmed", Tag<ScalarType>{});
86  auto num_recovered = obj.expect_element("Recovered", Tag<ScalarType>{});
87  auto num_deaths = obj.expect_element("Deaths", Tag<ScalarType>{});
88  auto date = obj.expect_element("Date", Tag<StringDate>{});
89  return apply(
90  io,
91  [](auto&& nc, auto&& nr, auto&& nd, auto&& d) {
92  return ConfirmedCasesNoAgeEntry{nc, nr, nd, d};
93  },
94  num_confirmed, num_recovered, num_deaths, date);
95  }
96 };
97 
104 inline IOResult<std::vector<ConfirmedCasesNoAgeEntry>> deserialize_confirmed_cases_noage(const Json::Value& jsvalue)
105 {
106  return deserialize_json(jsvalue, Tag<std::vector<ConfirmedCasesNoAgeEntry>>{});
107 }
108 
115 inline IOResult<std::vector<ConfirmedCasesNoAgeEntry>> read_confirmed_cases_noage(const std::string& filename)
116 {
117  return read_json(filename, Tag<std::vector<ConfirmedCasesNoAgeEntry>>{});
118 }
119 
126 class ConfirmedCasesDataEntry
127 {
128 public:
129  static std::vector<const char*> age_group_names;
130 
131  ScalarType num_confirmed;
132  ScalarType num_recovered;
133  ScalarType num_deaths;
134  Date date;
135  AgeGroup age_group;
136  boost::optional<regions::StateId> state_id;
137  boost::optional<regions::CountyId> county_id;
138  boost::optional<regions::DistrictId> district_id;
139 
140  template <class IOContext>
141  static IOResult<ConfirmedCasesDataEntry> deserialize(IOContext& io)
142  {
143  auto obj = io.expect_object("ConfirmedCasesDataEntry");
144  auto num_confirmed = obj.expect_element("Confirmed", Tag<ScalarType>{});
145  auto num_recovered = obj.expect_element("Recovered", Tag<ScalarType>{});
146  auto num_deaths = obj.expect_element("Deaths", Tag<ScalarType>{});
147  auto date = obj.expect_element("Date", Tag<StringDate>{});
148  auto age_group_str = obj.expect_element("Age_RKI", Tag<std::string>{});
149  auto state_id = obj.expect_optional("ID_State", Tag<regions::StateId>{});
150  auto county_id = obj.expect_optional("ID_County", Tag<regions::CountyId>{});
151  auto district_id = obj.expect_optional("ID_District", Tag<regions::DistrictId>{});
152  return apply(
153  io,
154  [](auto&& nc, auto&& nr, auto&& nd, auto&& d, auto&& a_str, auto&& sid, auto&& cid,
155  auto&& did) -> IOResult<ConfirmedCasesDataEntry> {
156  auto a = AgeGroup(0);
157  auto it = std::find(age_group_names.begin(), age_group_names.end(), a_str);
158  if (it != age_group_names.end()) {
159  a = AgeGroup(size_t(it - age_group_names.begin()));
160  }
161  else if (a_str == "unknown") {
162  a = AgeGroup(age_group_names.size());
163  }
164  else {
165  return failure(StatusCode::InvalidValue, "Invalid confirmed cases data age group.");
166  }
167  return success(ConfirmedCasesDataEntry{nc, nr, nd, d, a, sid, cid, did});
168  },
169  num_confirmed, num_recovered, num_deaths, date, age_group_str, state_id, county_id, district_id);
170  }
171 };
172 
178 inline IOResult<std::vector<ConfirmedCasesDataEntry>> deserialize_confirmed_cases_data(const Json::Value& jsvalue)
179 {
180  BOOST_OUTCOME_TRY(auto&& cases_data, deserialize_json(jsvalue, Tag<std::vector<ConfirmedCasesDataEntry>>{}));
181  //filter entries with unknown age group
182  auto it = std::remove_if(cases_data.begin(), cases_data.end(), [](auto&& rki_entry) {
183  return rki_entry.age_group >= AgeGroup(ConfirmedCasesDataEntry::age_group_names.size());
184  });
185  cases_data.erase(it, cases_data.end());
186  return success(std::move(cases_data));
187 }
188 
194 inline IOResult<std::vector<ConfirmedCasesDataEntry>> read_confirmed_cases_data(const std::string& filename)
195 {
196  BOOST_OUTCOME_TRY(auto&& jsvalue, read_json(filename));
197  return deserialize_confirmed_cases_data(jsvalue);
198 }
199 
206 class DiviEntry
207 {
208 public:
209  ScalarType num_icu;
210  Date date;
211  boost::optional<regions::StateId> state_id;
212  boost::optional<regions::CountyId> county_id;
213  boost::optional<regions::DistrictId> district_id;
214 
215  template <class IoContext>
216  static IOResult<DiviEntry> deserialize(IoContext& io)
217  {
218  auto obj = io.expect_object("DiviEntry");
219  auto num_icu = obj.expect_element("ICU", Tag<ScalarType>{});
220  auto date = obj.expect_element("Date", Tag<StringDate>{});
221  auto state_id = obj.expect_optional("ID_State", Tag<regions::StateId>{});
222  auto county_id = obj.expect_optional("ID_County", Tag<regions::CountyId>{});
223  auto district_id = obj.expect_optional("ID_District", Tag<regions::DistrictId>{});
224  return apply(
225  io,
226  [](auto&& ni, auto&& d, auto&& sid, auto&& cid, auto&& did) {
227  return DiviEntry{ni, d, sid, cid, did};
228  },
229  num_icu, date, state_id, county_id, district_id);
230  }
231 };
232 
238 inline bool is_divi_data_available(const Date& date)
239 {
240  static const Date divi_data_start(2020, 4, 23);
241  return date >= divi_data_start;
242 }
243 
252 inline bool is_vaccination_data_available(const Date& start_date, const Date& end_date)
253 {
254  static const Date vaccination_data_start(2020, 12, 27);
255  static const Date vaccination_data_end(2024, 7, 9);
256  return !(end_date < vaccination_data_start || start_date > vaccination_data_end);
257 }
258 
264 inline IOResult<std::vector<DiviEntry>> deserialize_divi_data(const Json::Value& jsvalue)
265 {
266  return deserialize_json(jsvalue, Tag<std::vector<DiviEntry>>{});
267 }
268 
274 inline IOResult<std::vector<DiviEntry>> read_divi_data(const std::string& filename)
275 {
276  return read_json(filename, Tag<std::vector<DiviEntry>>{});
277 }
278 
279 namespace details
280 {
281 //check all results in a vector and unpack each
282 template <class T>
283 IOResult<std::vector<T>> unpack_all(const std::vector<IOResult<T>>& v)
284 {
285  std::vector<T> w;
286  w.reserve(v.size());
287  for (auto&& r : v) {
288  BOOST_OUTCOME_TRY(auto&& t, r);
289  w.push_back(t);
290  }
291  return success(w);
292 }
293 } // namespace details
294 
301 class PopulationDataEntry
302 {
303 public:
304  static std::vector<const char*> age_group_names;
305 
306  CustomIndexArray<ScalarType, AgeGroup> population;
307  boost::optional<regions::StateId> state_id;
308  boost::optional<regions::CountyId> county_id;
309  boost::optional<regions::DistrictId> district_id;
310 
311  template <class IoContext>
312  static IOResult<PopulationDataEntry> deserialize(IoContext& io)
313  {
314  auto obj = io.expect_object("PopulationDataEntry");
315  auto state_id = obj.expect_optional("ID_State", Tag<regions::StateId>{});
316  auto county_id = obj.expect_optional("ID_County", Tag<regions::CountyId>{});
317  auto district_id = obj.expect_optional("ID_District", Tag<regions::DistrictId>{});
318  std::vector<IOResult<ScalarType>> age_groups;
319  age_groups.reserve(age_group_names.size());
320  std::transform(age_group_names.begin(), age_group_names.end(), std::back_inserter(age_groups),
321  [&obj](auto&& age_name) {
322  return obj.expect_element(age_name, Tag<ScalarType>{});
323  });
324  return apply(
325  io,
326  [](auto&& ag, auto&& sid, auto&& cid, auto&& did) {
327  return PopulationDataEntry{
328  CustomIndexArray<ScalarType, AgeGroup>(AgeGroup(ag.size()), ag.begin(), ag.end()), sid, cid, did};
329  },
330  details::unpack_all(age_groups), state_id, county_id, district_id);
331  }
332 };
333 
334 namespace details
335 {
336 inline void get_rki_age_interpolation_coefficients(const std::vector<ScalarType>& age_ranges,
337  std::vector<std::vector<ScalarType>>& interpolation,
338  std::vector<bool>& carry_over)
339 {
340  std::array<ScalarType, 6> param_ranges = {5., 10., 20., 25., 20., 20.};
341  assert(param_ranges.size() == ConfirmedCasesDataEntry::age_group_names.size() &&
342  "Number of RKI age groups does not match number of age ranges.");
343 
344  //counter for parameter age groups
345  size_t counter = 0;
346 
347  //residual of param age groups
348  ScalarType res = 0.0;
349  for (size_t i = 0; i < age_ranges.size(); i++) {
350 
351  // if current param age group didn't fit into previous rki age group, transfer residual to current age group
352  if (res < 0) {
353  interpolation[i].push_back(std::min(-res / age_ranges[i], 1.0));
354  }
355 
356  if (counter < param_ranges.size() - 1) {
357  res += age_ranges[i];
358  if (std::abs(res) < age_ranges[i]) {
359  counter++;
360  }
361  // iterate over param age groups while there is still room in the current rki age group
362  while (res > 0) {
363  res -= param_ranges[counter];
364  interpolation[i].push_back((param_ranges[counter] + std::min(res, 0.0)) / age_ranges[i]);
365  if (res >= 0) {
366  counter++;
367  }
368  }
369  if (res < 0) {
370  carry_over.push_back(true);
371  }
372  else if (res == 0) {
373  carry_over.push_back(false);
374  }
375  }
376  // if last param age group is reached
377  else {
378  interpolation[i].push_back((age_ranges[i] + res) / age_ranges[i]);
379  if (res < 0 || counter == 0) {
380  carry_over.push_back(true);
381  }
382  else if (res == 0) {
383  carry_over.push_back(false);
384  }
385  res = 0;
386  }
387  }
388 }
389 
390 inline std::vector<PopulationDataEntry>
391 interpolate_to_rki_age_groups(const std::vector<PopulationDataEntry>& population_data)
392 {
393  std::vector<ScalarType> age_ranges = {3., 3., 9., 3., 7., 5., 10., 10., 15., 10., 25.};
394  std::vector<std::vector<ScalarType>> coefficients{age_ranges.size()};
395  std::vector<bool> carry_over{};
396  get_rki_age_interpolation_coefficients(age_ranges, coefficients, carry_over);
397 
398  std::vector<PopulationDataEntry> interpolated{population_data};
399  for (auto region_entry_idx = size_t(0); region_entry_idx < population_data.size(); ++region_entry_idx) {
400  interpolated[region_entry_idx].population =
401  CustomIndexArray<ScalarType, AgeGroup>(AgeGroup(ConfirmedCasesDataEntry::age_group_names.size()), 0.0);
402 
403  size_t interpolated_age_idx = 0;
404  for (size_t age_idx = 0; age_idx < coefficients.size(); age_idx++) {
405  for (size_t coeff_idx = 0; coeff_idx < coefficients[age_idx].size(); coeff_idx++) {
406  interpolated[region_entry_idx].population[AgeGroup(interpolated_age_idx)] +=
407  coefficients[age_idx][coeff_idx] * population_data[region_entry_idx].population[AgeGroup(age_idx)];
408  if (coeff_idx < coefficients[age_idx].size() - 1 || !carry_over[age_idx]) {
409  interpolated_age_idx++;
410  }
411  }
412  }
413  }
414 
415  return interpolated;
416 }
417 } // namespace details
418 
426 inline IOResult<std::vector<PopulationDataEntry>> deserialize_population_data(const Json::Value& jsvalue,
427  bool rki_age_groups = true)
428 {
429  BOOST_OUTCOME_TRY(auto&& population_data, deserialize_json(jsvalue, Tag<std::vector<PopulationDataEntry>>{}));
430  if (rki_age_groups) {
431  return success(details::interpolate_to_rki_age_groups(population_data));
432  }
433  else {
434  return success(population_data);
435  }
436 }
437 
444 inline IOResult<std::vector<PopulationDataEntry>> read_population_data(const std::string& filename,
445  bool rki_age_group = true)
446 {
447  BOOST_OUTCOME_TRY(auto&& jsvalue, read_json(filename));
448  return deserialize_population_data(jsvalue, rki_age_group);
449 }
450 
455 IOResult<void> set_confirmed_cases_age_group_names(std::vector<const char*> names);
456 
461 IOResult<void> set_population_data_age_group_names(std::vector<const char*> names);
462 
467 IOResult<void> set_vaccination_data_age_group_names(std::vector<const char*> names);
468 
476 IOResult<std::vector<int>> get_node_ids(const std::string& path, bool is_node_for_county, bool rki_age_groups = true);
477 
481 class VaccinationDataEntry
482 {
483 public:
484  static std::vector<const char*> age_group_names;
485 
486  ScalarType num_vaccinations_partial, num_vaccinations_completed, num_vaccinations_refreshed_first,
487  num_vaccinations_refreshed_additional;
488  Date date;
489  AgeGroup age_group;
490  boost::optional<regions::StateId> state_id;
491  boost::optional<regions::CountyId> county_id;
492  boost::optional<regions::DistrictId> district_id;
493 
494  template <class IoContext>
495  static IOResult<VaccinationDataEntry> deserialize(IoContext& io)
496  {
497  auto obj = io.expect_object("VaccinationDataEntry");
498  auto num_vaccinations_partial = obj.expect_element("Vacc_partially", Tag<ScalarType>{});
499  auto num_vaccinations_completed = obj.expect_element("Vacc_completed", Tag<ScalarType>{});
500  auto num_vaccinations_refreshed_first = obj.expect_optional("Vacc_refreshed", Tag<ScalarType>{});
501  auto num_vaccinations_refreshed_additional = obj.expect_optional("Vacc_refreshed_2", Tag<ScalarType>{});
502  auto date = obj.expect_element("Date", Tag<StringDate>{});
503  auto age_group_str = obj.expect_element("Age_RKI", Tag<std::string>{});
504  auto state_id = obj.expect_optional("ID_County", Tag<regions::StateId>{});
505  auto county_id = obj.expect_optional("ID_County", Tag<regions::CountyId>{});
506  auto district_id = obj.expect_optional("ID_District", Tag<regions::DistrictId>{});
507  return mio::apply(
508  io,
509  [](auto np, auto nc, auto n_refreshed_1, auto n_refreshed_2, auto d, auto&& a_str, auto sid, auto cid,
510  auto did) -> IOResult<VaccinationDataEntry> {
511  auto it = std::find(age_group_names.begin(), age_group_names.end(), a_str);
512  auto a = AgeGroup(0);
513  if (it != age_group_names.end()) {
514  a = AgeGroup(size_t(it - age_group_names.begin()));
515  }
516  else {
517  return failure(StatusCode::InvalidValue, "Invalid vaccination data age group.");
518  }
519  // Optional values are 0 if they do not exist.
520  auto n_refreshed_1_value = n_refreshed_1.value_or(0.0);
521  auto n_refreshed_2_value = n_refreshed_2.value_or(0.0);
522  return success(
523  VaccinationDataEntry{np, nc, n_refreshed_1_value, n_refreshed_2_value, d, a, sid, cid, did});
524  },
525  num_vaccinations_partial, num_vaccinations_completed, num_vaccinations_refreshed_first,
526  num_vaccinations_refreshed_additional, date, age_group_str, state_id, county_id, district_id);
527  }
528 };
529 
535 inline IOResult<std::vector<VaccinationDataEntry>> deserialize_vaccination_data(const Json::Value& jsvalue)
536 {
537  return deserialize_json(jsvalue, Tag<std::vector<VaccinationDataEntry>>{});
538 }
539 
545 inline IOResult<std::vector<VaccinationDataEntry>> read_vaccination_data(const std::string& filename)
546 {
547  BOOST_OUTCOME_TRY(auto&& jsvalue, read_json(filename));
548  return deserialize_vaccination_data(jsvalue);
549 }
550 
551 } // namespace mio
552 
553 #endif //MEMILIO_HAS_JSONCPP
554 
555 #endif //MEMILIO_IO_EPI_DATA_H
double ScalarType
Configuration of memilio library.
Definition: memilio/config.h:30
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type min(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2599
ad::internal::unary_intermediate< AD_TAPE_REAL, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::operations::ad_fabs< AD_TAPE_REAL > > abs(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &x1)
Definition: ad.hpp:1144
int size(Comm comm)
Return the size of the given communicator.
Definition: miompi.cpp:75
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
IOResult< T > deserialize(IOContext &io, Tag< T > tag)
Restores an object from the data stored in an IO context.
Definition: io.h:860
auto failure(const IOStatus &s)
Create an object that is implicitly convertible to an error IOResult<T>.
Definition: io.h:380
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:407
auto i
Definition: io.h:809
details::ApplyResultT< F, T... > apply(IOContext &io, F f, const IOResult< T > &... rs)
Evaluate a function with zero or more unpacked IOResults as arguments.
Definition: io.h:481
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:359
IOResult< Date > parse_date(const std::string &date_str)
parses a date from a string.
Definition: date.h:219
Date()=default
default constructor.