Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
25db4dc
set up simulation for germany without age groups
charlie0614 Jul 16, 2025
1c25e18
create training data
charlie0614 Jul 17, 2025
6ee021f
[ci skip] add first draft for nuts1 simulation of germany
charlie0614 Jul 22, 2025
284d5ac
[ci skip] aggregate population data to states
charlie0614 Jul 22, 2025
3f6629d
changes for fitting and region-specific damping
charlie0614 Jul 31, 2025
152d268
Merge branch '1315-support-io-without-age-groups' of github.com:SciCo…
charlie0614 Jul 31, 2025
f45f9c3
move to unicorn
charlie0614 Jul 31, 2025
8fb9fe2
changes for germany and states simulation
charlie0614 Aug 5, 2025
2dbe477
first draft germany simulation
charlie0614 Aug 6, 2025
c4d7cab
update scripts for fitting
charlie0614 Aug 8, 2025
4f1c799
Merge branch '1315-support-io-without-age-groups' of github.com:SciCo…
charlie0614 Aug 8, 2025
67b5a6d
try for modeling spain
charlie0614 Aug 12, 2025
e93dced
change starting date
charlie0614 Aug 12, 2025
19f7bc8
comment everything out for spain
charlie0614 Aug 12, 2025
2d0dda4
made more errors
charlie0614 Aug 12, 2025
65f63c0
fix some again
charlie0614 Aug 12, 2025
36002bd
builds but cannot be imported
charlie0614 Aug 12, 2025
837b331
fix read data without age groups
charlie0614 Aug 13, 2025
c4e283d
ready for trainingsdata creation
charlie0614 Aug 13, 2025
4d4bd83
save all regions and handle summary_variables keys different
charlie0614 Aug 14, 2025
f235683
changes for fitting countylvl
charlie0614 Aug 14, 2025
349376f
Merge branch 'main' into 1315-support-io-without-age-groups
charlie0614 Aug 14, 2025
6a45444
[ci skip] sort provincias
HenrZu Aug 15, 2025
54548ee
rm no age functions for io
HenrZu Aug 15, 2025
a5ab06b
simple example
HenrZu Aug 15, 2025
8732180
lot of tests
HenrZu Aug 18, 2025
1c537a6
merge from fork
HenrZu Aug 18, 2025
eb77e16
rm from example cmakelist
HenrZu Aug 18, 2025
e20f283
better place for asserts
HenrZu Aug 18, 2025
f4933f6
Merge branch '1315-io-with-single-age-group-v2' into 1315-support-io-…
charlie0614 Aug 21, 2025
6870b4c
try new io without age groups
charlie0614 Aug 21, 2025
79f8e51
rm hardcoded rki_age_group
charlie0614 Aug 21, 2025
5483e52
[ci skip] working germany examples
charlie0614 Aug 21, 2025
bde63e4
Apply suggestions from code review
HenrZu Aug 22, 2025
39691f9
rm code tests, formats param io
HenrZu Aug 22, 2025
afe10d9
lower tol
HenrZu Aug 22, 2025
43d44a3
[ci skip] remove comments
charlie0614 Aug 25, 2025
02688ac
add covid parameters to estimation
charlie0614 Aug 26, 2025
67468af
only relevant inits
HenrZu Aug 26, 2025
1e8007c
[ci skip] improve fetch spain data
charlie0614 Aug 26, 2025
4b5dbdc
[ci skip] fix get spanish icu data
charlie0614 Aug 26, 2025
e4a930b
add get case data
charlie0614 Aug 27, 2025
2f55a44
remove comments
charlie0614 Aug 27, 2025
fee12c7
remove writing of graph
charlie0614 Aug 27, 2025
845a84d
format + create dir
HenrZu Aug 27, 2025
44e19b0
Merge branch '1315-support-io-without-age-groups' of github.com:SciCo…
charlie0614 Aug 27, 2025
08be57e
Merge branch '1315-io-with-single-age-group-v2' into 1315-support-io-…
charlie0614 Aug 27, 2025
9281a00
format
charlie0614 Aug 27, 2025
d64af34
format
charlie0614 Aug 27, 2025
6a900bc
format
charlie0614 Aug 27, 2025
4271e61
fix reading spanish population data
charlie0614 Aug 27, 2025
3f61e48
read spanish pop
HenrZu Aug 27, 2025
aa33602
Merge branch '1315-support-io-without-age-groups' of github.com:SciCo…
HenrZu Aug 27, 2025
ea6f566
fix init pop for provincias
HenrZu Aug 27, 2025
715c711
spanish divi and case data as input for secir model
HenrZu Aug 27, 2025
e389c33
Merge branch 'main' into 1315-support-io-without-age-groups
charlie0614 Aug 27, 2025
ab44aeb
[ci skip] bindings for read_input_data_provincias
HenrZu Aug 28, 2025
6487b72
get mobility data
charlie0614 Aug 28, 2025
fc55a8d
[ci skip] accumulated cases
HenrZu Aug 28, 2025
3d74140
no uncertainty in pop
HenrZu Aug 28, 2025
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
2 changes: 2 additions & 0 deletions cpp/memilio/geography/regions.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ DECL_TYPESAFE(int, CountyId);

DECL_TYPESAFE(int, DistrictId);

DECL_TYPESAFE(int, ProvinciaId);

/**
* get the id of the state that the specified county is in.
* @param[in, out] county a county id.
Expand Down
18 changes: 14 additions & 4 deletions cpp/memilio/io/epi_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ namespace mio

std::vector<const char*> ConfirmedCasesDataEntry::age_group_names = {"A00-A04", "A05-A14", "A15-A34",
"A35-A59", "A60-A79", "A80+"};

std::vector<const char*> PopulationDataEntry::age_group_names = {
std::vector<const char*> PopulationDataEntry::age_group_names = {
"<3 years", "3-5 years", "6-14 years", "15-17 years", "18-24 years", "25-29 years",
"30-39 years", "40-49 years", "50-64 years", "65-74 years", ">74 years"};
std::vector<const char*> PopulationDataEntrySpain::age_group_names = {"Population"};

std::vector<const char*> VaccinationDataEntry::age_group_names = {"0-4", "5-14", "15-34", "35-59", "60-79", "80-99"};

Expand All @@ -49,11 +49,14 @@ IOResult<std::vector<int>> get_node_ids(const std::string& path, bool is_node_fo
}
}
else {
if (entry.district_id) {
if (entry.state_id) {
id.push_back(entry.state_id->get());
}
else if (entry.district_id) {
id.push_back(entry.district_id->get());
}
else {
return failure(StatusCode::InvalidValue, "Population data file is missing district ids.");
return failure(StatusCode::InvalidValue, "Population data file is missing district and state ids.");
}
}
}
Expand All @@ -62,6 +65,13 @@ IOResult<std::vector<int>> get_node_ids(const std::string& path, bool is_node_fo
id.erase(std::unique(id.begin(), id.end()), id.end());
return success(id);
}

IOResult<std::vector<int>> get_country_id(const std::string& /*path*/, bool /*is_node_for_county*/,
bool /*rki_age_groups*/)
{
std::vector<int> id = {0};
return success(id);
}
} // namespace mio

#endif //MEMILIO_HAS_JSONCPP
107 changes: 91 additions & 16 deletions cpp/memilio/io/epi_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ class ConfirmedCasesNoAgeEntry
double num_recovered;
double num_deaths;
Date date;
boost::optional<regions::StateId> state_id;
boost::optional<regions::CountyId> county_id;
boost::optional<regions::DistrictId> district_id;

template <class IOContext>
static IOResult<ConfirmedCasesNoAgeEntry> deserialize(IOContext& io)
Expand All @@ -86,12 +89,15 @@ class ConfirmedCasesNoAgeEntry
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>{});
auto state_id = obj.expect_optional("ID_State", Tag<regions::StateId>{});
auto county_id = obj.expect_optional("ID_County", Tag<regions::CountyId>{});
auto district_id = obj.expect_optional("ID_District", Tag<regions::DistrictId>{});
return apply(
io,
[](auto&& nc, auto&& nr, auto&& nd, auto&& d) {
return ConfirmedCasesNoAgeEntry{nc, nr, nd, d};
[](auto&& nc, auto&& nr, auto&& nd, auto&& d, auto&& sid, auto&& cid, auto&& did) {
return ConfirmedCasesNoAgeEntry{nc, nr, nd, d, sid, cid, did};
},
num_confirmed, num_recovered, num_deaths, date);
num_confirmed, num_recovered, num_deaths, date, state_id, county_id, district_id);
}
};

Expand Down Expand Up @@ -142,29 +148,42 @@ class ConfirmedCasesDataEntry
{
auto obj = io.expect_object("ConfirmedCasesDataEntry");
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 num_recovered = obj.expect_optional("Recovered", Tag<double>{});
auto num_deaths = obj.expect_optional("Deaths", Tag<double>{});
auto date = obj.expect_element("Date", Tag<StringDate>{});
auto age_group_str = obj.expect_element("Age_RKI", Tag<std::string>{});
auto age_group_str = obj.expect_optional("Age_RKI", Tag<std::string>{});
auto state_id = obj.expect_optional("ID_State", Tag<regions::StateId>{});
auto county_id = obj.expect_optional("ID_County", Tag<regions::CountyId>{});
auto district_id = obj.expect_optional("ID_District", Tag<regions::DistrictId>{});
return apply(
io,
[](auto&& nc, auto&& nr, auto&& nd, auto&& d, auto&& a_str, auto&& sid, auto&& cid,
[](auto&& nc, auto&& nr, auto&& nd, auto&& d, auto&& a_str_opt, auto&& sid, auto&& cid,
auto&& did) -> IOResult<ConfirmedCasesDataEntry> {
auto a = AgeGroup(0);
auto it = std::find(age_group_names.begin(), age_group_names.end(), a_str);
if (it != age_group_names.end()) {
a = AgeGroup(size_t(it - age_group_names.begin()));
}
else if (a_str == "unknown") {
a = AgeGroup(age_group_names.size());
auto a = AgeGroup(0); // default if Age_RKI missing

// if no age group is given, use "total" as name
if (!a_str_opt) {
if (age_group_names.size() != 1) {
age_group_names.clear();
age_group_names.push_back("total");
}
}
else {
return failure(StatusCode::InvalidValue, "Invalid confirmed cases data age group.");
const auto& a_str = *a_str_opt;
auto it = std::find(age_group_names.begin(), age_group_names.end(), a_str);
if (it != age_group_names.end()) {
a = AgeGroup(size_t(it - age_group_names.begin()));
}
else if (a_str == "unknown") {
a = AgeGroup(age_group_names.size());
}
else {
return failure(StatusCode::InvalidValue, "Invalid confirmed cases data age group.");
}
}
return success(ConfirmedCasesDataEntry{nc, nr, nd, d, a, sid, cid, did});
double nrec = nr ? *nr : 0.0;
double ndead = nd ? *nd : 0.0;
return success(ConfirmedCasesDataEntry{nc, nrec, ndead, d, a, sid, cid, did});
},
num_confirmed, num_recovered, num_deaths, date, age_group_str, state_id, county_id, district_id);
}
Expand Down Expand Up @@ -331,6 +350,35 @@ class PopulationDataEntry
}
};

class PopulationDataEntrySpain
{
public:
static std::vector<const char*> age_group_names;

CustomIndexArray<double, AgeGroup> population;
boost::optional<regions::ProvinciaId> provincia_id;

template <class IoContext>
static IOResult<PopulationDataEntrySpain> deserialize(IoContext& io)
{
auto obj = io.expect_object("PopulationDataEntrySpain");
auto provincia = obj.expect_optional("ID_Provincia", Tag<regions::ProvinciaId>{});
std::vector<IOResult<double>> age_groups;
age_groups.reserve(age_group_names.size());
std::transform(age_group_names.begin(), age_group_names.end(), std::back_inserter(age_groups),
[&obj](auto&& age_name) {
return obj.expect_element(age_name, Tag<double>{});
});
return apply(
io,
[](auto&& ag, auto&& pid) {
return PopulationDataEntrySpain{
CustomIndexArray<double, AgeGroup>(AgeGroup(ag.size()), ag.begin(), ag.end()), pid};
},
details::unpack_all(age_groups), provincia);
}
};

namespace details
{
inline void get_rki_age_interpolation_coefficients(const std::vector<double>& age_ranges,
Expand Down Expand Up @@ -435,6 +483,19 @@ inline IOResult<std::vector<PopulationDataEntry>> deserialize_population_data(co
}
}

/**
* Deserialize population data from a JSON value.
* Age groups are interpolated to RKI age groups.
* @param jsvalue JSON value that contains the population data.
* @param rki_age_groups Specifies whether population data should be interpolated to rki age groups.
* @return list of population data.
*/
inline IOResult<std::vector<PopulationDataEntrySpain>> deserialize_population_data_spain(const Json::Value& jsvalue)
{
BOOST_OUTCOME_TRY(auto&& population_data, deserialize_json(jsvalue, Tag<std::vector<PopulationDataEntrySpain>>{}));
return success(population_data);
}

/**
* Deserialize population data from a JSON file.
* Age groups are interpolated to RKI age groups.
Expand All @@ -448,6 +509,18 @@ inline IOResult<std::vector<PopulationDataEntry>> read_population_data(const std
return deserialize_population_data(jsvalue, rki_age_group);
}

/**
* Deserialize population data from a JSON file.
* Age groups are interpolated to RKI age groups.
* @param filename JSON file that contains the population data.
* @return list of population data.
*/
inline IOResult<std::vector<PopulationDataEntrySpain>> read_population_data_spain(const std::string& filename)
{
BOOST_OUTCOME_TRY(auto&& jsvalue, read_json(filename));
return deserialize_population_data_spain(jsvalue);
}

/**
* @brief Sets the age groups' names for the ConfirmedCasesDataEntry%s.
* @param[in] names age group names
Expand All @@ -474,6 +547,8 @@ IOResult<void> set_vaccination_data_age_group_names(std::vector<const char*> nam
* @return list of node ids.
*/
IOResult<std::vector<int>> get_node_ids(const std::string& path, bool is_node_for_county, bool rki_age_groups = true);
IOResult<std::vector<int>> get_country_id(const std::string& /*path*/, bool /*is_node_for_county*/,
bool /*rki_age_groups*/ = true);

/**
* Represents an entry in a vaccination data file.
Expand Down
32 changes: 32 additions & 0 deletions cpp/memilio/io/parameters_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,43 @@ IOResult<std::vector<std::vector<double>>> read_population_data(const std::vecto
return success(vnum_population);
}

IOResult<std::vector<std::vector<double>>>
read_population_data_spain(const std::vector<PopulationDataEntrySpain>& population_data,
const std::vector<int>& vregion)
{
std::vector<std::vector<double>> vnum_population(vregion.size(), std::vector<double>(1, 0.0));

for (auto&& provincia_entry : population_data) {
if (provincia_entry.provincia_id) {
for (size_t idx = 0; idx < vregion.size(); ++idx) {
if (vregion[idx] == provincia_entry.provincia_id->get()) {
vnum_population[idx][0] += provincia_entry.population[AgeGroup(0)];
}
}
}
// id 0 means the whole country
for (size_t idx = 0; idx < vregion.size(); ++idx) {
if (vregion[idx] == 0) {
vnum_population[idx][0] += provincia_entry.population[AgeGroup(0)];
}
}
}

return success(vnum_population);
}

IOResult<std::vector<std::vector<double>>> read_population_data(const std::string& path,
const std::vector<int>& vregion)
{
BOOST_OUTCOME_TRY(auto&& population_data, mio::read_population_data(path));
return read_population_data(population_data, vregion);
}

IOResult<std::vector<std::vector<double>>> read_population_data_spain(const std::string& path,
const std::vector<int>& vregion)
{
BOOST_OUTCOME_TRY(auto&& population_data, mio::read_population_data_spain(path));
return read_population_data_spain(population_data, vregion);
}
} // namespace mio
#endif //MEMILIO_HAS_JSONCPP
18 changes: 18 additions & 0 deletions cpp/memilio/io/parameters_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ IOResult<std::vector<std::vector<double>>> read_population_data(const std::vecto
IOResult<std::vector<std::vector<double>>> read_population_data(const std::string& path,
const std::vector<int>& vregion);

/**
* @brief Reads (Spain) population data from a vector of Spain population data entries.
* Uses provincias IDs and a single aggregated age group.
* @param[in] population_data Vector of Spain population data entries.
* @param[in] vregion Vector of keys representing the provincias (or country = 0) of interest.
*/
IOResult<std::vector<std::vector<double>>>
read_population_data_spain(const std::vector<PopulationDataEntrySpain>& population_data,
const std::vector<int>& vregion);

/**
* @brief Reads (Spain) population data from census data file with provincias.
* @param[in] path Path to the provincias population data file.
* @param[in] vregion Vector of keys representing the provincias (or country = 0) of interest.
*/
IOResult<std::vector<std::vector<double>>> read_population_data_spain(const std::string& path,
const std::vector<int>& vregion);

} // namespace mio

#endif //MEMILIO_HAS_JSONCPP
Expand Down
20 changes: 10 additions & 10 deletions cpp/memilio/mobility/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,15 +307,15 @@ IOResult<void> set_nodes(const Parameters& params, Date start_date, Date end_dat
});

//uncertainty in populations
for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) {
for (auto j = Index<typename Model::Compartments>(0); j < Model::Compartments::Count; ++j) {
auto& compartment_value = nodes[node_idx].populations[{i, j}];
compartment_value =
UncertainValue<FP>(0.5 * (1.1 * double(compartment_value) + 0.9 * double(compartment_value)));
compartment_value.set_distribution(mio::ParameterDistributionUniform(0.9 * double(compartment_value),
1.1 * double(compartment_value)));
}
}
// for (auto i = mio::AgeGroup(0); i < params.get_num_groups(); i++) {
// for (auto j = Index<typename Model::Compartments>(0); j < Model::Compartments::Count; ++j) {
// auto& compartment_value = nodes[node_idx].populations[{i, j}];
// compartment_value =
// UncertainValue<FP>(0.5 * (1.1 * double(compartment_value) + 0.9 * double(compartment_value)));
// compartment_value.set_distribution(mio::ParameterDistributionUniform(0.9 * double(compartment_value),
// 1.1 * double(compartment_value)));
// }
// }

params_graph.add_node(node_ids[node_idx], nodes[node_idx]);
}
Expand Down Expand Up @@ -368,7 +368,7 @@ IOResult<void> set_edges(const fs::path& mobility_data_file, Graph<Model, Mobili
for (auto age = AgeGroup(0); age < populations.template size<mio::AgeGroup>(); ++age) {
for (auto compartment : mobile_compartments) {
auto coeff_index = populations.get_flat_index({age, compartment});
mobility_coeffs[size_t(ContactLocation::Work)].get_baseline()[coeff_index] =
mobility_coeffs[size_t(ContactLocation::Home)].get_baseline()[coeff_index] =
commuter_coeff_ij * commuting_weights[size_t(age)];
}
}
Expand Down
Loading
Loading