Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions cpp/memilio/io/epi_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,58 @@ class StringDate : public Date
}
};

/**
* Represents the entries of a confirmed cases data file, e.g., from RKI.
* Number of confirmed, recovered and deceased in Germany on a specific date.
* ConfirmedCasesNoAgeEntry is a simplified version of ConfirmedCasesDataEntry without agegroups.
*/
class ConfirmedCasesNoAgeEntry
{
public:
double num_confirmed;
double num_recovered;
double num_deaths;
Date date;

template <class IOContext>
static IOResult<ConfirmedCasesNoAgeEntry> deserialize(IOContext& io)
{
auto obj = io.expect_object("ConfirmedCasesNoAgeEntry");
auto num_confirmed = obj.expect_element("Confirmed", Tag<double>{});
auto num_recovered = obj.expect_element("Recovered", Tag<double>{});
auto num_deaths = obj.expect_element("Deaths", Tag<double>{});
auto date = obj.expect_element("Date", Tag<StringDate>{});
return apply(
io,
[](auto&& nc, auto&& nr, auto&& nd, auto&& d) {
return ConfirmedCasesNoAgeEntry{nc, nr, nd, d};
},
num_confirmed, num_recovered, num_deaths, date);
}
};

/**
* Deserialize a list of ConfirmedCasesNoAgeEntry from json.
* @param jsvalue json value, must be an array of objects, objects must match ConfirmedCasesNoAgeEntry.
* Accordingly, there should be one entry per date with the values for Confirmed, Recovered and Deaths. In addition, no age groups should be specified.
* @return List of ConfirmedCasesNoAgeEntry.
*/
inline IOResult<std::vector<ConfirmedCasesNoAgeEntry>> deserialize_confirmed_cases_noage(const Json::Value& jsvalue)
{
return deserialize_json(jsvalue, Tag<std::vector<ConfirmedCasesNoAgeEntry>>{});
}

/**
* Read list of ConfirmedCasesNoAgeEntry from a json file.
* @param filename name of the json file. File content must be an array of objects, objects must match ConfirmedCasesNoAgeEntry.
* Accordingly, there should be one entry per date with the values for Confirmed, Recovered and Deaths. In addition, no age groups should be specified.
* @return List of ConfirmedCasesNoAgeEntry.
*/
inline IOResult<std::vector<ConfirmedCasesNoAgeEntry>> read_confirmed_cases_noage(const std::string& filename)
{
return read_json(filename, Tag<std::vector<ConfirmedCasesNoAgeEntry>>{});
}

/**
* Represents the entries of a confirmed cases data file, e.g., from RKI.
* Number of confirmed, recovered and deceased in a region on a specific date.
Expand Down
1 change: 1 addition & 0 deletions cpp/models/lct_secir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_library(lct_secir
model.cpp
simulation.h
parameters.h
parameters_io.h
)
target_link_libraries(lct_secir PUBLIC memilio)
target_include_directories(lct_secir PUBLIC
Expand Down
8 changes: 6 additions & 2 deletions cpp/models/lct_secir/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

This model is based on the Linear Chain Trick.

The Linear Chain Trick provides the option to use Erlang-distributed sojourn times in the compartments through the use of subcompartments.
The normal ODE models have (possibly unrealistic) exponentially distributed sojourn times.
The Linear Chain Trick provides the option to use Erlang-distributed stay times in the compartments through the use of subcompartments.
The normal ODE models have (possibly unrealistic) exponentially distributed stay times.
The LCT model can still be described by an ordinary differential equation system.

For the concept see
Expand Down Expand Up @@ -57,3 +57,7 @@ The notation of the compartments with indices here stands for subcompartments an

A simple example can be found at [LCT minimal example](../../examples/lct_secir.cpp).

## Initialization

- The file [parameters_io](parameters_io.h) provides functionality to compute an initial value vector for the LCT-SECIR model based on real data.

11 changes: 11 additions & 0 deletions cpp/models/lct_secir/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,17 @@ class Model
return m_initial_values;
}

/**
* @brief Sets the initial values for the model.
*
* @param[in] init Vector with initial values for all infection states inclusive subcompartments.
*/
void set_initial_values(Eigen::VectorXd init)
{
m_initial_values = init;
m_N0 = m_initial_values.sum();
}

Parameters parameters{}; ///< Parameters of the model.

private:
Expand Down
356 changes: 356 additions & 0 deletions cpp/models/lct_secir/parameters_io.h

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ if(MEMILIO_HAS_JSONCPP)
set(TESTSOURCES ${TESTSOURCES}
test_json_serializer.cpp
test_epi_data_io.cpp
test_lct_parameters_io.cpp
)
endif()

Expand Down
92 changes: 92 additions & 0 deletions cpp/tests/data/cases_all_germany.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
[
{
"Date": "2020-05-24",
"Confirmed": 1.2,
"Deaths": 0.5,
"Recovered": 0
},
{
"Date": "2020-05-25",
"Confirmed": 5,
"Deaths": 2.0,
"Recovered": 2.5
},
{
"Date": "2020-05-26",
"Confirmed": 20.2,
"Deaths": 5.01,
"Recovered": 3.0
},
{
"Date": "2020-05-27",
"Confirmed": 25.6,
"Deaths": 6,
"Recovered": 3.0
},
{
"Date": "2020-05-28",
"Confirmed": 27.6,
"Deaths": 6,
"Recovered": 3.0
},
{
"Date": "2020-05-29",
"Confirmed": 30.6,
"Deaths": 6,
"Recovered": 3.0
},
{
"Date": "2020-05-30",
"Confirmed": 30.6,
"Deaths": 6,
"Recovered": 3.0
},
{
"Date": "2020-05-31",
"Confirmed": 35,
"Deaths": 6,
"Recovered": 3.0
},
{
"Date": "2020-06-01",
"Confirmed": 44,
"Deaths": 8,
"Recovered": 3.0
},
{
"Date": "2020-06-02",
"Confirmed": 44.5,
"Deaths": 8.999,
"Recovered": 6
},
{
"Date": "2020-06-03",
"Confirmed": 70,
"Deaths": 9,
"Recovered": 6
},
{
"Date": "2020-06-04",
"Confirmed": 100,
"Deaths": 20,
"Recovered": 6
},
{
"Date": "2020-06-05",
"Confirmed": 100.3,
"Deaths": 23.56,
"Recovered": 6
},
{
"Date": "2020-06-06",
"Confirmed": 115,
"Deaths": 24,
"Recovered": 6
},
{
"Date": "2020-06-07",
"Confirmed": 120.6,
"Deaths": 30,
"Recovered": 6
}
]
1 change: 1 addition & 0 deletions cpp/tests/data/test_empty_file.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
58 changes: 58 additions & 0 deletions cpp/tests/test_epi_data_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,36 @@ TEST(TestEpiDataIo, read_rki_error_age)
ASSERT_THAT(print_wrap(result), IsFailure(mio::StatusCode::InvalidValue));
}

TEST(TestEpiDataIo, read_confirmed_cases_noage)
{
Json::Value js(Json::arrayValue);
js[0]["Date"] = "2021-12-01";
js[0]["Confirmed"] = 1;
js[0]["Deaths"] = 2;
js[0]["Recovered"] = 3;

js[1]["Date"] = "2021-12-02";
js[1]["Confirmed"] = 4;
js[1]["Deaths"] = 5;
js[1]["Recovered"] = 6;

auto result = mio::deserialize_confirmed_cases_noage(js);
ASSERT_THAT(print_wrap(result), IsSuccess());

auto rki_data_noage = result.value();
ASSERT_EQ(rki_data_noage.size(), 2);

ASSERT_EQ(rki_data_noage[0].date, mio::Date(2021, 12, 1));
ASSERT_EQ(rki_data_noage[0].num_confirmed, 1);
ASSERT_EQ(rki_data_noage[0].num_deaths, 2);
ASSERT_EQ(rki_data_noage[0].num_recovered, 3);

ASSERT_EQ(rki_data_noage[1].date, mio::Date(2021, 12, 2));
ASSERT_EQ(rki_data_noage[1].num_confirmed, 4);
ASSERT_EQ(rki_data_noage[1].num_deaths, 5);
ASSERT_EQ(rki_data_noage[1].num_recovered, 6);
}

TEST(TestEpiDataIo, read_divi)
{
Json::Value js(Json::arrayValue);
Expand Down Expand Up @@ -301,6 +331,34 @@ TEST(TestEpiDataIo, read_confirmed_cases_data)
ASSERT_EQ(case_data[2].state_id, boost::none);
}

TEST(TestEpiDataIo, read_confirmed_cases_noage_data)
{
auto rki_data_noage =
mio::read_confirmed_cases_noage(mio::path_join(TEST_DATA_DIR, "cases_all_germany.json")).value();

ASSERT_EQ(rki_data_noage.size(), 15);

ASSERT_EQ(rki_data_noage[0].date, mio::Date(2020, 05, 24));
ASSERT_EQ(rki_data_noage[0].num_confirmed, 1.2);
ASSERT_EQ(rki_data_noage[0].num_deaths, 0.5);
ASSERT_EQ(rki_data_noage[0].num_recovered, 0);

ASSERT_EQ(rki_data_noage[8].date, mio::Date(2020, 06, 01));
ASSERT_EQ(rki_data_noage[8].num_confirmed, 44);
ASSERT_EQ(rki_data_noage[8].num_deaths, 8);
ASSERT_EQ(rki_data_noage[8].num_recovered, 3);

ASSERT_EQ(rki_data_noage[9].date, mio::Date(2020, 06, 02));
ASSERT_EQ(rki_data_noage[9].num_confirmed, 44.5);
ASSERT_EQ(rki_data_noage[9].num_deaths, 8.999);
ASSERT_EQ(rki_data_noage[9].num_recovered, 6);

ASSERT_EQ(rki_data_noage[14].date, mio::Date(2020, 06, 07));
ASSERT_EQ(rki_data_noage[14].num_confirmed, 120.6);
ASSERT_EQ(rki_data_noage[14].num_deaths, 30);
ASSERT_EQ(rki_data_noage[14].num_recovered, 6);
}

TEST(TestEpiDataIO, read_vaccination_data)
{
auto vacc_data = mio::read_vaccination_data(mio::path_join(TEST_DATA_DIR, "test_all_ageinf_vacc.json")).value();
Expand Down
Loading