Skip to content

Commit 3252cf5

Browse files
HenrZuannawendler
andauthored
1268 Vaccination data no longer reported since July 2024 (#1269)
- Allow usage of DIVI data after July 2024 - Vaccination data is not available for dates after July 2024. Now we check if it needs to be considered in the simulation period - Better handling of vaccination data when its not available Co-authored-by: annawendler <106674756+annawendler@users.noreply.github.com>
1 parent 2d50c90 commit 3252cf5

File tree

7 files changed

+271
-32
lines changed

7 files changed

+271
-32
lines changed

cpp/memilio/io/epi_data.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,22 @@ class DiviEntry
238238
inline bool is_divi_data_available(const Date& date)
239239
{
240240
static const Date divi_data_start(2020, 4, 23);
241-
static const Date divi_data_end(2024, 7, 21);
242-
return date >= divi_data_start && date <= divi_data_end;
241+
return date >= divi_data_start;
242+
}
243+
244+
/**
245+
* @brief Checks if vaccination data is available for any day within the given date interval,
246+
* only checking for overlap with the bounds of the simulation period.
247+
* @param start_date The start date of the interval to check.
248+
* @param end_date The end date of the interval to check.
249+
* @return True if there is any overlap between the given date range and the vaccination data
250+
* availability period, false otherwise.
251+
*/
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);
243257
}
244258

245259
/**

cpp/models/ode_secirts/parameters_io.h

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -862,10 +862,15 @@ IOResult<void> set_vaccination_data(std::vector<Model<FP>>& model, const std::ve
862862
if (max_date_entry == vacc_data.end()) {
863863
return failure(StatusCode::InvalidFileFormat, "Vaccination data file is empty.");
864864
}
865-
auto max_date = max_date_entry->date;
866-
if (max_date < offset_date_by_days(date, num_days)) {
867-
log_error("Vaccination data does not contain all dates. After the last day the data, vaccinations per day are "
868-
"set to 0.");
865+
auto max_date = max_date_entry->date;
866+
auto min_date_entry = std::min_element(vacc_data.begin(), vacc_data.end(), [](auto&& a, auto&& b) {
867+
return a.date < b.date;
868+
});
869+
auto min_date = min_date_entry->date;
870+
if (min_date > date || max_date < offset_date_by_days(date, num_days)) {
871+
log_warning("Vaccination data only available from {} to {}. "
872+
"For days before and after, vaccinations will be set to 0.",
873+
min_date, max_date);
869874
}
870875

871876
for (auto&& vacc_data_entry : vacc_data) {
@@ -952,6 +957,27 @@ template <typename FP = double>
952957
IOResult<void> set_vaccination_data(std::vector<Model<FP>>& model, const std::string& path, Date date,
953958
const std::vector<int>& vregion, int num_days)
954959
{
960+
// Check if vaccination data is available for the given date range
961+
auto end_date = offset_date_by_days(date, num_days);
962+
if (!is_vaccination_data_available(date, end_date)) {
963+
log_warning("No vaccination data available in range from {} to {}. "
964+
"Vaccination data will be set to 0.",
965+
date, end_date);
966+
// Set vaccination data to 0 for all models
967+
for (auto& m : model) {
968+
m.parameters.template get<DailyPartialVaccinations<FP>>().resize(SimulationDay(num_days + 1));
969+
m.parameters.template get<DailyFullVaccinations<FP>>().resize(SimulationDay(num_days + 1));
970+
m.parameters.template get<DailyBoosterVaccinations<FP>>().resize(SimulationDay(num_days + 1));
971+
for (auto d = SimulationDay(0); d < SimulationDay(num_days + 1); ++d) {
972+
for (auto a = AgeGroup(0); a < m.parameters.get_num_groups(); ++a) {
973+
m.parameters.template get<DailyPartialVaccinations<FP>>()[{a, d}] = 0.0;
974+
m.parameters.template get<DailyFullVaccinations<FP>>()[{a, d}] = 0.0;
975+
m.parameters.template get<DailyBoosterVaccinations<FP>>()[{a, d}] = 0.0;
976+
}
977+
}
978+
}
979+
return success();
980+
}
955981
BOOST_OUTCOME_TRY(auto&& vacc_data, read_vaccination_data(path));
956982
BOOST_OUTCOME_TRY(set_vaccination_data(model, vacc_data, date, vregion, num_days));
957983
return success();

cpp/models/ode_secirvvs/parameters_io.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,17 @@ IOResult<void> set_vaccination_data(std::vector<Model<FP>>& model, const std::ve
727727
}
728728
auto max_date = max_date_entry->date;
729729

730+
auto min_date_entry = std::min_element(vacc_data.begin(), vacc_data.end(), [](auto&& a, auto&& b) {
731+
return a.date < b.date;
732+
});
733+
auto min_date = min_date_entry->date;
734+
if (min_date > date || max_date < offset_date_by_days(date, num_days)) {
735+
log_warning("Vaccination data only available from {} to {}. "
736+
"For days before, vaccination data will be set to 0. For days after, "
737+
"vaccination data will be set to the last available date.",
738+
min_date, max_date);
739+
}
740+
730741
for (auto&& vacc_data_entry : vacc_data) {
731742
auto it = std::find_if(vregion.begin(), vregion.end(), [&vacc_data_entry](auto&& r) {
732743
return r == 0 || (vacc_data_entry.county_id && vacc_data_entry.county_id == regions::CountyId(r)) ||
@@ -833,6 +844,26 @@ template <typename FP = double>
833844
IOResult<void> set_vaccination_data(std::vector<Model<FP>>& model, const std::string& path, Date date,
834845
const std::vector<int>& vregion, int num_days)
835846
{
847+
// Check if vaccination data is available for the given date range
848+
auto end_date = offset_date_by_days(date, num_days);
849+
if (!is_vaccination_data_available(date, end_date)) {
850+
log_warning("No vaccination data available in range from {} to {}. "
851+
"Vaccination data will be set to 0.",
852+
date, end_date);
853+
// Set vaccination data to 0 for all models
854+
for (auto& m : model) {
855+
m.parameters.template get<DailyPartialVaccinations<FP>>().resize(SimulationDay(num_days + 1));
856+
m.parameters.template get<DailyFullVaccinations<FP>>().resize(SimulationDay(num_days + 1));
857+
858+
for (auto d = SimulationDay(0); d < SimulationDay(num_days + 1); ++d) {
859+
for (auto a = AgeGroup(0); a < m.parameters.get_num_groups(); ++a) {
860+
m.parameters.template get<DailyPartialVaccinations<FP>>()[{a, d}] = 0.0;
861+
m.parameters.template get<DailyFullVaccinations<FP>>()[{a, d}] = 0.0;
862+
}
863+
}
864+
}
865+
return success();
866+
}
836867
BOOST_OUTCOME_TRY(auto&& vacc_data, read_vaccination_data(path));
837868
BOOST_OUTCOME_TRY(set_vaccination_data(model, vacc_data, date, vregion, num_days));
838869
return success();

cpp/tests/test_epi_data_io.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,28 @@ TEST(TestEpiDataIo, read_divi_data)
300300
TEST(TestEpiDataIo, is_divi_data_available)
301301
{
302302
EXPECT_FALSE(mio::is_divi_data_available(mio::Date(2020, 4, 22))); // Before start
303-
EXPECT_FALSE(mio::is_divi_data_available(mio::Date(2024, 7, 22))); // After end
304303
EXPECT_TRUE(mio::is_divi_data_available(mio::Date(2020, 4, 23))); // Start date
305-
EXPECT_TRUE(mio::is_divi_data_available(mio::Date(2022, 1, 1))); // Inside range
306-
EXPECT_TRUE(mio::is_divi_data_available(mio::Date(2024, 7, 21))); // End date
304+
EXPECT_TRUE(mio::is_divi_data_available(mio::Date(2022, 1, 1))); // Day after start
305+
}
306+
307+
TEST(TestEpiDataIo, is_vaccination_data_available)
308+
{
309+
// Single date tests
310+
EXPECT_FALSE(mio::is_vaccination_data_available(mio::Date(2020, 12, 26), mio::Date(2020, 12, 26))); // Before start
311+
EXPECT_TRUE(mio::is_vaccination_data_available(mio::Date(2020, 12, 27), mio::Date(2020, 12, 27))); // Start date
312+
EXPECT_TRUE(mio::is_vaccination_data_available(mio::Date(2024, 7, 9), mio::Date(2024, 7, 9))); // End date
313+
EXPECT_FALSE(mio::is_vaccination_data_available(mio::Date(2024, 7, 10), mio::Date(2024, 7, 10))); // After end
314+
315+
// Date range tests
316+
EXPECT_TRUE(
317+
mio::is_vaccination_data_available(mio::Date(2020, 12, 20), mio::Date(2020, 12, 28))); // Overlap with start
318+
EXPECT_TRUE(mio::is_vaccination_data_available(mio::Date(2024, 7, 5), mio::Date(2024, 7, 15))); // Overlap with end
319+
EXPECT_TRUE(
320+
mio::is_vaccination_data_available(mio::Date(2021, 1, 1), mio::Date(2021, 12, 31))); // Completely within range
321+
EXPECT_TRUE(
322+
mio::is_vaccination_data_available(mio::Date(2020, 1, 1), mio::Date(2025, 1, 1))); // Contains entire range
323+
EXPECT_FALSE(mio::is_vaccination_data_available(mio::Date(2019, 1, 1), mio::Date(2020, 12, 26))); // Before start
324+
EXPECT_FALSE(mio::is_vaccination_data_available(mio::Date(2024, 7, 10), mio::Date(2025, 1, 1))); // After end
307325
}
308326

309327
TEST(TestEpiDataIo, read_confirmed_cases_data)

cpp/tests/test_odesecir.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,11 +1454,6 @@ TEST(TestOdeSecir, set_divi_data_invalid_dates)
14541454
EXPECT_THAT(print_wrap(model_vector[0].populations.array().cast<double>()),
14551455
MatrixNear(print_wrap(model.populations.array().cast<double>()), 1e-10, 1e-10));
14561456

1457-
// Test with data after DIVI dataset was no longer updated.
1458-
EXPECT_THAT(mio::osecir::details::set_divi_data(model_vector, "", {1001}, {2025, 12, 01}, 1.0), IsSuccess());
1459-
EXPECT_THAT(print_wrap(model_vector[0].populations.array().cast<double>()),
1460-
MatrixNear(print_wrap(model.populations.array().cast<double>()), 1e-10, 1e-10));
1461-
14621457
mio::set_log_level(mio::LogLevel::warn);
14631458
}
14641459

@@ -1479,7 +1474,7 @@ TEST_F(ModelTestOdeSecir, set_confirmed_cases_data_with_ICU)
14791474

14801475
// Change dates of the case data so that no ICU data is available at that time.
14811476
// Also, increase the number of confirmed cases by 1 each day.
1482-
const auto t0 = mio::Date(2025, 1, 1);
1477+
const auto t0 = mio::Date(2000, 1, 1);
14831478
auto day_add = 0;
14841479
for (auto& entry : case_data) {
14851480
entry.date = offset_date_by_days(t0, day_add);

cpp/tests/test_odesecirts.cpp

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -768,12 +768,6 @@ TEST(TestOdeSECIRTS, set_divi_data_invalid_dates)
768768
// Assure that populations is the same as before.
769769
EXPECT_THAT(print_wrap(model_vector[0].populations.array().cast<double>()),
770770
MatrixNear(print_wrap(model.populations.array().cast<double>()), 1e-10, 1e-10));
771-
772-
// Test with data after DIVI dataset was no longer updated.
773-
EXPECT_THAT(mio::osecirts::details::set_divi_data(model_vector, "", {1001}, {2025, 12, 01}, 1.0), IsSuccess());
774-
EXPECT_THAT(print_wrap(model_vector[0].populations.array().cast<double>()),
775-
MatrixNear(print_wrap(model.populations.array().cast<double>()), 1e-10, 1e-10));
776-
777771
mio::set_log_level(mio::LogLevel::warn);
778772
}
779773

@@ -802,7 +796,7 @@ TEST(TestOdeSECIRTS, set_confirmed_cases_data_with_ICU)
802796

803797
// Change dates of the case data so that no ICU data is available at that time.
804798
// Also, increase the number of confirmed cases by 1 each day.
805-
const auto t0 = mio::Date(2025, 1, 1);
799+
const auto t0 = mio::Date(2000, 1, 1);
806800
auto day_add = 0;
807801
for (auto& entry : case_data) {
808802
entry.date = offset_date_by_days(t0, day_add);
@@ -1079,6 +1073,91 @@ TEST(TestOdeSECIRTS, model_initialization)
10791073
MatrixNear(print_wrap(expected_values), 1e-5, 1e-5));
10801074
}
10811075

1076+
TEST(TestOdeSECIRTS, set_vaccination_data_not_avail)
1077+
{
1078+
const auto num_age_groups = 2;
1079+
const auto num_days = 5;
1080+
mio::osecirts::Model<double> model(num_age_groups);
1081+
std::vector<mio::osecirts::Model<double>> model_vector = {model};
1082+
1083+
// Setup initial non-zero vaccination data
1084+
auto& params = model_vector[0].parameters;
1085+
params.get<mio::osecirts::DailyPartialVaccinations<double>>().resize(mio::SimulationDay(num_days + 1));
1086+
params.get<mio::osecirts::DailyFullVaccinations<double>>().resize(mio::SimulationDay(num_days + 1));
1087+
params.get<mio::osecirts::DailyBoosterVaccinations<double>>().resize(mio::SimulationDay(num_days + 1));
1088+
1089+
const double initial_partial_vacc_val = 10.0;
1090+
const double initial_full_vacc_val = 5.0;
1091+
const double initial_booster_vacc_val = 2.0;
1092+
1093+
for (auto g = mio::AgeGroup(0); g < mio::AgeGroup(num_age_groups); ++g) {
1094+
for (auto d = mio::SimulationDay(0); d < mio::SimulationDay(num_days + 1); ++d) {
1095+
params.get<mio::osecirts::DailyPartialVaccinations<double>>()[{g, d}] = initial_partial_vacc_val;
1096+
params.get<mio::osecirts::DailyFullVaccinations<double>>()[{g, d}] = initial_full_vacc_val;
1097+
params.get<mio::osecirts::DailyBoosterVaccinations<double>>()[{g, d}] = initial_booster_vacc_val;
1098+
}
1099+
}
1100+
1101+
// Call set_vaccination_data with an unavailable date
1102+
mio::Date unavailable_date(2019, 1, 1); // Date before vaccinations started
1103+
std::vector<int> region = {1001};
1104+
std::string any_path = "dummy_vacc_path.json";
1105+
1106+
auto result =
1107+
mio::osecirts::details::set_vaccination_data(model_vector, any_path, unavailable_date, region, num_days);
1108+
1109+
ASSERT_THAT(result, IsSuccess());
1110+
1111+
// Check that vaccinations are set to zero for all days and age groups
1112+
for (auto d = mio::SimulationDay(0); d < mio::SimulationDay(num_days + 1); ++d) {
1113+
for (auto a = mio::AgeGroup(0); a < mio::AgeGroup(num_age_groups); ++a) {
1114+
auto partial_vacc = params.get<mio::osecirts::DailyPartialVaccinations<double>>()[{a, d}];
1115+
auto full_vacc = params.get<mio::osecirts::DailyFullVaccinations<double>>()[{a, d}];
1116+
auto booster_vacc = params.get<mio::osecirts::DailyBoosterVaccinations<double>>()[{a, d}];
1117+
EXPECT_NEAR(partial_vacc, 0.0, 1e-10);
1118+
EXPECT_NEAR(full_vacc, 0.0, 1e-10);
1119+
EXPECT_NEAR(booster_vacc, 0.0, 1e-10);
1120+
}
1121+
}
1122+
}
1123+
1124+
TEST(TestOdeSECIRTS, set_vaccination_data_min_date_not_avail)
1125+
{
1126+
mio::set_log_level(mio::LogLevel::off);
1127+
1128+
// create model
1129+
const auto num_age_groups = 1;
1130+
const auto num_days = 5;
1131+
mio::osecirts::Model<double> model(num_age_groups);
1132+
std::vector<mio::osecirts::Model<double>> model_vector = {model};
1133+
auto& params = model_vector[0].parameters;
1134+
1135+
// create simple vaccination data
1136+
std::vector<mio::VaccinationDataEntry> vacc_data = {
1137+
mio::VaccinationDataEntry{10.0, 5.0, 2.0, 0.0, mio::Date(2021, 1, 15), mio::AgeGroup(0), {}, {}, {}}};
1138+
1139+
// simulation date before vaccination data
1140+
mio::Date earlier_date(2021, 1, 1);
1141+
std::vector<int> region = {0};
1142+
1143+
auto result = mio::osecirts::details::set_vaccination_data(model_vector, vacc_data, earlier_date, region, num_days);
1144+
ASSERT_THAT(result, IsSuccess());
1145+
1146+
// check that vaccinations are set to zero for all days and age groups
1147+
for (auto d = mio::SimulationDay(0); d < mio::SimulationDay(num_days + 1); ++d) {
1148+
for (auto a = mio::AgeGroup(0); a < mio::AgeGroup(num_age_groups); ++a) {
1149+
auto partial_vacc = params.get<mio::osecirts::DailyPartialVaccinations<double>>()[{a, d}];
1150+
auto full_vacc = params.get<mio::osecirts::DailyFullVaccinations<double>>()[{a, d}];
1151+
auto booster_vacc = params.get<mio::osecirts::DailyBoosterVaccinations<double>>()[{a, d}];
1152+
EXPECT_NEAR(partial_vacc, 0.0, 1e-10);
1153+
EXPECT_NEAR(full_vacc, 0.0, 1e-10);
1154+
EXPECT_NEAR(booster_vacc, 0.0, 1e-10);
1155+
}
1156+
}
1157+
1158+
mio::set_log_level(mio::LogLevel::warn);
1159+
}
1160+
10821161
#endif
10831162

10841163
TEST(TestOdeSECIRTS, parameter_percentiles)

0 commit comments

Comments
 (0)