diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index abc50a81db..e7eaed0cbf 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -66,11 +66,16 @@ int main() model.parameters.get() = 100; model.parameters.get() = 0.0143; - model.parameters.get().resize(mio::SimulationDay(size_t(1000))); - model.parameters.get().array().setConstant(5); - model.parameters.get().resize(mio::SimulationDay(size_t(1000))); - model.parameters.get().array().setConstant(3); - + const size_t daily_vaccinations = 10; + model.parameters.get().resize(mio::SimulationDay((size_t)tmax + 1)); + model.parameters.get().resize(mio::SimulationDay((size_t)tmax + 1)); + for (size_t i = 0; i < tmax + 1; ++i) { + auto num_vaccinations = static_cast(i * daily_vaccinations); + model.parameters.get()[{(mio::AgeGroup)0, mio::SimulationDay(i)}] = + num_vaccinations; + model.parameters.get()[{(mio::AgeGroup)0, mio::SimulationDay(i)}] = + num_vaccinations; + } auto& contacts = model.parameters.get(); auto& contact_matrix = contacts.get_cont_freq_mat(); contact_matrix[0].get_baseline().setConstant(0.5); diff --git a/cpp/models/ode_secirvvs/parameters_io.h b/cpp/models/ode_secirvvs/parameters_io.h index e975d3944d..22b8c858c3 100644 --- a/cpp/models/ode_secirvvs/parameters_io.h +++ b/cpp/models/ode_secirvvs/parameters_io.h @@ -1225,6 +1225,24 @@ export_input_data_county_timeseries(std::vector&& model, const std::strin return success(); } +#else +template +IOResult export_input_data_county_timeseries(std::vector&, const std::string&, std::vector const&, + Date, const std::vector&, double, int, const std::string&, + const std::string&, const std::string&) +{ + mio::log_warning("HDF5 not available. Cannot export time series of extrapolated real data."); + return success(); +} + +template +IOResult export_input_data_county_timeseries(std::vector&&, const std::string&, std::vector const&, + Date, const std::vector&, double, int, const std::string&, + const std::string&, const std::string&, bool, const std::string&) +{ + mio::log_warning("HDF5 not available. Cannot export time series of extrapolated real data."); + return success(); +} #endif //MEMILIO_HAS_HDF5 diff --git a/pycode/examples/simulation/2020_npis_sarscov2_wildtype_germany.py b/pycode/examples/simulation/2020_npis_sarscov2_wildtype_germany.py index 98e6b32ff8..2f7501d98e 100644 --- a/pycode/examples/simulation/2020_npis_sarscov2_wildtype_germany.py +++ b/pycode/examples/simulation/2020_npis_sarscov2_wildtype_germany.py @@ -177,8 +177,8 @@ def set_contact_matrices(self, model): minimum_file = os.path.join( self.data_dir, "contacts", "minimum_" + location + ".txt") contact_matrices[i] = mio.ContactMatrix( - mio.secir.read_mobility_plain(baseline_file), - mio.secir.read_mobility_plain(minimum_file) + mio.read_mobility_plain(baseline_file), + mio.read_mobility_plain(minimum_file) ) model.parameters.ContactPatterns.cont_freq_mat = contact_matrices @@ -203,7 +203,7 @@ def set_npis(self, params, end_date): typ_home = Intervention.Home.value typ_school = Intervention.SchoolClosure.value - typ_home = Intervention.HomeOffice.value + typ_homeoffice = Intervention.HomeOffice.value typ_gathering = Intervention.GatheringBanFacilitiesClosure.value typ_distance = Intervention.PhysicalDistanceAndMasks.value typ_senior = Intervention.SeniorAwareness.value @@ -231,7 +231,7 @@ def school_closure(t, min, max): def home_office(t, min, max): return damping_helper( - t, min, max, lvl_main, typ_home, [loc_work]) + t, min, max, lvl_main, typ_homeoffice, [loc_work]) def social_events(t, min, max): return damping_helper( diff --git a/pycode/examples/simulation/osecirvvs_simple.py b/pycode/examples/simulation/osecirvvs_simple.py new file mode 100644 index 0000000000..5baa385d73 --- /dev/null +++ b/pycode/examples/simulation/osecirvvs_simple.py @@ -0,0 +1,165 @@ +############################################################################# +# Copyright (C) 2020-2024 MEmilio +# +# Authors: Martin J. Kuehn, Maximilian Betz +# +# Contact: Martin J. Kuehn +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +import argparse +import os +from datetime import date, datetime + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +from memilio.simulation import AgeGroup, Damping, SimulationDay +from memilio.simulation.osecirvvs import InfectionState +from memilio.simulation.osecirvvs import Model, simulate + + +def run_secirvvs_simulation(show_plot=True): + """ + Runs the c++ SECIRVVS model using a single age group + and plots the results + """ + + t0 = 0 + tmax = 30 # number of days to simulate + dt = 0.1 + num_groups = 1 + + # Initialize Parameters + model = Model(num_groups) + + # set parameters + for i in range(num_groups): + # Initial number of peaople in each compartment + model.populations[AgeGroup(i), InfectionState.ExposedNaive] = 10 + model.populations[AgeGroup( + i), InfectionState.ExposedImprovedImmunity] = 11 + model.populations[AgeGroup( + i), InfectionState.ExposedPartialImmunity] = 12 + model.populations[AgeGroup( + i), InfectionState.InfectedNoSymptomsNaive] = 13 + model.populations[AgeGroup( + i), InfectionState.InfectedNoSymptomsNaiveConfirmed] = 13 + model.populations[AgeGroup( + i), InfectionState.InfectedNoSymptomsPartialImmunity] = 14 + model.populations[AgeGroup( + i), InfectionState.InfectedNoSymptomsPartialImmunityConfirmed] = 14 + model.populations[AgeGroup( + i), InfectionState.InfectedNoSymptomsImprovedImmunity] = 15 + model.populations[AgeGroup( + i), InfectionState.InfectedNoSymptomsImprovedImmunityConfirmed] = 15 + model.populations[AgeGroup( + i), InfectionState.InfectedSymptomsNaive] = 5 + model.populations[AgeGroup( + i), InfectionState.InfectedSymptomsNaiveConfirmed] = 5 + model.populations[AgeGroup( + i), InfectionState.InfectedSymptomsPartialImmunity] = 6 + model.populations[AgeGroup( + i), InfectionState.InfectedSymptomsPartialImmunityConfirmed] = 6 + model.populations[AgeGroup( + i), InfectionState.InfectedSymptomsImprovedImmunity] = 7 + model.populations[AgeGroup( + i), InfectionState.InfectedSymptomsImprovedImmunityConfirmed] = 7 + model.populations[AgeGroup(i), InfectionState.InfectedSevereNaive] = 8 + model.populations[AgeGroup( + i), InfectionState.InfectedSevereImprovedImmunity] = 1 + model.populations[AgeGroup( + i), InfectionState.InfectedSeverePartialImmunity] = 2 + model.populations[AgeGroup( + i), InfectionState.InfectedCriticalNaive] = 3 + model.populations[AgeGroup( + i), InfectionState.InfectedCriticalPartialImmunity] = 4 + model.populations[AgeGroup( + i), InfectionState.InfectedCriticalImprovedImmunity] = 5 + model.populations[AgeGroup( + i), InfectionState.SusceptibleImprovedImmunity] = 6 + model.populations[AgeGroup( + i), InfectionState.SusceptiblePartialImmunity] = 7 + model.populations[AgeGroup(i), InfectionState.DeadNaive] = 0 + model.populations[AgeGroup(i), InfectionState.DeadPartialImmunity] = 0 + model.populations[AgeGroup(i), InfectionState.DeadImprovedImmunity] = 0 + model.populations.set_difference_from_group_total_AgeGroup( + (AgeGroup(i), InfectionState.SusceptibleNaive), 1000) + + model.parameters.ICUCapacity.value = 100 + model.parameters.TestAndTraceCapacity.value = 0.0143 + model.parameters.DailyFirstVaccination.resize_SimulationDay( + SimulationDay(tmax + 1)) + model.parameters.DailyFullVaccination.resize_SimulationDay( + SimulationDay(tmax + 1)) + daily_vaccinations = 10 + for i, num_vaccinations in enumerate(range(0, daily_vaccinations * (tmax + 1), daily_vaccinations)): + model.parameters.DailyFirstVaccination[AgeGroup( + 0), SimulationDay(i)] = num_vaccinations + model.parameters.DailyFullVaccination[AgeGroup( + 0), SimulationDay(i)] = num_vaccinations + + # contact patterns + baseline = np.ones((num_groups, num_groups)) * 0.5 + np.fill_diagonal(baseline, 5.0) + model.parameters.ContactPatterns.cont_freq_mat[0].baseline = baseline + model.parameters.ContactPatterns.cont_freq_mat.add_damping(Damping( + coeffs=np.ones((num_groups, num_groups)) * 0.3, t=5.0, level=0, type=0)) + + # times + model.parameters.TimeInfectedSymptoms[AgeGroup(0)] = 7 + model.parameters.TimeInfectedSevere[AgeGroup(0)] = 6 + model.parameters.TimeInfectedCritical[AgeGroup(0)] = 7 + + # probabilities + model.parameters.TransmissionProbabilityOnContact[AgeGroup(0)] = 0.15 + model.parameters.RelativeTransmissionNoSymptoms[AgeGroup(0)] = 0.5 + # The precise value between Risk* (situation under control) and MaxRisk* (situation not under control) + # depends on incidence and test and trace capacity + model.parameters.RiskOfInfectionFromSymptomatic[AgeGroup(0)] = 0.0 + model.parameters.MaxRiskOfInfectionFromSymptomatic[AgeGroup(0)] = 0.4 + model.parameters.RecoveredPerInfectedNoSymptoms[AgeGroup(0)] = 0.2 + model.parameters.SeverePerInfectedSymptoms[AgeGroup(0)] = 0.1 + model.parameters.CriticalPerSevere[AgeGroup(0)] = 0.1 + model.parameters.DeathsPerCritical[AgeGroup(0)] = 0.1 + + model.parameters.ReducExposedPartialImmunity[AgeGroup(0)] = 0.8 + model.parameters.ReducExposedImprovedImmunity[AgeGroup(0)] = 0.331 + model.parameters.ReducInfectedSymptomsPartialImmunity[AgeGroup(0)] = 0.65 + model.parameters.ReducInfectedSymptomsImprovedImmunity[AgeGroup(0)] = 0.243 + model.parameters.ReducInfectedSevereCriticalDeadPartialImmunity[AgeGroup( + 0)] = 0.1 + model.parameters.ReducInfectedSevereCriticalDeadImprovedImmunity[AgeGroup( + 0)] = 0.091 + model.parameters.ReducTimeInfectedMild[AgeGroup(0)] = 0.9 + + model.parameters.Seasonality.value = 0.2 + + model.apply_constraints() + + # Run Simulation + result = simulate(t0, tmax, dt, model) + + # # interpolate results + # result = interpolate_simulation_result(result) + + print(result.get_last_value()) + + +if __name__ == "__main__": + arg_parser = argparse.ArgumentParser( + 'osecirvvs_simple', + description='Simple example demonstrating the setup and simulation of the SECIRVVS model with a single age group.') + args = arg_parser.parse_args() + run_secirvvs_simulation(**args.__dict__) diff --git a/pycode/memilio-simulation/CMakeLists.txt b/pycode/memilio-simulation/CMakeLists.txt index fffa3584dc..a366bc4d86 100644 --- a/pycode/memilio-simulation/CMakeLists.txt +++ b/pycode/memilio-simulation/CMakeLists.txt @@ -33,13 +33,6 @@ endif() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../cpp ${CMAKE_CURRENT_BINARY_DIR}/cpp EXCLUDE_FROM_ALL) # build python extensions -pybind11_add_module(_simulation_secir MODULE - memilio/simulation/secir.cpp -) -target_link_libraries(_simulation_secir PRIVATE memilio ode_secir) -target_include_directories(_simulation_secir PRIVATE memilio/simulation) -install(TARGETS _simulation_secir LIBRARY DESTINATION memilio) - pybind11_add_module(_simulation_abm MODULE memilio/simulation/abm.cpp ) @@ -62,6 +55,13 @@ target_link_libraries(_simulation PRIVATE memilio) target_include_directories(_simulation PRIVATE memilio/simulation) install(TARGETS _simulation LIBRARY DESTINATION memilio) +pybind11_add_module(_simulation_osir MODULE + memilio/simulation/osir.cpp +) +target_link_libraries(_simulation_osir PRIVATE memilio ode_sir) +target_include_directories(_simulation_osir PRIVATE memilio/simulation) +install(TARGETS _simulation_osir LIBRARY DESTINATION memilio) + pybind11_add_module(_simulation_oseir MODULE memilio/simulation/oseir.cpp ) @@ -69,10 +69,16 @@ target_link_libraries(_simulation_oseir PRIVATE memilio ode_seir) target_include_directories(_simulation_oseir PRIVATE memilio/simulation) install(TARGETS _simulation_oseir LIBRARY DESTINATION memilio) -pybind11_add_module(_simulation_osir MODULE - memilio/simulation/osir.cpp +pybind11_add_module(_simulation_secir MODULE + memilio/simulation/secir.cpp ) -target_link_libraries(_simulation_osir PRIVATE memilio ode_sir) -target_include_directories(_simulation_osir PRIVATE memilio/simulation) -install(TARGETS _simulation_osir LIBRARY DESTINATION memilio) +target_link_libraries(_simulation_secir PRIVATE memilio ode_secir) +target_include_directories(_simulation_secir PRIVATE memilio/simulation) +install(TARGETS _simulation_secir LIBRARY DESTINATION memilio) +pybind11_add_module(_simulation_osecirvvs MODULE + memilio/simulation/osecirvvs.cpp +) +target_link_libraries(_simulation_osecirvvs PRIVATE memilio ode_secirvvs) +target_include_directories(_simulation_osecirvvs PRIVATE memilio/simulation) +install(TARGETS _simulation_osecirvvs LIBRARY DESTINATION memilio) diff --git a/pycode/memilio-simulation/memilio/simulation/io/mobility_io.h b/pycode/memilio-simulation/memilio/simulation/io/mobility_io.h index 9a0fac6e2d..ebe3737491 100644 --- a/pycode/memilio-simulation/memilio/simulation/io/mobility_io.h +++ b/pycode/memilio-simulation/memilio/simulation/io/mobility_io.h @@ -34,7 +34,7 @@ void bind_write_graph(pybind11::module_& m) { m.def("write_graph", [&](const mio::Graph& graph, const std::string& directory) { int ioflags = mio::IOF_None; - mio::write_graph(graph, directory, ioflags); + auto ioresult = mio::write_graph(graph, directory, ioflags); }); } diff --git a/pycode/memilio-simulation/memilio/simulation/io/result_io.h b/pycode/memilio-simulation/memilio/simulation/io/result_io.h index 3d85cf0d2b..c79d6778fb 100644 --- a/pycode/memilio-simulation/memilio/simulation/io/result_io.h +++ b/pycode/memilio-simulation/memilio/simulation/io/result_io.h @@ -39,7 +39,7 @@ void bind_save_results(pybind11::module_& m) const std::vector>& ensemble_params, const std::vector& county_ids, const std::string& result_dir, bool save_single_runs, bool save_percentiles) { boost::filesystem::path dir(result_dir); - mio::save_results(ensemble_results, ensemble_params, county_ids, dir, save_single_runs, + auto ioresult = mio::save_results(ensemble_results, ensemble_params, county_ids, dir, save_single_runs, save_percentiles); return NULL; }); diff --git a/pycode/memilio-simulation/memilio/simulation/osecirvvs.cpp b/pycode/memilio-simulation/memilio/simulation/osecirvvs.cpp new file mode 100755 index 0000000000..7b6ac13a20 --- /dev/null +++ b/pycode/memilio-simulation/memilio/simulation/osecirvvs.cpp @@ -0,0 +1,350 @@ +/* +* Copyright (C) 2020-2023 German Aerospace Center (DLR-SC) +* +* Authors: Daniel Abele, Maximilian Betz +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "pybind_util.h" + +//Includes from pymio +#include "utils/parameter_set.h" +#include "compartments/simulation.h" +#include "compartments/compartmentalmodel.h" +#include "mobility/graph_simulation.h" +#include "mobility/metapopulation_mobility_instant.h" +#include "epidemiology/populations.h" +#include "io/mobility_io.h" +#include "io/result_io.h" + +//Includes from MEmilio +#include "ode_secirvvs/model.h" +#include "ode_secirvvs/infection_state.h" +#include "ode_secirvvs/analyze_result.h" +#include "ode_secirvvs/parameter_space.h" +#include "ode_secirvvs/parameters_io.h" +#include "memilio/compartments/flow_simulation.h" +#include "memilio/compartments/parameter_studies.h" + +#include "pybind11/stl_bind.h" +#include + +namespace py = pybind11; + +namespace +{ +//select only the first node of the graph of each run, used for parameterstudy with single nodes +template +std::vector +filter_graph_results(std::vector, mio::MigrationEdge>>&& graph_results) +{ + std::vector results; + results.reserve(graph_results.size()); + for (auto i = size_t(0); i < graph_results.size(); ++i) { + results.emplace_back(std::move(graph_results[i].nodes()[0].property.get_simulation())); + } + return std::move(results); +} + +/* + * @brief bind ParameterStudy for any model + */ +template +void bind_ParameterStudy(py::module_& m, std::string const& name) +{ + py::class_>(m, name.c_str()) + .def(py::init(), py::arg("model"), py::arg("t0"), + py::arg("tmax"), py::arg("num_runs")) + .def(py::init&, double, double, double, + size_t>(), + py::arg("model_graph"), py::arg("t0"), py::arg("tmax"), py::arg("dt"), py::arg("num_runs")) + .def_property("num_runs", &mio::ParameterStudy::get_num_runs, + &mio::ParameterStudy::set_num_runs) + .def_property("tmax", &mio::ParameterStudy::get_tmax, &mio::ParameterStudy::set_tmax) + .def_property("t0", &mio::ParameterStudy::get_t0, &mio::ParameterStudy::set_t0) + .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model), + py::return_value_policy::reference_internal) + .def_property_readonly("model", py::overload_cast<>(&mio::ParameterStudy::get_model, py::const_), + py::return_value_policy::reference_internal) + .def_property_readonly("model_graph", + py::overload_cast<>(&mio::ParameterStudy::get_model_graph), + py::return_value_policy::reference_internal) + .def_property_readonly("model_graph", + py::overload_cast<>(&mio::ParameterStudy::get_model_graph, py::const_), + py::return_value_policy::reference_internal) + .def( + "run", + [](mio::ParameterStudy& self, + std::function, mio::MigrationEdge>, size_t)> + handle_result, bool variant_high) { + self.run( + [variant_high](auto&& g) { + return draw_sample(g, variant_high); + }, + [&handle_result](auto&& g, auto&& run_idx) { + //handle_result_function needs to return something + //we don't want to run an unknown python object through parameterstudies, so + //we just return 0 and ignore the list returned by run(). + //So python will behave slightly different than c++ + handle_result(std::move(g), run_idx); + return 0; + }); + }, + py::arg("handle_result_func"), py::arg("variant_high")) + .def("run", + [](mio::ParameterStudy& self, bool variant_high) { //default argument doesn't seem to work with functions + return self.run([variant_high](auto&& g) { + return draw_sample(g, variant_high); + }); + }, + py::arg("variant_high")) + .def( + "run_single", + [](mio::ParameterStudy& self, std::function handle_result, bool variant_high) { + self.run( + [variant_high](auto&& g) { + return draw_sample(g, variant_high); + }, + [&handle_result](auto&& r, auto&& run_idx) { + handle_result(std::move(r.nodes()[0].property.get_simulation()), run_idx); + return 0; + }); + }, + py::arg("handle_result_func"), py::arg("variant_high")) + .def("run_single", + [](mio::ParameterStudy& self, bool variant_high) { + return filter_graph_results(self.run([variant_high](auto&& g) { + return draw_sample(g, variant_high); + })); + }, + py::arg("variant_high")); +} + +enum class ContactLocation +{ + Home = 0, + School, + Work, + Other, + Count, +}; + +} // namespace + +namespace pymio +{ +//specialization of pretty_name +template <> +std::string pretty_name() +{ + return "AgeGroup"; +} + +template <> +std::string pretty_name() +{ + return "InfectionState"; +} + +} // namespace pymio + +PYBIND11_MAKE_OPAQUE(std::vector>, mio::MigrationEdge>>); + +PYBIND11_MODULE(_simulation_osecirvvs, m) +{ + pymio::iterable_enum(m, "InfectionState") + .value("SusceptibleNaive", mio::osecirvvs::InfectionState::SusceptibleNaive) + .value("SusceptiblePartialImmunity", mio::osecirvvs::InfectionState::SusceptiblePartialImmunity) + .value("ExposedNaive", mio::osecirvvs::InfectionState::ExposedNaive) + .value("ExposedPartialImmunity", mio::osecirvvs::InfectionState::ExposedPartialImmunity) + .value("ExposedImprovedImmunity", mio::osecirvvs::InfectionState::ExposedImprovedImmunity) + .value("InfectedNoSymptomsNaive", mio::osecirvvs::InfectionState::InfectedNoSymptomsNaive) + .value("InfectedNoSymptomsPartialImmunity", mio::osecirvvs::InfectionState::InfectedNoSymptomsPartialImmunity) + .value("InfectedNoSymptomsImprovedImmunity", mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunity) + .value("InfectedNoSymptomsNaiveConfirmed", mio::osecirvvs::InfectionState::InfectedNoSymptomsNaiveConfirmed) + .value("InfectedNoSymptomsPartialImmunityConfirmed", + mio::osecirvvs::InfectionState::InfectedNoSymptomsPartialImmunityConfirmed) + .value("InfectedNoSymptomsImprovedImmunityConfirmed", + mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed) + .value("InfectedSymptomsNaive", mio::osecirvvs::InfectionState::InfectedSymptomsNaive) + .value("InfectedSymptomsPartialImmunity", mio::osecirvvs::InfectionState::InfectedSymptomsPartialImmunity) + .value("InfectedSymptomsImprovedImmunity", mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunity) + .value("InfectedSymptomsNaiveConfirmed", mio::osecirvvs::InfectionState::InfectedSymptomsNaiveConfirmed) + .value("InfectedSymptomsPartialImmunityConfirmed", + mio::osecirvvs::InfectionState::InfectedSymptomsPartialImmunityConfirmed) + .value("InfectedSymptomsImprovedImmunityConfirmed", + mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunityConfirmed) + .value("InfectedSevereNaive", mio::osecirvvs::InfectionState::InfectedSevereNaive) + .value("InfectedSeverePartialImmunity", mio::osecirvvs::InfectionState::InfectedSeverePartialImmunity) + .value("InfectedSevereImprovedImmunity", mio::osecirvvs::InfectionState::InfectedSevereImprovedImmunity) + .value("InfectedCriticalNaive", mio::osecirvvs::InfectionState::InfectedCriticalNaive) + .value("InfectedCriticalPartialImmunity", mio::osecirvvs::InfectionState::InfectedCriticalPartialImmunity) + .value("InfectedCriticalImprovedImmunity", mio::osecirvvs::InfectionState::InfectedCriticalImprovedImmunity) + .value("SusceptibleImprovedImmunity", mio::osecirvvs::InfectionState::SusceptibleImprovedImmunity) + .value("DeadNaive", mio::osecirvvs::InfectionState::DeadNaive) + .value("DeadPartialImmunity", mio::osecirvvs::InfectionState::DeadPartialImmunity) + .value("DeadImprovedImmunity", mio::osecirvvs::InfectionState::DeadImprovedImmunity); + + pymio::bind_ParameterSet(m, "ParametersBase"); + + py::class_(m, "Parameters") + .def(py::init()) + .def_property("commuter_nondetection", + [](const mio::osecirvvs::Parameters& self) { + return self.get_commuter_nondetection(); + }, + [](mio::osecirvvs::Parameters& self, double v) { + self.get_commuter_nondetection() = v; + }) + .def_property("start_commuter_detection", + [](const mio::osecirvvs::Parameters& self) { + return self.get_start_commuter_detection(); + }, + [](mio::osecirvvs::Parameters& self, double v) { + self.get_start_commuter_detection() = v; + }) + .def_property("end_commuter_detection", + [](const mio::osecirvvs::Parameters& self) { + return self.get_end_commuter_detection(); + }, + [](mio::osecirvvs::Parameters& self, double v) { + self.get_end_commuter_detection() = v; + }) + .def_property("end_dynamic_npis", + [](const mio::osecirvvs::Parameters& self) { + return self.get_end_dynamic_npis(); + }, + [](mio::osecirvvs::Parameters& self, double v) { + self.get_end_dynamic_npis() = v; + }) + .def("check_constraints", &mio::osecirvvs::Parameters::check_constraints) + .def("apply_constraints", &mio::osecirvvs::Parameters::apply_constraints); + + using SecirvvsPopulations = mio::Populations; + pymio::bind_Population(m, "Population", mio::Tag{}); + + pymio::bind_CompartmentalModel( + m, "ModelBase"); + py::class_>(m, "Model") + .def(py::init(), py::arg("num_agegroups")); + + pymio::bind_Simulation>(m, "Simulation"); + + m.def( + "simulate", + [](double t0, double tmax, double dt, const mio::osecirvvs::Model& model) { + return mio::osecirvvs::simulate(t0, tmax, dt, model); + }, + "Simulates a Model from t0 to tmax.", py::arg("t0"), py::arg("tmax"), py::arg("dt"), py::arg("model")); + + m.def( + "simulate_flows", + [](double t0, double tmax, double dt, const mio::osecirvvs::Model& model) { + return mio::osecirvvs::simulate_flows(t0, tmax, dt, model); + }, + "Simulates a osecirvvs model with flows from t0 to tmax.", py::arg("t0"), py::arg("tmax"), py::arg("dt"), + py::arg("model")); + + pymio::bind_ModelNode(m, "ModelNode"); + pymio::bind_SimulationNode>(m, "SimulationNode"); + pymio::bind_ModelGraph(m, "ModelGraph"); + pymio::bind_MigrationGraph>(m, "MigrationGraph"); + pymio::bind_GraphSimulation>, mio::MigrationEdge>>( + m, "MigrationSimulation"); + + //normally, std::vector is bound to any python iterable, but this doesn't work for move-only elements + //Bound the vector as a custom type that serves as output of ParameterStudy::run and input to + //interpolate_ensemble_results + py::bind_vector>, mio::MigrationEdge>>>(m, "EnsembleGraphResults"); + bind_ParameterStudy>(m, "ParameterStudy"); + + m.def( + "draw_sample", + [](mio::osecirvvs::Model& model) { + return mio::osecirvvs::draw_sample(model); + }, + py::arg("model")); + + // These functions are in general not secir dependent, only with the current config + m.def( + "set_nodes", + [](const mio::osecirvvs::Parameters& params, mio::Date start_date, mio::Date end_date, const std::string& data_dir, + const std::string& population_data_path, bool is_node_for_county, + mio::Graph& params_graph, + const std::vector& scaling_factor_inf, double scaling_factor_icu, double tnt_capacity_factor, + int num_days = 0, bool export_time_series = false) { + auto result = mio::set_nodes), + decltype(mio::get_node_ids)>( + params, start_date, end_date, data_dir, population_data_path, is_node_for_county, params_graph, + mio::osecirvvs::read_input_data_county, mio::get_node_ids, scaling_factor_inf, + scaling_factor_icu, tnt_capacity_factor, num_days, export_time_series); + return pymio::check_and_throw(result); + }, + py::return_value_policy::move); + + m.def( + "set_edges", + [](const std::string& data_dir, mio::Graph& params_graph, + size_t contact_locations_size) { + auto migrating_comp = {mio::osecirvvs::InfectionState::SusceptibleNaive, + mio::osecirvvs::InfectionState::ExposedNaive, + mio::osecirvvs::InfectionState::InfectedNoSymptomsNaive, + mio::osecirvvs::InfectionState::InfectedSymptomsNaive, + mio::osecirvvs::InfectionState::SusceptibleImprovedImmunity, + mio::osecirvvs::InfectionState::SusceptiblePartialImmunity, + mio::osecirvvs::InfectionState::ExposedPartialImmunity, + mio::osecirvvs::InfectionState::InfectedNoSymptomsPartialImmunity, + mio::osecirvvs::InfectionState::InfectedSymptomsPartialImmunity, + mio::osecirvvs::InfectionState::ExposedImprovedImmunity, + mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunity, + mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunity}; + auto weights = std::vector{0., 0., 1.0, 1.0, 0.33, 0., 0.}; + auto result = mio::set_edges( + data_dir, params_graph, migrating_comp, contact_locations_size, mio::read_mobility_plain, weights); + return pymio::check_and_throw(result); + }, + py::return_value_policy::move); + +#ifdef MEMILIO_HAS_HDF5 + pymio::bind_save_results(m); +#endif // MEMILIO_HAS_HDF5 + +#ifdef MEMILIO_HAS_JSONCPP + pymio::bind_write_graph(m); + m.def( + "read_input_data_county", + [](std::vector& model, mio::Date date, const std::vector& county, + const std::vector& scaling_factor_inf, double scaling_factor_icu, const std::string& dir, + int num_days = 0, bool export_time_series = false) { + auto result = mio::osecirvvs::read_input_data_county( + model, date, county, scaling_factor_inf, scaling_factor_icu, dir, num_days, export_time_series); + return pymio::check_and_throw(result); + }, + py::return_value_policy::move); +#endif // MEMILIO_HAS_JSONCPP + + m.def("interpolate_simulation_result", + py::overload_cast>, mio::MigrationEdge>&>(&mio::interpolate_simulation_result>)); + + m.def("interpolate_ensemble_results", &mio::interpolate_ensemble_results>, mio::MigrationEdge>>); + + m.attr("__version__") = "dev"; +} diff --git a/pycode/memilio-simulation/memilio/simulation/osecirvvs.py b/pycode/memilio-simulation/memilio/simulation/osecirvvs.py new file mode 100755 index 0000000000..4b6db7a470 --- /dev/null +++ b/pycode/memilio-simulation/memilio/simulation/osecirvvs.py @@ -0,0 +1,25 @@ +############################################################################# +# Copyright (C) 2020-2023 German Aerospace Center (DLR-SC) +# +# Authors: Daniel Abele, Maximilian Betz +# +# Contact: Martin J. Kuehn +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# + +""" +Python bindings for MEmilio osecirvvs model. +""" + +from memilio._simulation_osecirvvs import * diff --git a/pycode/memilio-simulation/memilio/simulation/secir.cpp b/pycode/memilio-simulation/memilio/simulation/secir.cpp index ede186f659..bf1c502216 100644 --- a/pycode/memilio-simulation/memilio/simulation/secir.cpp +++ b/pycode/memilio-simulation/memilio/simulation/secir.cpp @@ -310,24 +310,8 @@ PYBIND11_MODULE(_simulation_secir, m) return pymio::check_and_throw(result); }, py::return_value_policy::move); - - m.def( - "get_node_ids", - [](const std::string& path, bool is_node_for_county) { - auto result = mio::get_node_ids(path, is_node_for_county); - return pymio::check_and_throw(result); - }, - py::return_value_policy::move); #endif // MEMILIO_HAS_JSONCPP - m.def( - "read_mobility_plain", - [](const std::string& filename) { - auto result = mio::read_mobility_plain(filename); - return pymio::check_and_throw(result); - }, - py::return_value_policy::move); - m.def("interpolate_simulation_result", py::overload_cast(&mio::interpolate_simulation_result)); diff --git a/pycode/memilio-simulation/memilio/simulation/simulation.cpp b/pycode/memilio-simulation/memilio/simulation/simulation.cpp index e60ee53704..7b11c8a9ff 100755 --- a/pycode/memilio-simulation/memilio/simulation/simulation.cpp +++ b/pycode/memilio-simulation/memilio/simulation/simulation.cpp @@ -37,6 +37,9 @@ #include "memilio/utils/date.h" #include "memilio/geography/regions.h" #include "memilio/epidemiology/contact_matrix.h" +#include "memilio/epidemiology/simulation_day.h" +#include "memilio/io/mobility_io.h" +#include "memilio/io/epi_data.h" namespace py = pybind11; @@ -49,6 +52,12 @@ std::string pretty_name() return "AgeGroup"; } +template <> +std::string pretty_name() +{ + return "SimulationDay"; +} + } // namespace pymio PYBIND11_MODULE(_simulation, m) @@ -56,6 +65,9 @@ PYBIND11_MODULE(_simulation, m) pymio::bind_CustomIndexArray(m, "AgeGroupArray"); py::class_>(m, "AgeGroup").def(py::init()); + pymio::bind_CustomIndexArray(m, "AgeGroupSimulationDayArray"); + py::class_>(m, "SimulationDay").def(py::init()); + pymio::bind_date(m, "Date"); auto damping_class = py::class_(m, "Damping"); @@ -118,6 +130,24 @@ PYBIND11_MODULE(_simulation, m) py::arg("state_id"), py::arg("start_date") = mio::Date(std::numeric_limits::min(), 1, 1), py::arg("end_date") = mio::Date(std::numeric_limits::max(), 1, 1)); + m.def( + "read_mobility_plain", + [](const std::string& filename) { + auto result = mio::read_mobility_plain(filename); + return pymio::check_and_throw(result); + }, + py::return_value_policy::move); + +#ifdef MEMILIO_HAS_JSONCPP + m.def( + "get_node_ids", + [](const std::string& path, bool is_node_for_county) { + auto result = mio::get_node_ids(path, is_node_for_county); + return pymio::check_and_throw(result); + }, + py::return_value_policy::move); +#endif // MEMILIO_HAS_JSONCPP + pymio::bind_logging(m, "LogLevel"); m.def("seed_random_number_generator", [] { diff --git a/pycode/memilio-simulation/memilio/simulation/utils/custom_index_array.h b/pycode/memilio-simulation/memilio/simulation/utils/custom_index_array.h index 3d00b89503..08fc2dda5a 100755 --- a/pycode/memilio-simulation/memilio/simulation/utils/custom_index_array.h +++ b/pycode/memilio-simulation/memilio/simulation/utils/custom_index_array.h @@ -37,6 +37,12 @@ namespace pymio { +template +struct has_indices_attribute : std::false_type {}; + +template +struct has_indices_attribute().size().indices)>> : std::true_type {}; + // Recursively bind the members of custom index array // that require a single Tag as a template argument. template @@ -49,6 +55,11 @@ void bind_single_tag_template_members(pybind11::module_& m, pybind11::class_& std::string tname = pretty_name(); c.def(("size_" + tname).c_str(), &C::template size); + // Only add if the Index is multidimensional, e.g. only when Index::indices exists, because it is used in resize + if constexpr (has_indices_attribute::value){ + c.def(("resize_" + tname).c_str(), &C::template resize); + } + // Catch warning ImportError: generic_type: type "" is already registered! try { bind_Index(m, ("Index_" + tname).c_str()); @@ -141,6 +152,9 @@ void bind_single_or_multi_index_members_CustomIndexArray(pybind11::module_& m, p c.def("size", [](const C& self) { return self.size(); //just a single index, no tuple }); + c.def("resize", [](C& self, mio::Index new_dims) { + self.resize(new_dims); //tuple of single indices + }); } template std::enable_if_t<(sizeof...(Tags) > 1)> bind_single_or_multi_index_members_CustomIndexArray(pybind11::module_& m, @@ -150,6 +164,10 @@ std::enable_if_t<(sizeof...(Tags) > 1)> bind_single_or_multi_index_members_Custo c.def("size", [](const C& self) { return self.size().indices; //tuple of single indices }); + c.def("resize", [](C& self, mio::Index new_dims) { + self.resize(new_dims); //tuple of single indices + }); + // c.def("resize", pybind11::overload_cast>(&C::resize)); // Catch warning ImportError: generic_type: type "" is already registered! try { diff --git a/pycode/memilio-simulation/memilio/simulation_test/data/ode-secirvvs-compare.csv b/pycode/memilio-simulation/memilio/simulation_test/data/ode-secirvvs-compare.csv new file mode 100644 index 0000000000..86a5e3e58c --- /dev/null +++ b/pycode/memilio-simulation/memilio/simulation_test/data/ode-secirvvs-compare.csv @@ -0,0 +1,70 @@ +Time S_0 S_1 E_0 E_1 E_2 I_0 I_1 I_2 I_3 I_4 I_5 I_6 I_7 I_8 I_9 I_10 I_11 IS_0 IS_1 IS_2 IC_0 IC_1 IC_2 R D_0 D_1 D_2 +0.0000000000000 9760.0000000000000 0.0000000000000 100.0000000000000 0.0000000000000 0.0000000000000 50.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 50.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 20.0000000000000 0.0000000000000 0.0000000000000 10.0000000000000 0.0000000000000 0.0000000000000 10.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 +0.1000000000000 9756.3131862962109 0.0000000000000 100.5532632351259 0.0000000000000 0.0039572102526 50.6180426925369 0.0000000000000 0.0000597572573 0.0000000000000 0.0000000000000 0.0000000000000 51.2763528680507 0.0000000000000 0.0000008974353 0.0000000000000 0.0000000000000 0.0000000000000 20.0025433582010 0.0000000000000 0.0000000008939 9.9254689172105 0.0000000000000 0.0000000000004 11.2697648057297 0.0373599610951 0.0000000000000 0.0000000000000 +0.2373994584400 9751.1610719621767 0.0000000000000 101.3705719880882 0.0000000000000 0.0100815955083 51.4468094186853 0.0000000000000 0.0003457366242 0.0000000000000 0.0000000000000 0.0000000000000 53.0336425902207 0.0000000000000 0.0000121655925 0.0000000000000 0.0000000000000 0.0000000000000 20.0142808604239 0.0000000000000 0.0000000286161 9.8245943609003 0.0000000000000 0.0000000000337 13.0503491932711 0.0882400998586 0.0000000000000 0.0000000000000 +0.3946730872617 9745.1430135633072 0.0000000000000 102.3819349565636 0.0000000000000 0.0180947514806 52.3713880509217 0.0000000000000 0.0009836761292 0.0000000000000 0.0000000000000 0.0000000000000 55.0485286277393 0.0000000000000 0.0000567148615 0.0000000000000 0.0000000000000 0.0000000000000 20.0392964350392 0.0000000000000 0.0000002204917 9.7113061665027 0.0000000000000 0.0000000004300 15.1395488427179 0.1458479938138 0.0000000000000 0.0000000000011 +0.5927674871826 9737.3828962499138 0.0000000000000 103.7620192545356 0.0000000000000 0.0297612690586 53.5076846552938 0.0000000000000 0.0022980896176 0.0000000000000 0.0000000000000 0.0000000000000 57.5890472354583 0.0000000000000 0.0001955414226 0.0000000000000 0.0000000000000 0.0000000000000 20.0881334179542 0.0000000000000 0.0000011336990 9.5719248630343 0.0000000000000 0.0000000033021 17.8485694526664 0.2174688340308 0.0000000000000 0.0000000000122 +0.8435636089739 9727.2752844313127 0.0000000000000 105.6639531744305 0.0000000000000 0.0471470424664 54.9150747159744 0.0000000000000 0.0048548200577 0.0000000000000 0.0000000000000 0.0000000000000 60.8061794435096 0.0000000000000 0.0005755505085 0.0000000000000 0.0000000000000 0.0000000000000 20.1771394354464 0.0000000000000 0.0000047080498 9.4007620524812 0.0000000000000 0.0000000193803 21.4023427345249 0.3066818717530 0.0000000000000 0.0000000001019 +1.0000000000000 9720.8124946468743 0.0000000000000 106.9304970828724 0.0000000000000 0.0595283010559 55.7820187711789 0.0000000000000 0.0069967469502 0.0000000000000 0.0000000000000 0.0000000000000 62.8120031200177 0.0000000000000 0.0009709156043 0.0000000000000 0.0000000000000 0.0000000000000 20.2477265472947 0.0000000000000 0.0000093665172 9.2969979489272 0.0000000000000 0.0000000455151 23.6892318227695 0.3615246841368 0.0000000000000 0.0000000002836 +1.3596241838580 9705.4995335854128 0.0000000000000 110.0534975052420 0.0000000000000 0.0926830108977 57.7634757858703 0.0000000000000 0.0136672363605 0.0000000000000 0.0000000000000 0.0000000000000 67.4183017014093 0.0000000000000 0.0025078488226 0.0000000000000 0.0000000000000 0.0000000000000 20.4527472346471 0.0000000000000 0.0000325223677 9.0671973210702 0.0000000000000 0.0000002128709 29.1510161165293 0.4853399166947 0.0000000000000 0.0000000018024 +1.7765736531789 9686.9533692432760 0.0000000000000 114.0061876925804 0.0000000000000 0.1397877942428 60.0744194377165 0.0000000000000 0.0247678321235 0.0000000000000 0.0000000000000 0.0000000000000 72.7510081178559 0.0000000000000 0.0057611688845 0.0000000000000 0.0000000000000 0.0000000000000 20.7626536403763 0.0000000000000 0.0000964193632 8.8159874099572 0.0000000000000 0.0000008161246 35.8408345637515 0.6251258547175 0.0000000000000 0.0000000090257 +2.0000000000000 9676.6642840529948 0.0000000000000 116.2572088702390 0.0000000000000 0.1690978785889 61.3313288480742 0.0000000000000 0.0323545854462 0.0000000000000 0.0000000000000 0.0000000000000 75.6071472690212 0.0000000000000 0.0083418594391 0.0000000000000 0.0000000000000 0.0000000000000 20.9595433789124 0.0000000000000 0.0001561693327 8.6880820324024 0.0000000000000 0.0000014801205 39.5840021463515 0.6984514106471 0.0000000000000 0.0000000184255 +2.5488718130703 9650.3364059273481 0.0000000000000 122.1463680515941 0.0000000000000 0.2540712289649 64.5060800255885 0.0000000000000 0.0563807989368 0.0000000000000 0.0000000000000 0.0000000000000 82.6322409598278 0.0000000000000 0.0178630159201 0.0000000000000 0.0000000000000 0.0000000000000 21.5310346511181 0.0000000000000 0.0004198490452 8.3936454808878 0.0000000000000 0.0000050068151 49.2512876391827 0.8741972853405 0.0000000000000 0.0000000794267 +3.0000000000000 9627.5572346696154 0.0000000000000 127.3429783452881 0.0000000000000 0.3388146343170 67.2330436477940 0.0000000000000 0.0824618516399 0.0000000000000 0.0000000000000 0.0000000000000 88.4338343649905 0.0000000000000 0.0298869064085 0.0000000000000 0.0000000000000 0.0000000000000 22.0907817492237 0.0000000000000 0.0008171042830 8.1725544866041 0.0000000000000 0.0000113532798 57.7032821206321 1.0142985539063 0.0000000000000 0.0000002120145 +3.6185289754131 9594.6015142603301 0.0000000000000 134.9575479927246 0.0000000000000 0.4792267311337 71.1778961614943 0.0000000000000 0.1287227654673 0.0000000000000 0.0000000000000 0.0000000000000 96.4688940991898 0.0000000000000 0.0542047656116 0.0000000000000 0.0000000000000 0.0000000000000 22.9846137328916 0.0000000000000 0.0017598337468 7.8997749272389 0.0000000000000 0.0000290989932 70.0451863774425 1.2006285980150 0.0000000000000 0.0000006557178 +4.0000000000000 9573.2503653192816 0.0000000000000 139.9263246433787 0.0000000000000 0.5811592709247 73.7412942101069 0.0000000000000 0.1639766559207 0.0000000000000 0.0000000000000 0.0000000000000 101.4928842444652 0.0000000000000 0.0746419504192 0.0000000000000 0.0000000000000 0.0000000000000 23.6063815647107 0.0000000000000 0.0026537617230 7.7488552619521 0.0000000000000 0.0000481134340 78.0988727483321 1.3125410564358 0.0000000000000 0.0000011989129 +4.7670873976741 9527.8445614744396 0.0000000000000 150.5346216023111 0.0000000000000 0.8258950481491 79.2193796252348 0.0000000000000 0.2524406657481 0.0000000000000 0.0000000000000 0.0000000000000 111.8151655996258 0.0000000000000 0.1310948317164 0.0000000000000 0.0000000000000 0.0000000000000 25.0145678449551 0.0000000000000 0.0054521319650 7.4847998471955 0.0000000000000 0.0001159267724 95.3403823637300 1.5315195925932 0.0000000000000 0.0000034455615 +5.0000000000000 9513.3819289488220 0.0000000000000 153.9178957811776 0.0000000000000 0.9117144827323 80.9715273043242 0.0000000000000 0.2844071737328 0.0000000000000 0.0000000000000 0.0000000000000 115.0197964971563 0.0000000000000 0.1529131342195 0.0000000000000 0.0000000000000 0.0000000000000 25.4831529927434 0.0000000000000 0.0066328752968 7.4149257891323 0.0000000000000 0.0001472098505 100.8583683343608 1.5965848860786 0.0000000000000 0.0000045903681 +5.8539520805764 9457.5346306836054 0.0000000000000 166.9716623960698 0.0000000000000 1.2782147273237 87.7630266040865 0.0000000000000 0.4246832698669 0.0000000000000 0.0000000000000 0.0000000000000 127.1128433882496 0.0000000000000 0.2551266086937 0.0000000000000 0.0000000000000 0.0000000000000 27.3623348239082 0.0000000000000 0.0126923889969 7.1991496848534 0.0000000000000 0.0003240396939 122.2548884247231 1.8304111182437 0.0000000000000 0.0000118416802 +6.0000000000000 9447.5254398368652 0.0000000000000 169.3070913756641 0.0000000000000 1.3497465449067 88.9835280952835 0.0000000000000 0.4526183050328 0.0000000000000 0.0000000000000 0.0000000000000 129.2413566574424 0.0000000000000 0.2765075542195 0.0000000000000 0.0000000000000 0.0000000000000 27.7089819686216 0.0000000000000 0.0140495184923 7.1685401574411 0.0000000000000 0.0003665305565 126.1020049789320 1.8697547457077 0.0000000000000 0.0000137308294 +6.8887144924501 9383.5977885069933 0.0000000000000 184.1757969630790 0.0000000000000 1.8481479742620 96.7916373954193 0.0000000000000 0.6507860983720 0.0000000000000 0.0000000000000 0.0000000000000 142.6295760805878 0.0000000000000 0.4354005671885 0.0000000000000 0.0000000000000 0.0000000000000 29.9776729981223 0.0000000000000 0.0248554439368 7.0213167661636 0.0000000000000 0.0007309376868 150.7402382411558 2.1060205619689 0.0000000000000 0.0000314650592 +7.0000000000000 9375.2168112907639 0.0000000000000 186.1180239806306 0.0000000000000 1.9187932780562 97.8161790419685 0.0000000000000 0.6792811245300 0.0000000000000 0.0000000000000 0.0000000000000 144.3627221790595 0.0000000000000 0.4591178090612 0.0000000000000 0.0000000000000 0.0000000000000 30.2811371441767 0.0000000000000 0.0265600336013 7.0075654097231 0.0000000000000 0.0007918530581 153.9776893009936 2.1352929132064 0.0000000000000 0.0000346411648 +7.9678108589959 9298.6333641495876 0.0000000000000 203.7763467862843 0.0000000000000 2.6210428427219 107.1756899124332 0.0000000000000 0.9664415789343 0.0000000000000 0.0000000000000 0.0000000000000 160.0259457532036 0.0000000000000 0.7071656517841 0.0000000000000 0.0000000000000 0.0000000000000 33.1048380177801 0.0000000000000 0.0454595107924 6.9313560902380 0.0000000000000 0.0015118409325 183.6227621242426 2.3880004137176 0.0000000000000 0.0000753273412 +8.0000000000000 9295.9692893090341 0.0000000000000 204.3875409053480 0.0000000000000 2.6473093430967 107.5010072386888 0.0000000000000 0.9772971697070 0.0000000000000 0.0000000000000 0.0000000000000 160.5660709704202 0.0000000000000 0.7168159763212 0.0000000000000 0.0000000000000 0.0000000000000 33.2045075690093 0.0000000000000 0.0462286697877 6.9301497317348 0.0000000000000 0.0015425875143 184.6557968915394 2.3963664670131 0.0000000000000 0.0000771707807 +9.0000000000000 9209.2837163055974 0.0000000000000 224.1530466766238 0.0000000000000 3.5693773587308 118.0664610832629 0.0000000000000 1.3622234867961 0.0000000000000 0.0000000000000 0.0000000000000 178.0128469318820 0.0000000000000 1.0686320416857 0.0000000000000 0.0000000000000 0.0000000000000 36.4900416278865 0.0000000000000 0.0755903118522 6.9348492885267 0.0000000000000 0.0027781295638 218.3242000031306 2.6560803788783 0.0000000000000 0.0001563755779 +10.0000000000000 9114.6575656390323 0.0000000000000 245.4445989887476 0.0000000000000 4.7254737716359 129.5381521081927 0.0000000000000 1.8527128284720 0.0000000000000 0.0000000000000 0.0000000000000 196.8463879140418 0.0000000000000 1.5372060119197 0.0000000000000 0.0000000000000 0.0000000000000 40.1534334068027 0.0000000000000 0.1177306765336 7.0207064646135 0.0000000000000 0.0047051488724 255.1835384775401 2.9174944129490 0.0000000000000 0.0002941506389 +11.0000000000000 9011.5943388318210 0.0000000000000 268.2787909422779 0.0000000000000 6.1632673299377 141.9373287697757 0.0000000000000 2.4709738308711 0.0000000000000 0.0000000000000 0.0000000000000 217.1937509422860 0.0000000000000 2.1495077079317 0.0000000000000 0.0000000000000 0.0000000000000 44.2140206227249 0.0000000000000 0.1763850146991 7.1872906791978 0.0000000000000 0.0075861264980 295.4425958170133 3.1836422262780 0.0000000000000 0.0005211586820 +12.0000000000000 8899.6140643063209 0.0000000000000 292.6533187710886 0.0000000000000 7.9384178771727 155.2785506269620 0.0000000000000 3.2432474122142 0.0000000000000 0.0000000000000 0.0000000000000 239.1663870717937 0.0000000000000 2.9376193153459 0.0000000000000 0.0000000000000 0.0000000000000 48.6938349895267 0.0000000000000 0.2560560738616 7.4347104563162 0.0000000000000 0.0117494378533 339.3136131170113 3.4575514377490 0.0000000000000 0.0008791067772 +13.0000000000000 8778.2652163337243 0.0000000000000 318.5417837636904 0.0000000000000 10.1153319109650 169.5669782914106 0.0000000000000 4.2002873760682 0.0000000000000 0.0000000000000 0.0000000000000 262.8596436062686 0.0000000000000 3.9395740731159 0.0000000000000 0.0000000000000 0.0000000000000 53.6167457738725 0.0000000000000 0.3621517300745 7.7636029877068 0.0000000000000 0.0176012587114 387.0073957483202 3.7422637115460 0.0000000000000 0.0014234345195 +14.0000000000000 8647.1381295764659 0.0000000000000 345.8886052286725 0.0000000000000 12.7678189364266 184.7954664760079 0.0000000000000 5.3778257882136 0.0000000000000 0.0000000000000 0.0000000000000 288.3511924924297 0.0000000000000 5.2002386881035 0.0000000000000 0.0000000000000 0.0000000000000 59.0076447271348 0.0000000000000 0.5011423428597 8.1751053835765 0.0000000000000 0.0256395436632 438.7281102341193 4.0408540955177 0.0000000000000 0.0022264868031 +15.0000000000000 8505.8798101662087 0.0000000000000 374.6041033484202 0.0000000000000 15.9795791325838 200.9415698846126 0.0000000000000 6.8169957478767 0.0000000000000 0.0000000000000 0.0000000000000 315.6985229418307 0.0000000000000 6.7722197124327 0.0000000000000 0.0000000000000 0.0000000000000 64.8916301829696 0.0000000000000 0.6807372652940 8.6708102099450 0.0000000000000 0.0364702916360 494.6677208823513 4.3564489807666 0.0000000000000 0.0033812530664 +16.0000000000000 8354.2098881427391 0.0000000000000 404.5599289252771 0.0000000000000 19.8444395547486 217.9645595720600 0.0000000000000 8.5646760707087 0.0000000000000 0.0000000000000 0.0000000000000 344.9356525255386 0.0000000000000 8.7167646891958 0.0000000000000 0.0000000000000 0.0000000000000 71.2931577219147 0.0000000000000 0.9100790663563 9.2527067298544 0.0000000000000 0.0508262645902 555.0000728520668 4.6922421228799 0.0000000000000 0.0050057620653 +17.0000000000000 8191.9373002194852 0.0000000000000 435.5851034472872 0.0000000000000 24.4662446383186 235.8025645101776 0.0000000000000 10.6737144220973 0.0000000000000 0.0000000000000 0.0000000000000 376.0692263074719 0.0000000000000 11.1046178176341 0.0000000000000 0.0000000000000 0.0000000000000 78.2351340588804 0.0000000000000 1.1999528392710 9.9231084447387 0.0000000000000 0.0695882585230 619.8746886079067 5.0515082019349 0.0000000000000 0.0072482262692 +18.0000000000000 8018.9771336105514 0.0000000000000 467.4629981013657 0.0000000000000 29.9582992927882 254.3699818233525 0.0000000000000 13.2029782641366 0.0000000000000 0.0000000000000 0.0000000000000 409.0741960785496 0.0000000000000 14.0167776901733 0.0000000000000 0.0000000000000 0.0000000000000 85.7379398932941 0.0000000000000 1.5630064540169 10.6845670225549 0.0000000000000 0.0938089350240 689.4104063918132 5.4376134103018 0.0000000000000 0.0102930320744 +19.0000000000000 7835.3669078475477 0.0000000000000 499.9296240310724 0.0000000000000 36.4422619180158 273.5553298246372 0.0000000000000 16.2171776022781 0.0000000000000 0.0000000000000 0.0000000000000 443.8893026948972 0.0000000000000 17.5450920511308 0.0000000000000 0.0000000000000 0.0000000000000 93.8183767035157 0.0000000000000 2.0139757658216 11.5397724642138 0.0000000000000 0.1247391017991 763.6890497695813 5.8540225552992 0.0000000000000 0.0143676701866 +20.0000000000000 7641.2814353146441 0.0000000000000 532.6736226127930 0.0000000000000 44.0463929684565 293.2197438499894 0.0000000000000 19.7864011970400 0.0000000000000 0.0000000000000 0.0000000000000 480.4126216884645 0.0000000000000 21.7926125956651 0.0000000000000 0.0000000000000 0.0000000000000 102.4885421386409 0.0000000000000 2.5699066501001 12.4914393743479 0.0000000000000 0.1638561777986 842.7493725807642 6.3043021577087 0.0000000000000 0.0197506935834 +21.0000000000000 7437.0453001625365 0.0000000000000 565.3383259803098 0.0000000000000 52.9030848083255 313.1963301311539 0.0000000000000 23.9853100647550 0.0000000000000 0.0000000000000 0.0000000000000 518.4974713435213 0.0000000000000 26.8736230063205 0.0000000000000 0.0000000000000 0.0000000000000 111.7546488791757 0.0000000000000 3.2503633782460 13.5421794490470 0.0000000000000 0.2128943906456 926.5815686027618 6.7921190260547 0.0000000000000 0.0267807771422 +22.0000000000000 7223.1419490347389 0.0000000000000 597.5261969158208 0.0000000000000 63.1456324652887 333.2905921000684 0.0000000000000 28.8919401497496 0.0000000000000 0.0000000000000 0.0000000000000 557.9490174220284 0.0000000000000 32.9132474844534 0.0000000000000 0.0000000000000 0.0000000000000 121.6158125121169 0.0000000000000 4.0776104120362 14.6943607568319 0.0000000000000 0.2738760315546 1015.1226639844998 7.3212337988201 0.0000000000000 0.0358669319878 +23.0000000000000 7000.2184147964927 0.0000000000000 628.8058485772968 0.0000000000000 74.9042531805466 353.2821217045284 0.0000000000000 34.5860811901505 0.0000000000000 0.0000000000000 0.0000000000000 598.5219327018860 0.0000000000000 40.0465469691293 0.0000000000000 0.0000000000000 0.0000000000000 132.0628447664266 0.0000000000000 5.0767523707100 15.9499550515345 0.0000000000000 0.3491428402163 1108.2531169732151 7.8954889802103 0.0000000000000 0.0474998976534 +24.0000000000000 6769.0848114014525 0.0000000000000 658.7216881775364 0.0000000000000 88.3014241906351 372.9277019533079 0.0000000000000 41.1472216687353 0.0000000000000 0.0000000000000 0.0000000000000 639.9194749910365 0.0000000000000 48.4170180638983 0.0000000000000 0.0000000000000 0.0000000000000 143.0770988065169 0.0000000000000 6.2758149663062 17.3103751901242 0.0000000000000 0.4413863183373 1205.7949285201369 8.5187910566925 0.0000000000000 0.0622646952813 +25.0000000000000 6530.7079517419070 0.0000000000000 686.8060311762119 0.0000000000000 103.4466794366145 391.9658946411121 0.0000000000000 48.6520801373806 0.0000000000000 0.0000000000000 0.0000000000000 681.7943271415179 0.0000000000000 58.1744271605276 0.0000000000000 0.0000000000000 0.0000000000000 154.6294223822996 0.0000000000000 7.7057484104835 18.7763056948766 0.0000000000000 0.5536754902427 1307.5115159349900 9.1950863770582 0.0000000000000 0.0808542747742 +26.0000000000000 6286.1987473816334 0.0000000000000 712.5933080671257 0.0000000000000 120.4310808105066 410.1230908298460 0.0000000000000 57.1717798008433 0.0000000000000 0.0000000000000 0.0000000000000 723.7514910357321 0.0000000000000 69.4719404746331 0.0000000000000 0.0000000000000 0.0000000000000 166.6792815097270 0.0000000000000 9.4003344976357 20.3475305412995 0.0000000000000 0.6894803625032 1413.1095199481949 9.9283306121416 0.0000000000000 0.1040841281732 +27.0000000000000 6036.7934327954108 0.0000000000000 735.6357562913973 0.0000000000000 139.3216497565949 427.1208868140695 0.0000000000000 66.7687634291109 0.0000000000000 0.0000000000000 0.0000000000000 765.3534413672166 0.0000000000000 82.4625497861682 0.0000000000000 0.0000000000000 0.0000000000000 179.1741209498028 0.0000000000000 11.3959795851962 22.0227632970987 0.0000000000000 0.8526891075739 1522.2426073657102 10.7224517830799 0.0000000000000 0.1329076715646 +28.0000000000000 5783.8290869214825 0.0000000000000 755.5197827818931 0.0000000000000 160.1561007049264 442.6845245854657 0.0000000000000 77.4935855763742 0.0000000000000 0.0000000000000 0.0000000000000 806.1276248589406 0.0000000000000 97.2948423365001 0.0000000000000 0.0000000000000 0.0000000000000 192.0490270595311 0.0000000000000 13.7313782907863 23.7994856935247 0.0000000000000 1.0476168385490 1634.5172051739421 11.5813070575509 0.0000000000000 0.1684321205278 +29.0000000000000 5528.7143599547653 0.0000000000000 771.8820279638909 0.0000000000000 182.9382489585448 456.5520162330977 0.0000000000000 89.3817539074376 0.0000000000000 0.0000000000000 0.0000000000000 845.5762415390257 0.0000000000000 114.1082187226743 0.0000000000000 0.0000000000000 0.0000000000000 205.2267528028172 0.0000000000000 16.4470370713634 25.6738014780421 0.0000000000000 1.2790037846383 1749.4999693243649 12.5086337566192 0.0000000000000 0.2119345027135 +29.2954345588958 5456.7726339943720 0.0000000000000 772.4518325197380 0.0000000000000 188.8876330822667 460.2076765546440 0.0000000000000 93.0935226530071 0.0000000000000 0.0000000000000 0.0000000000000 856.9001360539011 0.0000000000000 119.4726240834838 0.0000000000000 0.0000000000000 0.0000000000000 209.1650928333014 0.0000000000000 17.3281879740118 26.2454645814629 0.0000000000000 1.3550842390034 1785.0973612113480 12.7962278693665 0.0000000000000 0.2265223500886 +29.7112207971861 5387.8518621836620 0.0000000000000 742.4910558189745 0.0000000000000 187.3119070990508 463.0427032469729 0.0000000000000 97.8229257439911 0.0000000000000 0.0000000000000 0.0000000000000 872.3956079276805 0.0000000000000 127.2919976714087 0.0000000000000 0.0000000000000 0.0000000000000 214.7339865900351 0.0000000000000 18.6325339673215 27.0633343465641 0.0000000000000 1.4685739637333 1846.4331829737064 13.2118028528057 0.0000000000000 0.2485256140890 +30.0000000000000 5362.1666406382292 0.0000000000000 702.9085464511160 0.0000000000000 179.6693633734090 461.5210532148353 0.0000000000000 100.0993896932391 0.0000000000000 0.0000000000000 0.0000000000000 882.4926530924093 0.0000000000000 132.7953402593965 0.0000000000000 0.0000000000000 0.0000000000000 218.6131737796292 0.0000000000000 19.5830025118444 27.6402943293244 0.0000000000000 1.5519946157041 1897.1856755097001 13.5079951911922 0.0000000000000 0.2648773399666 +30.4544423458127 5327.2484726615139 0.0000000000000 642.3989612307510 0.0000000000000 167.6949763267031 452.9317939728962 0.0000000000000 101.7651554797545 0.0000000000000 0.0000000000000 0.0000000000000 896.2896978235838 0.0000000000000 141.2442344718316 0.0000000000000 0.0000000000000 0.0000000000000 224.7114875701887 0.0000000000000 21.1491447553209 28.5624121536236 0.0000000000000 1.6912273554951 1980.0330752136260 13.9868628584355 0.0000000000000 0.2924981262715 +31.0000000000000 5286.0021049855850 0.0000000000000 579.6148400595312 0.0000000000000 155.9249329218449 435.6355289879520 0.0000000000000 101.5424626205874 0.0000000000000 0.0000000000000 0.0000000000000 908.2006916273091 0.0000000000000 150.5868678969020 0.0000000000000 0.0000000000000 0.0000000000000 231.9532961818498 0.0000000000000 23.1279721890253 29.6906273362572 0.0000000000000 1.8716630616178 2080.9373855713748 14.5827079405230 0.0000000000000 0.3289186196363 +31.9212556007658 5219.1798269838382 0.0000000000000 492.5981194775942 0.0000000000000 140.8844248115028 397.3467112455997 0.0000000000000 98.0666399024325 0.0000000000000 0.0000000000000 0.0000000000000 914.8236742164354 0.0000000000000 163.5197461966724 0.0000000000000 0.0000000000000 0.0000000000000 243.6805182915678 0.0000000000000 26.6361797554925 31.6386811591535 0.0000000000000 2.2098333678518 2253.3743946571331 15.6419500894388 0.0000000000000 0.3992998452838 +32.0000000000000 5213.6673718869361 0.0000000000000 486.0694924272630 0.0000000000000 139.8198120577829 393.8128761986083 0.0000000000000 97.6670439294570 0.0000000000000 0.0000000000000 0.0000000000000 914.5910709945376 0.0000000000000 164.4436533445644 0.0000000000000 0.0000000000000 0.0000000000000 244.6390468917869 0.0000000000000 26.9417452052199 31.8070881538646 0.0000000000000 2.2406672984891 2268.1586358504819 15.7356250140387 0.0000000000000 0.4058707469664 +32.9624221622297 5149.1089349945632 0.0000000000000 415.4568928595178 0.0000000000000 128.8733748995759 350.0118085244087 0.0000000000000 92.2607039187681 0.0000000000000 0.0000000000000 0.0000000000000 902.3217776383783 0.0000000000000 173.4658910069734 0.0000000000000 0.0000000000000 0.0000000000000 255.5823611056741 0.0000000000000 30.6859536675472 33.8771043112583 0.0000000000000 2.6407678631292 2448.2997170630092 16.9208802225548 0.0000000000000 0.4938319246374 +33.0000000000000 5146.6964578495408 0.0000000000000 413.0050450814590 0.0000000000000 128.5111279994531 348.3157285381905 0.0000000000000 92.0405318603777 0.0000000000000 0.0000000000000 0.0000000000000 901.5133095220392 0.0000000000000 173.7367585982010 0.0000000000000 0.0000000000000 0.0000000000000 255.9768830632731 0.0000000000000 30.8313125548218 33.9580769325025 0.0000000000000 2.6572210348889 2455.2913063288147 16.9686758381187 0.0000000000000 0.4975647983142 +34.0000000000000 5085.5107135391381 0.0000000000000 354.5798372002890 0.0000000000000 120.1679870055208 304.7961882557869 0.0000000000000 86.2712620430972 0.0000000000000 0.0000000000000 0.0000000000000 872.3435610119662 0.0000000000000 178.9608118447309 0.0000000000000 0.0000000000000 0.0000000000000 265.4134483084016 0.0000000000000 34.6233432305920 36.1008457187446 0.0000000000000 3.1153653333105 2639.2285728660518 18.2823802587823 0.0000000000000 0.6056833835834 +35.0000000000000 5029.9881831062403 0.0000000000000 306.9776377981875 0.0000000000000 113.6178903026826 265.5365673658321 0.0000000000000 80.9778363474132 0.0000000000000 0.0000000000000 0.0000000000000 831.3799614036902 0.0000000000000 180.9492998592380 0.0000000000000 0.0000000000000 0.0000000000000 272.5997891891182 0.0000000000000 38.1880117658680 38.1870872646796 0.0000000000000 3.6062725908587 2817.5843030776809 19.6755326104678 0.0000000000000 0.7316273180383 +36.0000000000000 4979.7625596380003 0.0000000000000 267.6116154754171 0.0000000000000 108.1195395771110 231.1584238357198 0.0000000000000 76.3082269562094 0.0000000000000 0.0000000000000 0.0000000000000 782.6875466395272 0.0000000000000 180.5268556879513 0.0000000000000 0.0000000000000 0.0000000000000 277.3814754564780 0.0000000000000 41.4400667508076 40.1683608234167 0.0000000000000 4.1196459502722 2988.6941505249938 21.1450979184904 0.0000000000000 0.8764347656001 +37.0000000000000 4934.3770181065593 0.0000000000000 234.6645775379008 0.0000000000000 103.2141507639080 201.5121154081124 0.0000000000000 72.2099369977951 0.0000000000000 0.0000000000000 0.0000000000000 729.6651979939847 0.0000000000000 178.3805290015978 0.0000000000000 0.0000000000000 0.0000000000000 279.7612935187731 0.0000000000000 44.3312942593547 42.0002576123753 0.0000000000000 4.6448574389222 3151.5117332569594 22.6862895766020 0.0000000000000 1.0407485271505 +38.0000000000000 4893.3608867351313 0.0000000000000 206.8177819072621 0.0000000000000 98.6270669506357 176.1305753761092 0.0000000000000 68.5644359859948 0.0000000000000 0.0000000000000 0.0000000000000 674.9556514208091 0.0000000000000 175.0340343126105 0.0000000000000 0.0000000000000 0.0000000000000 279.8564827547477 0.0000000000000 46.8410499539544 43.6451071450177 0.0000000000000 5.1717437789972 3305.4775883821235 24.2927756977239 0.0000000000000 1.2248195988781 +39.0000000000000 4856.2657085773635 0.0000000000000 183.0868265395950 0.0000000000000 94.2023525614801 154.4533164527852 0.0000000000000 65.2491720270971 0.0000000000000 0.0000000000000 0.0000000000000 620.5049446865155 0.0000000000000 170.8650907162343 0.0000000000000 0.0000000000000 0.0000000000000 277.8599299920896 0.0000000000000 48.9676764447588 45.0733973057291 0.0000000000000 5.6910936559929 3450.3949944296610 25.9569620146498 0.0000000000000 1.4285345960442 +40.0000000000000 4822.6802502729570 0.0000000000000 162.7193538324189 0.0000000000000 89.8593179818286 135.9308429902659 0.0000000000000 62.1621992425593 0.0000000000000 0.0000000000000 0.0000000000000 567.6752913800037 0.0000000000000 166.1363558890616 0.0000000000000 0.0000000000000 0.0000000000000 274.0085579323552 0.0000000000000 50.7217224208859 46.2641992086966 0.0000000000000 6.1948969614299 3586.3252467171105 27.6703084189088 0.0000000000000 1.6514567515151 +41.0000000000000 4792.2348662971863 0.0000000000000 145.1291867438638 0.0000000000000 85.5637356169803 120.0680554118096 0.0000000000000 59.2284987145326 0.0000000000000 0.0000000000000 0.0000000000000 517.3661284430166 0.0000000000000 161.0265402515643 0.0000000000000 0.0000000000000 0.0000000000000 268.5592686920062 0.0000000000000 52.1210142495145 47.2048907471729 0.0000000000000 6.6764279759602 3713.5048665021068 29.4236474169209 0.0000000000000 1.8928729373631 +42.0000000000000 4764.6007166826221 0.0000000000000 129.8524752852635 0.0000000000000 81.3089280844934 106.4375280988898 0.0000000000000 56.3981279409257 0.0000000000000 0.0000000000000 0.0000000000000 470.1239554961630 0.0000000000000 155.6565026884284 0.0000000000000 0.0000000000000 0.0000000000000 261.7716032404484 0.0000000000000 53.1872922618084 47.8904300536518 0.0000000000000 7.1302224310952 3832.2828930043138 31.2074830795169 0.0000000000000 2.1518416523769 +43.0000000000000 4739.4867071094804 0.0000000000000 116.5175240462133 0.0000000000000 77.1034905227126 94.6787275126546 0.0000000000000 53.6413846797200 0.0000000000000 0.0000000000000 0.0000000000000 426.2343591508300 0.0000000000000 150.1090954268037 0.0000000000000 0.0000000000000 0.0000000000000 253.8958960456962 0.0000000000000 53.9440397753082 48.3223752104396 0.0000000000000 7.5519932487498 3943.0749109284307 33.0122574603555 0.0000000000000 2.4272388826018 +44.0000000000000 4716.6356175791452 0.0000000000000 104.8233919808899 0.0000000000000 72.9634604870774 84.4913621103535 0.0000000000000 50.9434828749243 0.0000000000000 0.0000000000000 0.0000000000000 385.7955904758804 0.0000000000000 144.4432578892774 0.0000000000000 0.0000000000000 0.0000000000000 245.1656995543130 0.0000000000000 54.4151598417806 48.5077942734825 0.0000000000000 7.9385159473863 4046.3302890090922 34.8285778209427 0.0000000000000 2.7178001554502 +45.0000000000000 4695.8201420561227 0.0000000000000 94.5243014421447 0.0000000000000 68.9074551699564 75.6269062894072 0.0000000000000 48.2998175794449 0.0000000000000 0.0000000000000 0.0000000000000 348.7755810187546 0.0000000000000 138.7034643076307 0.0000000000000 0.0000000000000 0.0000000000000 235.7934019151220 0.0000000000000 54.6242206844440 48.4581655671104 0.0000000000000 8.2875037122061 4142.5094796568728 36.6474025463496 0.0000000000000 3.0221580544300 +46.0000000000000 4676.8391711399190 0.0000000000000 85.4180182698523 0.0000000000000 64.9537729745343 67.8801834548382 0.0000000000000 45.7121753612605 0.0000000000000 0.0000000000000 0.0000000000000 315.0550996960125 0.0000000000000 132.9257028764651 0.0000000000000 0.0000000000000 0.0000000000000 225.9681549770762 0.0000000000000 54.5940618818880 48.1883334594067 0.0000000000000 8.5974840810218 4232.0687800719988 38.4601867038551 0.0000000000000 3.3388750518673 +47.0000000000000 4659.5144486570316 0.0000000000000 77.3370278502916 0.0000000000000 61.1187803638289 61.0817947621411 0.0000000000000 43.1859054307383 0.0000000000000 0.0000000000000 0.0000000000000 284.4597806058847 0.0000000000000 127.1410157777256 0.0000000000000 0.0000000000000 0.0000000000000 215.8554186016611 0.0000000000000 54.3466154957971 47.7155587179797 0.0000000000000 8.8676836756887 4315.4505080790177 40.2589901275650 0.0000000000000 3.6664718546437 +48.0000000000000 4643.6876325692419 0.0000000000000 70.1417321527958 0.0000000000000 57.4161253515511 55.0916388861248 0.0000000000000 40.7279248038973 0.0000000000000 0.0000000000000 0.0000000000000 256.7834525334731 0.0000000000000 121.3774141084225 0.0000000000000 0.0000000000000 0.0000000000000 205.5975920790827 0.0000000000000 53.9028452949367 47.0586836752650 0.0000000000000 9.0979238407408 4393.0770310732569 42.0365520042357 0.0000000000000 4.0034516269694 +49.0000000000000 4629.2177431873788 0.0000000000000 63.7151406278418 0.0000000000000 53.8564693399723 49.7935163784173 0.0000000000000 38.3453890618753 0.0000000000000 0.0000000000000 0.0000000000000 231.8048009114360 0.0000000000000 115.6607707395383 0.0000000000000 0.0000000000000 0.0000000000000 195.3153386905798 0.0000000000000 53.2827432721691 46.2374192990154 0.0000000000000 9.2885278945976 4465.3474836475425 43.7863364321871 0.0000000000000 4.3483205174417 +50.0000000000000 4615.9789618213899 0.0000000000000 57.9586859488351 0.0000000000000 50.4475316233904 45.0907075240636 0.0000000000000 36.0448651869713 0.0000000000000 0.0000000000000 0.0000000000000 209.2989964739303 0.0000000000000 110.0151154062884 0.0000000000000 0.0000000000000 0.0000000000000 185.1093164747527 0.0000000000000 52.5053478122009 45.2717525073075 0.0000000000000 9.4402395076308 4532.6363212785736 45.5025535153253 0.0000000000000 4.6996049193344 \ No newline at end of file diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_osecirvvs.py b/pycode/memilio-simulation/memilio/simulation_test/test_osecirvvs.py new file mode 100644 index 0000000000..d0580c5987 --- /dev/null +++ b/pycode/memilio-simulation/memilio/simulation_test/test_osecirvvs.py @@ -0,0 +1,214 @@ +############################################################################# +# Copyright (C) 2020-2024 MEmilio +# +# Authors: Maximilian Betz +# +# Contact: Martin J. Kuehn +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# + +import unittest + +import os +import numpy as np +import pandas as pd + +from memilio.simulation import AgeGroup, ContactMatrix, Damping, SimulationDay +from memilio.simulation.osecirvvs import InfectionState +from memilio.simulation.osecirvvs import Model, Simulation, simulate, simulate_flows + + +class Test_osecirvvs_integration(unittest.TestCase): + + here = os.path.dirname(os.path.abspath(__file__)) + + def setUp(self): + + model = Model(1) + + self.t0 = 0 + self.tmax = 50 + self.dt = 0.1 + + cont_freq = 10 + nb_total_t0, nb_exp_t0, nb_inf_t0, nb_car_t0, nb_hosp_t0, nb_icu_t0, nb_rec_t0, nb_dead_t0 = 10000, 100, 50, 50, 20, 10, 10, 0 + + A0 = AgeGroup(0) + + model.populations[A0, InfectionState.ExposedNaive] = nb_exp_t0 + model.populations[A0, InfectionState.ExposedImprovedImmunity] = 0 + model.populations[A0, InfectionState.ExposedPartialImmunity] = 0 + model.populations[A0, + InfectionState.InfectedNoSymptomsNaive] = nb_car_t0 + model.populations[A0, + InfectionState.InfectedNoSymptomsNaiveConfirmed] = 0 + model.populations[A0, + InfectionState.InfectedNoSymptomsPartialImmunity] = 0 + model.populations[A0, + InfectionState.InfectedNoSymptomsPartialImmunityConfirmed] = 0 + model.populations[A0, + InfectionState.InfectedNoSymptomsImprovedImmunity] = 0 + model.populations[A0, + InfectionState.InfectedNoSymptomsImprovedImmunityConfirmed] = 0 + model.populations[A0, InfectionState.InfectedSymptomsNaive] = nb_inf_t0 + model.populations[A0, + InfectionState.InfectedSymptomsNaiveConfirmed] = 0 + model.populations[A0, + InfectionState.InfectedSymptomsPartialImmunity] = 0 + model.populations[A0, + InfectionState.InfectedSymptomsPartialImmunityConfirmed] = 0 + model.populations[A0, + InfectionState.InfectedSymptomsImprovedImmunity] = 0 + model.populations[A0, + InfectionState.InfectedSymptomsImprovedImmunityConfirmed] = 0 + model.populations[A0, InfectionState.InfectedSevereNaive] = nb_hosp_t0 + model.populations[A0, + InfectionState.InfectedSevereImprovedImmunity] = 0 + model.populations[A0, InfectionState.InfectedSeverePartialImmunity] = 0 + model.populations[A0, InfectionState.InfectedCriticalNaive] = nb_icu_t0 + model.populations[A0, + InfectionState.InfectedCriticalPartialImmunity] = 0 + model.populations[A0, + InfectionState.InfectedCriticalImprovedImmunity] = 0 + model.populations[A0, + InfectionState.SusceptibleImprovedImmunity] = nb_rec_t0 + model.populations[A0, InfectionState.SusceptiblePartialImmunity] = 0 + model.populations[A0, InfectionState.DeadNaive] = nb_dead_t0 + model.populations[A0, InfectionState.DeadPartialImmunity] = 0 + model.populations[A0, InfectionState.DeadImprovedImmunity] = 0 + model.populations.set_difference_from_group_total_AgeGroup( + (A0, InfectionState.SusceptibleNaive), nb_total_t0) + + model.parameters.ICUCapacity.value = 10000 + model.parameters.TestAndTraceCapacity.value = 10000 + model.parameters.DailyFirstVaccination.resize_SimulationDay( + SimulationDay(1000)) + model.parameters.DailyFirstVaccination[:, :] = 0 + model.parameters.DailyFullVaccination.resize_SimulationDay( + SimulationDay(1000)) + model.parameters.DailyFullVaccination[:, :] = 0 + + model.parameters.TimeExposed[A0] = 3.2 + model.parameters.TimeInfectedNoSymptoms[A0] = 2.0 + model.parameters.TimeInfectedSymptoms[A0] = 5.0 + model.parameters.TimeInfectedSevere[A0] = 10.0 + model.parameters.TimeInfectedCritical[A0] = 8.0 + + contacts = ContactMatrix(np.r_[cont_freq]) + contacts.add_damping( + Damping(coeffs=np.r_[0.7], t=30.0, level=0, type=0)) + model.parameters.ContactPatterns.cont_freq_mat[0] = contacts + + model.parameters.TransmissionProbabilityOnContact[A0] = 0.05 + model.parameters.RelativeTransmissionNoSymptoms[A0] = 1 + model.parameters.RecoveredPerInfectedNoSymptoms[A0] = 0.09 + model.parameters.RiskOfInfectionFromSymptomatic[A0] = 0.25 + model.parameters.SeverePerInfectedSymptoms[A0] = 0.2 + model.parameters.CriticalPerSevere[A0] = 0.25 + model.parameters.DeathsPerCritical[A0] = 0.3 + + # TODO: Reduction not possible like this, division by zero! + model.parameters.ReducExposedPartialImmunity[A0] = 1.0 + model.parameters.ReducExposedImprovedImmunity[A0] = 1.0 + model.parameters.ReducInfectedSymptomsPartialImmunity[A0] = 1.0 + model.parameters.ReducInfectedSymptomsImprovedImmunity[A0] = 0 + model.parameters.ReducInfectedSevereCriticalDeadPartialImmunity[A0] = 0 + model.parameters.ReducInfectedSevereCriticalDeadImprovedImmunity[A0] = 0 + model.parameters.ReducTimeInfectedMild[A0] = 1 + + model.parameters.Seasonality.value = 0.2 + + model.apply_constraints() + + self.model = model + + def test_simulate_simple(self): + result = simulate(t0=0., tmax=100., dt=0.1, model=self.model) + self.assertAlmostEqual(result.get_time(0), 0.) + self.assertAlmostEqual(result.get_time(1), 0.1) + self.assertAlmostEqual(result.get_last_time(), 100.) + self.assertEqual(len(result.get_last_value()), 27) + + def test_flow_simulation_simple(self): + flow_sim_results = simulate_flows( + t0=0., tmax=100., dt=0.1, model=self.model) + flows = flow_sim_results[1] + self.assertEqual(flows.get_time(0), 0.) + self.assertEqual(flows.get_last_time(), 100.) + self.assertEqual(len(flows.get_last_value()), 45) + + compartments = flow_sim_results[0] + self.assertEqual(compartments.get_time(0), 0.) + self.assertEqual(compartments.get_last_time(), 100.) + self.assertEqual(len(compartments.get_last_value()), 27) + + def test_simulation_simple(self): + sim = Simulation(self.model, t0=0., dt=0.1) + sim.advance(tmax=100.) + result = sim.result + self.assertAlmostEqual(result.get_time(0), 0.) + self.assertAlmostEqual(result.get_time(1), 0.1) + self.assertAlmostEqual(result.get_last_time(), 100.) + + def test_compare_with_cpp(self): + """ + Tests the correctness of the python bindings. The results of a simulation + in python get compared to the results of a cpp simulation. Cpp simulation + results contained in the file ode-sceirvvs-compare.csv. + If cpp model changes this test needs to be adjusted accordingly. + """ + refData = pd.read_csv( + os.path.join(self.here + '/data/ode-secirvvs-compare.csv'), + sep=r' ', engine='python') + + result = simulate(t0=self.t0, tmax=self.tmax, + dt=self.dt, model=self.model) + + for index_timestep, timestep in refData.iterrows(): + # compare time + t = float(timestep.at['Time']) + self.assertAlmostEqual( + t, result.get_time(index_timestep), + delta=1e-10) + + # compare compartments + for index_compartment in range(0, 27): + self.assertAlmostEqual( + timestep[index_compartment+1], + result[index_timestep][index_compartment], delta=1e-10) + + def test_check_constraints_parameters(self): + + model = Model(1) + A0 = AgeGroup(0) + + model.parameters.CriticalPerSevere[A0] = 0.25 + model.parameters.TimeInfectedSymptoms[A0] = 5.0 + model.parameters.TransmissionProbabilityOnContact[A0] = 0.05 + self.assertEqual(model.parameters.check_constraints(), 0) + + model.parameters.CriticalPerSevere[A0] = 2. + self.assertEqual(model.parameters.check_constraints(), 1) + + model.parameters.CriticalPerSevere[A0] = 0.25 + model.parameters.TimeInfectedSymptoms[A0] = 0 + self.assertEqual(model.parameters.check_constraints(), 1) + + model.parameters.TimeInfectedSymptoms[A0] = 5. + model.parameters.TransmissionProbabilityOnContact[A0] = -1. + self.assertEqual(model.parameters.check_constraints(), 1) + + +if __name__ == '__main__': + unittest.main()