-
Notifications
You must be signed in to change notification settings - Fork 25
1213 PAIS for ABM #1587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
1213 PAIS for ABM #1587
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import matplotlib.pyplot as plt | ||
| import pandas as pd | ||
|
|
||
| df = pd.read_csv("active_pais.txt", index_col=0, parse_dates=True) | ||
|
|
||
| plt.figure(figsize=(10, 6)) | ||
| for column in df.columns: | ||
| plt.plot(df.index, df[column], label=column) | ||
| plt.legend(["0-4", "5-14", "15-34", "35-59", "60-79", "80+"], title="Age Groups") | ||
| plt.title("Active PAIS Over Time by Age Group") | ||
| plt.xlabel("Time in Days since 01.01.1970") | ||
| plt.ylabel("Number of Individuals") | ||
| plt.show() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,7 +178,6 @@ struct LogInfectionState : mio::LogAlways { | |
| Eigen::VectorX<ScalarType> sum = | ||
| Eigen::VectorX<ScalarType>::Zero(Eigen::Index(mio::abm::InfectionState::Count)); | ||
| auto curr_time = sim.get_time(); | ||
| PRAGMA_OMP(for) | ||
| for (auto& location : sim.get_model().get_locations()) { | ||
| for (uint32_t inf_state = 0; inf_state < (int)mio::abm::InfectionState::Count; inf_state++) { | ||
| sum[inf_state] += sim.get_model().get_subpopulation(location.get_id(), curr_time, | ||
|
|
@@ -189,6 +188,30 @@ struct LogInfectionState : mio::LogAlways { | |
| } | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Looger to log the TimeSeries of the number of Person%s that have an active PAIS. | ||
| */ | ||
| struct LogPAIS : mio::LogAlways { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LogCurrentlyActiveAmountOfPais? |
||
| using Type = std::pair<mio::abm::TimePoint, ScalarType>; | ||
| /** | ||
| * @brief Log the TimeSeries of the number of Person%s that have an active PAIS. | ||
| * @param[in] sim The simulation of the abm. | ||
| * @return A pair of the TimePoint and the TimeSeries of the number of Person%s that have an active PAIS. | ||
| */ | ||
| static Type log(const mio::abm::Simulation<>& sim) | ||
| { | ||
| ScalarType sum = 0; | ||
| auto curr_time = sim.get_time(); | ||
| for (auto& person : sim.get_model().get_persons()) { | ||
| auto person_id = person.get_id(); | ||
| if (sim.get_model().get_person(person_id).has_active_pais(curr_time)) { | ||
| sum++; | ||
| } | ||
| } | ||
| return std::make_pair(curr_time, sum); | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * @brief This is like the DataWriterToMemory, but it only logs time series data. | ||
| * @tparam Loggers The loggers that are used to log data. The loggers must return a touple with a TimePoint and a value. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -144,6 +144,26 @@ InfectionState Infection::get_infection_state(TimePoint t) const | |
| return std::prev(it)->second; | ||
| } | ||
|
|
||
| std::pair<TimePoint, InfectionState> Infection::get_highest_infection_state() const | ||
| { | ||
| if (m_infection_course.back().second == InfectionState::Dead) { | ||
| return m_infection_course.back(); | ||
| } | ||
| else { | ||
| return m_infection_course[m_infection_course.size() - 2]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. short comment why this leads to the desired case and why this is true also: name sounds like over all infections but its about the last infection course no? |
||
| } | ||
| } | ||
|
|
||
| TimePoint Infection::get_infection_state_start_date(InfectionState state) const | ||
| { | ||
| for (const auto& [time_point, inf_state] : m_infection_course) { | ||
| if (inf_state == state) { | ||
| return time_point; | ||
| } | ||
| } | ||
| return TimePoint(-1); // invalid TimePoint | ||
| } | ||
|
|
||
| void Infection::set_detected() | ||
| { | ||
| m_detected = true; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| #ifndef MIO_ABM_MODEL_H | ||
| #define MIO_ABM_MODEL_H | ||
|
|
||
| #include "abm/sex.h" | ||
| #include "abm/infection_state.h" | ||
| #include "abm/model_functions.h" | ||
| #include "abm/location_type.h" | ||
|
|
@@ -203,9 +204,10 @@ class Model | |
| * @brief Add a Person to the Model. | ||
| * @param[in] id The LocationID of the initial Location of the Person. | ||
| * @param[in] age AgeGroup of the person. | ||
| * @param[in] sex Sex of the person. | ||
| * @return Id of the newly created Person. | ||
| */ | ||
| PersonId add_person(const LocationId id, AgeGroup age); | ||
| PersonId add_person(const LocationId id, AgeGroup age, Sex sex = Sex::Male); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we want unisex as I think we model with that 95% of the time. (also this could be political, think "migration") |
||
|
|
||
| /** | ||
| * @brief Adds a copy of a given Person to the Model. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| /* | ||
| * Copyright (C) 2020-2026 MEmilio | ||
| * | ||
| * Authors: David Kerkmann | ||
| * | ||
| * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de> | ||
| * | ||
| * 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 "abm/pais.h" | ||
| #include "abm/person.h" | ||
| #include "abm/random_events.h" | ||
|
|
||
| namespace mio | ||
| { | ||
| namespace abm | ||
| { | ||
|
|
||
| void PAIS::update_severity(const Parameters& params, PersonalRandomNumberGenerator& rng, TimePoint t, TimeSpan dt) | ||
| { | ||
| if (severity.empty() || t > severity.back().first) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it should be t >... or? We only proceed when the last update was before t and this is describing this and we return then ( or am I stupid?) |
||
| return; // only update if the last update was before t | ||
| } | ||
| std::pair<PAISState, ScalarType> transmission_probs[static_cast<uint32_t>(PAISState::Count)]; | ||
|
|
||
| for (auto&& v : enum_members<PAISState>()) { | ||
| transmission_probs[static_cast<uint32_t>(v)] = { | ||
| v, params.get<PAISTransitionMatrix>()(static_cast<Eigen::Index>(severity.back().second), | ||
| static_cast<Eigen::Index>(v))}; | ||
| } | ||
| auto severity_new = random_transition(rng, severity.back().second, dt, transmission_probs); | ||
| if (severity_new != severity.back().second) { | ||
| this->severity.push_back({t, severity_new}); // only update if there is a change in severity | ||
| } | ||
| } | ||
|
|
||
| void PAIS::init_or_refresh(const Parameters& params, Person& p, PersonalRandomNumberGenerator& rng, | ||
| const Infection& inf, TimePoint t) | ||
| { | ||
| // get highest InfectionState of the new infection | ||
| auto highest_state = inf.get_highest_infection_state(); | ||
| if (highest_state.second != InfectionState::Dead) { | ||
| // if the Person already had an active PAIS and gets a reinfection, refresh the PAIS status | ||
| if (get_severity(t) != PAISState::Count) { | ||
| add_new_severity(t, highest_state); | ||
| } | ||
| else { | ||
| // base probability of developing PAIS based on age, sex, virus variant and number of vaccinations | ||
| ScalarType pais_prob = params.get<PAISProbability>()[{inf.get_virus_variant(), p.get_age(), p.get_sex(), | ||
| get_vaccination_class(p.get_vaccinations().size())}]; | ||
| // increase probability of developing PAIS if the Person had a severe acute infection or worse | ||
| if (highest_state.second == InfectionState::InfectedSevere || | ||
| highest_state.second == InfectionState::InfectedCritical) { | ||
| pais_prob *= params.get<PAISProbabilitySeverityFactor>()[{ | ||
| inf.get_virus_variant(), get_vaccination_class(p.get_vaccinations().size())}]; | ||
| } | ||
| // reduce probability of developing PAIS if the Person has not had PAIS after an earlier infection | ||
| if (!p.get_infections().empty() && get_severity(t) == PAISState::Count) { | ||
| pais_prob *= params.get<PAISProtectionAtSecondInfection>()[{ | ||
| inf.get_virus_variant(), get_vaccination_class(p.get_vaccinations().size())}]; | ||
| } | ||
|
|
||
| auto& uniform_dist = UniformDistribution<ScalarType>::get_instance(); | ||
| if (uniform_dist(rng) < pais_prob) { | ||
| TimePoint time_recovered = inf.get_infection_state_start_date(InfectionState::Recovered); | ||
| add_new_severity(time_recovered, highest_state); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void PAIS::add_new_severity(TimePoint t, std::pair<TimePoint, InfectionState> highest_state) | ||
| { | ||
| PAISState severity_new; | ||
| if (highest_state.second == InfectionState::InfectedSevere || | ||
| highest_state.second == InfectionState::InfectedCritical) { | ||
| severity_new = PAISState::Severe; | ||
| } | ||
| else { | ||
| severity_new = PAISState::Medium; | ||
| } | ||
| if (severity.empty() || (t > severity.back().first && severity_new != severity.back().second)) { | ||
| this->severity.push_back({t, severity_new}); | ||
| } | ||
| } | ||
|
|
||
| PAISState PAIS::get_severity(TimePoint t) const | ||
| { | ||
| if (severity.empty()) { | ||
| return PAISState::Count; | ||
| } | ||
| if (t < severity[0].first) { | ||
| return PAISState::Count; | ||
| } | ||
|
|
||
| auto it = std::upper_bound(severity.begin(), severity.end(), t, | ||
| [](const TimePoint& s, const std::pair<TimePoint, PAISState>& state) { | ||
| return state.first > s; | ||
| }); | ||
| return std::prev(it)->second; | ||
| } | ||
|
|
||
| } // namespace abm | ||
| } // namespace mio | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a note also to myself but I think we should leave OMP out for loggers atm (as you did), I've only felt they produce more headache than an performance upgrade...
ill take a closer look at them if I do the performance analysis with parallelization