diff --git a/cpp/benchmarks/abm.cpp b/cpp/benchmarks/abm.cpp index 0a16c654d3..5e195b540f 100644 --- a/cpp/benchmarks/abm.cpp +++ b/cpp/benchmarks/abm.cpp @@ -72,8 +72,8 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list //some % of people are infected, large enough to have some infection activity without everyone dying auto pct_infected = 0.05; if (mio::UniformDistribution::get_instance()(prng, 0.0, 1.0) < pct_infected) { - auto state = mio::abm::InfectionState( - mio::UniformIntDistribution::get_instance()(prng, 1, int(mio::abm::InfectionState::Count) - 1)); + auto state = mio::abm::SymptomState( + mio::UniformIntDistribution::get_instance()(prng, 1, int(mio::abm::SymptomState::Count) - 1)); auto infection = mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, person.get_age(), model.parameters, mio::abm::TimePoint(0), state); person.add_new_infection(std::move(infection)); @@ -107,7 +107,7 @@ mio::abm::Simulation<> make_simulation(size_t num_persons, std::initializer_list }); auto random_criteria = [&]() { auto random_ages = sample(ages, 2); - auto random_states = std::vector(0); + auto random_states = std::vector(0); return mio::abm::TestingCriteria(random_ages, random_states); }; diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index f4240e44fa..5f0d3b5773 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -73,7 +73,7 @@ int main() auto model = mio::abm::Model(num_age_groups); mio::ParameterDistributionLogNormal log_norm(4., 1.); // Set same infection parameter for all age groups. For example, the incubation period is log normally distributed with parameters 4 and 1. - model.parameters.get() = mio::ParameterDistributionLogNormal(4., 1.); + model.parameters.get() = mio::ParameterDistributionLogNormal(4., 1.); // Set the age group the can go to school is AgeGroup(1) (i.e. 5-14) model.parameters.get()[age_group_5_to_14] = true; @@ -144,11 +144,11 @@ int main() auto persons = model.get_persons(); for (auto& person : persons) { auto rng = mio::abm::PersonalRandomNumberGenerator(model.get_rng(), person); - mio::abm::InfectionState infection_state = - (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); - if (infection_state != mio::abm::InfectionState::Susceptible) + mio::abm::SymptomState symptom_state = + (mio::abm::SymptomState)(rand() % ((uint32_t)mio::abm::SymptomState::Count)); + if (symptom_state != mio::abm::SymptomState::Count) person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - model.parameters, start_date, infection_state)); + model.parameters, start_date, symptom_state)); } // Assign locations to the people diff --git a/cpp/examples/graph_abm.cpp b/cpp/examples/graph_abm.cpp index 7050dc89a3..dad57cc795 100644 --- a/cpp/examples/graph_abm.cpp +++ b/cpp/examples/graph_abm.cpp @@ -20,7 +20,7 @@ #include "abm/household.h" #include "abm/model.h" -#include "abm/infection_state.h" +#include "abm/symptom_state.h" #include "abm/location_type.h" #include "abm/time.h" #include "abm/person_id.h" diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index ae04b68b2d..d7ac787036 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -29,7 +29,7 @@ add_library(abm lockdown_rules.h infection.cpp infection.h - infection_state.h + symptom_state.h virus_variant.h protection_event.h mask.h diff --git a/cpp/models/abm/common_abm_loggers.h b/cpp/models/abm/common_abm_loggers.h index 0d91c69c24..a8a212c259 100644 --- a/cpp/models/abm/common_abm_loggers.h +++ b/cpp/models/abm/common_abm_loggers.h @@ -21,7 +21,7 @@ #ifndef ABM_COMMON_LOGGERS_H #define ABM_COMMON_LOGGERS_H -#include "abm/infection_state.h" +#include "abm/symptom_state.h" #include "abm/person_id.h" #include "abm/simulation.h" #include "memilio/io/history.h" @@ -48,7 +48,7 @@ struct mobility_data { mio::abm::TimePoint end_time; mio::abm::TransportMode transport_mode; mio::abm::ActivityType activity_type; - mio::abm::InfectionState infection_state; + mio::abm::SymptomState symptom_state; }; constexpr mio::abm::ActivityType guess_activity_type(mio::abm::LocationType current_location) @@ -138,7 +138,7 @@ struct LogPersonInformation : mio::LogOnce { */ struct LogDataForMobility : mio::LogAlways { using Type = std::vector>; + mio::abm::TransportMode, mio::abm::ActivityType, mio::abm::SymptomState>>; /** * @brief Log the mobility data of the agents in the simulation. * @param[in] sim The simulation of the ABM. @@ -148,7 +148,7 @@ struct LogDataForMobility : mio::LogAlways { * -# The time point. * -# The transport mode. * -# The activity type. - * -# The infection state. + * -# The symptom state. */ static Type log(const mio::abm::Simulation<>& sim) { @@ -156,33 +156,32 @@ struct LogDataForMobility : mio::LogAlways { for (Person p : sim.get_model().get_persons()) { mobility_data.push_back( std::make_tuple(p.get_id(), p.get_location(), sim.get_time(), p.get_last_transport_mode(), - guess_activity_type(p.get_location_type()), p.get_infection_state(sim.get_time()))); + guess_activity_type(p.get_location_type()), p.get_symptom_state(sim.get_time()))); } return mobility_data; } }; /** -* @brief Logger to log the TimeSeries of the number of Person%s in an #InfectionState. +* @brief Logger to log the TimeSeries of the number of Person%s in an #SymptomState. */ -struct LogInfectionState : mio::LogAlways { +struct LogSymptomState : mio::LogAlways { using Type = std::pair>; /** - * @brief Log the TimeSeries of the number of Person%s in an #InfectionState. + * @brief Log the TimeSeries of the number of Person%s in an #SymptomState. * @param[in] sim The simulation of the abm. - * @return A pair of the TimePoint and the TimeSeries of the number of Person%s in an #InfectionState. + * @return A pair of the TimePoint and the TimeSeries of the number of Person%s in an #SymptomState. */ static Type log(const mio::abm::Simulation<>& sim) { - Eigen::VectorX sum = - Eigen::VectorX::Zero(Eigen::Index(mio::abm::InfectionState::Count)); - auto curr_time = sim.get_time(); + Eigen::VectorX sum = Eigen::VectorX::Zero(Eigen::Index(mio::abm::SymptomState::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, - mio::abm::InfectionState(inf_state)); + for (uint32_t inf_state = 0; inf_state < (int)mio::abm::SymptomState::Count; inf_state++) { + sum[inf_state] += + sim.get_model().get_subpopulation(location.get_id(), curr_time, mio::abm::SymptomState(inf_state)); } } return std::make_pair(curr_time, sum); diff --git a/cpp/models/abm/infection.cpp b/cpp/models/abm/infection.cpp index 0cd8e869f1..88f2fd6e56 100644 --- a/cpp/models/abm/infection.cpp +++ b/cpp/models/abm/infection.cpp @@ -58,31 +58,28 @@ void Infection::initialize_viral_shed(PersonalRandomNumberGenerator& rng, VirusV } Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, - TimePoint init_date, InfectionState init_state, ProtectionEvent latest_protection, bool detected) + TimePoint init_date, SymptomState init_state, bool recovered, bool detected, + ProtectionEvent latest_protection) : m_virus_variant(virus) , m_detected(detected) { assert(age.get() < params.get_num_groups()); - assert(init_state != InfectionState::Susceptible && - "Initializatin of an Infection must happen with an InfectionState that is not Suscpetible."); - draw_infection_course_forward(rng, age, params, init_date, init_state, latest_protection); - m_viral_load.start_date = draw_infection_course_backward(rng, age, params, init_date, init_state); + draw_symptom_course_forward(rng, age, params, init_date, init_state, recovered, latest_protection); + m_viral_load.start_date = draw_symptom_course_backward(rng, age, params, init_date, init_state, recovered); initialize_viral_load(rng, virus, age, params, latest_protection); initialize_viral_shed(rng, virus, age, params); } Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, - TimePoint init_date, InfectionState init_state, - const InitialInfectionStateDistribution& init_state_dist, ProtectionEvent latest_protection, - bool detected) + TimePoint init_date, SymptomState init_state, + const InitialSymptomStateDistribution& init_state_dist, bool detected, + ProtectionEvent latest_protection) : m_virus_variant(virus) , m_detected(detected) { assert(age.get() < params.get_num_groups()); - assert(init_state != InfectionState::Susceptible && - "Initializatin of an Infection must happen with an InfectionState that is not Suscpetible."); // Draw the first transition and time that the agent has already spent in that state StateTransition first_transition = @@ -90,12 +87,12 @@ Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, Age ScalarType relative_time_in_first_state = init_state_dist[{virus, age}].get(rng); init_date -= first_transition.duration.multiply(relative_time_in_first_state); - m_infection_course.push_back({init_date, first_transition.from_state}); + m_symptom_course.push_back({init_date, first_transition.from_state}); - // Draw the rest of the infection course - draw_infection_course_forward(rng, age, params, init_date + first_transition.duration, first_transition.to_state, - latest_protection); - m_viral_load.start_date = draw_infection_course_backward(rng, age, params, init_date, init_state); + // Draw the rest of the symptom course + draw_symptom_course_forward(rng, age, params, init_date + first_transition.duration, first_transition.to_state, + false, latest_protection); + m_viral_load.start_date = draw_symptom_course_backward(rng, age, params, init_date, init_state, false); initialize_viral_load(rng, virus, age, params, latest_protection); initialize_viral_shed(rng, virus, age, params); @@ -120,7 +117,7 @@ ScalarType Infection::get_viral_load(TimePoint t) const ScalarType Infection::get_viral_shed(TimePoint t) const { - if (m_viral_load.start_date >= t || get_infection_state(t) == InfectionState::Exposed) { + if (m_viral_load.start_date >= t) { return 0; } return m_individual_viral_shed_factor / (1 + exp(-(m_log_norm_alpha + m_log_norm_beta * get_viral_load(t)))); @@ -131,14 +128,14 @@ VirusVariant Infection::get_virus_variant() const return m_virus_variant; } -InfectionState Infection::get_infection_state(TimePoint t) const +SymptomState Infection::get_symptom_state(TimePoint t) const { - if (t < m_infection_course[0].first) { - return InfectionState::Susceptible; + if (t < m_symptom_course[0].first) { + return SymptomState::None; } - auto it = std::upper_bound(m_infection_course.begin(), m_infection_course.end(), t, - [](const TimePoint& s, const std::pair& state) { + auto it = std::upper_bound(m_symptom_course.begin(), m_symptom_course.end(), t, + [](const TimePoint& s, const std::pair& state) { return state.first > s; }); return std::prev(it)->second; @@ -159,8 +156,16 @@ TimePoint Infection::get_start_date() const return m_viral_load.start_date; } +bool Infection::is_recovered(TimePoint t) const +{ + if (get_symptom_state(t) == SymptomState::None && t > m_symptom_course[0].first) { + return true; + } + return false; +} + StateTransition Infection::get_forward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, - const Parameters& params, InfectionState current_state, + const Parameters& params, SymptomState current_state, TimePoint current_time, ProtectionEvent latest_protection) const { auto& uniform_dist = UniformDistribution::get_instance(); @@ -168,26 +173,21 @@ StateTransition Infection::get_forward_transition(PersonalRandomNumberGenerator& switch (current_state) { - case InfectionState::Exposed: - transition.to_state = InfectionState::InfectedNoSymptoms; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); - break; - - case InfectionState::InfectedNoSymptoms: { + case SymptomState::None: { ScalarType p = uniform_dist(rng); if (p < params.get()[{m_virus_variant, age}]) { - transition.to_state = InfectionState::InfectedSymptoms; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + transition.to_state = SymptomState::Moderate; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else { - transition.to_state = InfectionState::Recovered; + transition.to_state = SymptomState::None; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } break; } - case InfectionState::InfectedSymptoms: { + case SymptomState::Moderate: { ScalarType p = uniform_dist(rng); ScalarType severity_protection_factor = get_severity_protection_factor(params, latest_protection, age, current_time); @@ -195,51 +195,51 @@ StateTransition Infection::get_forward_transition(PersonalRandomNumberGenerator& (1 - severity_protection_factor) * params.get()[{m_virus_variant, age}]; if (p < severe_probability) { - transition.to_state = InfectionState::InfectedSevere; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + transition.to_state = SymptomState::Severe; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else { - transition.to_state = InfectionState::Recovered; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + transition.to_state = SymptomState::None; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } break; } - case InfectionState::InfectedSevere: { + case SymptomState::Severe: { ScalarType p = uniform_dist(rng); ScalarType critical_prob = params.get()[{m_virus_variant, age}]; ScalarType death_prob = params.get()[{m_virus_variant, age}]; if (p < death_prob) { - transition.to_state = InfectionState::Dead; + transition.to_state = SymptomState::Dead; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else if (p < critical_prob + death_prob) { - transition.to_state = InfectionState::InfectedCritical; + transition.to_state = SymptomState::Critical; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else { - transition.to_state = InfectionState::Recovered; + transition.to_state = SymptomState::None; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } break; } - case InfectionState::InfectedCritical: { + case SymptomState::Critical: { ScalarType p = uniform_dist(rng); if (p < params.get()[{m_virus_variant, age}]) { - transition.to_state = InfectionState::Dead; + transition.to_state = SymptomState::Dead; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else { - transition.to_state = InfectionState::Recovered; + transition.to_state = SymptomState::None; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } break; } default: - log_error("Forward transition requested for invalid infection state (probably recovered or dead)."); + log_error("Forward transition requested for invalid symptom state (probably dead)."); break; } @@ -247,41 +247,37 @@ StateTransition Infection::get_forward_transition(PersonalRandomNumberGenerator& } StateTransition Infection::get_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, - const Parameters& params, InfectionState current_state) const + const Parameters& params, SymptomState current_state) const { StateTransition transition{current_state, current_state, TimeSpan{}}; switch (current_state) { - case InfectionState::InfectedNoSymptoms: - transition.from_state = InfectionState::Exposed; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); - break; - case InfectionState::InfectedSymptoms: - transition.from_state = InfectionState::InfectedNoSymptoms; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + case SymptomState::Moderate: + transition.from_state = SymptomState::None; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); break; - case InfectionState::InfectedSevere: - transition.from_state = InfectionState::InfectedSymptoms; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + case SymptomState::Severe: + transition.from_state = SymptomState::Moderate; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); break; - case InfectionState::InfectedCritical: - transition.from_state = InfectionState::InfectedSevere; + case SymptomState::Critical: + transition.from_state = SymptomState::Severe; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); break; - case InfectionState::Recovered: + case SymptomState::None: transition = get_recovered_backward_transition(rng, age, params); break; - case InfectionState::Dead: + case SymptomState::Dead: transition = get_dead_backward_transition(rng, age, params); break; default: - log_error("Backward transition requested for invalid infection state (probably susceptible)."); + log_error("Backward transition requested for invalid symptom state."); break; } @@ -303,22 +299,22 @@ StateTransition Infection::get_recovered_backward_transition(PersonalRandomNumbe ScalarType severe_prob = params.get()[{m_virus_variant, age}]; ScalarType critical_prob = params.get()[{m_virus_variant, age}]; - StateTransition transition{InfectionState::InfectedNoSymptoms, InfectionState::Recovered, TimeSpan{}}; + StateTransition transition{SymptomState::None, SymptomState::None, TimeSpan{}}; if (p > symptoms_prob * inv_death) { - transition.from_state = InfectionState::InfectedNoSymptoms; + transition.from_state = SymptomState::None; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else if (p > symptoms_prob * severe_prob * inv_death) { - transition.from_state = InfectionState::InfectedSymptoms; - transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + transition.from_state = SymptomState::Moderate; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else if (p > symptoms_prob * severe_prob * critical_prob * inv_death) { - transition.from_state = InfectionState::InfectedSevere; + transition.from_state = SymptomState::Severe; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else { - transition.from_state = InfectionState::InfectedCritical; + transition.from_state = SymptomState::Critical; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } @@ -331,14 +327,14 @@ StateTransition Infection::get_dead_backward_transition(PersonalRandomNumberGene auto& uniform_dist = UniformDistribution::get_instance(); ScalarType p = uniform_dist(rng); - StateTransition transition{InfectionState::InfectedSevere, InfectionState::Dead, TimeSpan{}}; + StateTransition transition{SymptomState::None, SymptomState::Dead, TimeSpan{}}; if (p < params.get()[{m_virus_variant, age}]) { - transition.from_state = InfectionState::InfectedSevere; + transition.from_state = SymptomState::Severe; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } else { - transition.from_state = InfectionState::InfectedCritical; + transition.from_state = SymptomState::Critical; transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } @@ -363,36 +359,40 @@ ScalarType Infection::get_severity_protection_factor(const Parameters& params, P current_time.days() - latest_protection.time.days()); } -void Infection::draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, - const Parameters& params, TimePoint init_date, InfectionState start_state, - ProtectionEvent latest_protection) +void Infection::draw_symptom_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + TimePoint init_date, SymptomState start_state, bool recovered, + ProtectionEvent latest_protection) { - TimePoint current_time = init_date; - InfectionState current_state = start_state; - m_infection_course.push_back({current_time, current_state}); + TimePoint current_time = init_date; + SymptomState current_state = start_state; + m_symptom_course.push_back({current_time, current_state}); - while (current_state != InfectionState::Recovered && current_state != InfectionState::Dead) { + while (current_state != SymptomState::Dead && !recovered) { StateTransition transition = get_forward_transition(rng, age, params, current_state, current_time, latest_protection); current_time += transition.duration; current_state = transition.to_state; - m_infection_course.push_back({current_time, current_state}); + if (current_state == SymptomState::None) { + recovered = true; + } + m_symptom_course.push_back({current_time, current_state}); } } -TimePoint Infection::draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, - const Parameters& params, TimePoint init_date, - InfectionState init_state) +TimePoint Infection::draw_symptom_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params, TimePoint init_date, + SymptomState init_state, bool recovered) { - TimePoint current_time = init_date; - InfectionState current_state = init_state; + TimePoint current_time = init_date; + SymptomState current_state = init_state; - while (current_state != InfectionState::Exposed) { + while (current_state != SymptomState::None || recovered) { + recovered = false; // Do one step if initial state is recovered, then continue as normal. StateTransition transition = get_backward_transition(rng, age, params, current_state); current_time -= transition.duration; current_state = transition.from_state; - m_infection_course.insert(m_infection_course.begin(), {current_time, current_state}); + m_symptom_course.insert(m_symptom_course.begin(), {current_time, current_state}); } return current_time; diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index bf25631094..fa166de35d 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -23,7 +23,7 @@ #include "abm/personal_rng.h" #include "memilio/io/default_serialize.h" #include "abm/time.h" -#include "abm/infection_state.h" +#include "abm/symptom_state.h" #include "abm/virus_variant.h" #include "abm/parameters.h" @@ -35,11 +35,11 @@ namespace abm { /** - * @brief Represents a transition period between two infection states. + * @brief Represents a transition period between two symptom states. */ struct StateTransition { - InfectionState from_state; - InfectionState to_state; + SymptomState from_state; + SymptomState to_state; TimeSpan duration; // Duration that the infection stays in from_state before transitioning to to_state. }; @@ -68,11 +68,11 @@ struct ViralLoad { }; /** - * @brief Distributions of the relative time that people have been in their initial infection state at the beginning of the simulation. + * @brief Distributions of the relative time that people have been in their initial symptom state at the beginning of the simulation. * Values have to be within [0, 1]. * This makes it possible to draw from a user-defined distribution instead of drawing from a uniform distribution. */ -using InitialInfectionStateDistribution = CustomIndexArray; +using InitialSymptomStateDistribution = CustomIndexArray; class Infection { @@ -85,13 +85,15 @@ class Infection * @param[in] age AgeGroup to determine the ViralLoad course. * @param[in] params Parameters of the Model. * @param[in] init_date Date of initializing the Infection. - * @param[in] init_state [Default: InfectionState::Exposed] #InfectionState at time of initializing the Infection. - * @param[in] latest_protection [Default: {ProtectionType::NoProtection, TimePoint(0)}] The pair value of last ProtectionType (previous Infection/Vaccination) and TimePoint of that protection. - * @param[in] detected [Default: false] If the Infection is detected. + * @param[in] init_state [Default: SymptomState::Exposed] #SymptomState at time of initializing the Infection. + * @param[in] detected [Default: false] If the Infection is detected. + * @param[in] recovered [Default: false] If the Person is already recovered at the time of initializing the Infection. Only works if the Person has start_state = SymptomState::None. + * @param[in] latest_protection [Default: {ProtectionType::NoProtection, TimePoint(0)}] The pair value of last ProtectionType (previous Infection/Vaccination) and TimePoint of that protection. + */ Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, - TimePoint start_date, InfectionState start_state = InfectionState::Exposed, - ProtectionEvent latest_protection = {ProtectionType::NoProtection, TimePoint(0)}, bool detected = false); + TimePoint start_date, SymptomState start_state = SymptomState::None, bool recovered = false, + bool detected = false, ProtectionEvent latest_protection = {ProtectionType::NoProtection, TimePoint(0)}); /** * @brief Create an Infection for a single Person with a time spent in the given initial state that is drawn from the given distribution. @@ -100,14 +102,14 @@ class Infection * @param[in] age AgeGroup to determine the ViralLoad course. * @param[in] params Parameters of the Model. * @param[in] init_date Date of initializing the Infection. - * @param[in] init_state #InfectionState at time of initializing the Infection. + * @param[in] init_state #SymptomState at time of initializing the Infection. * @param[in] init_state_dist Distribution to draw the relative time spent in the initial state from. Values have to be within [0, 1]. - * @param[in] latest_protection The pair value of last ProtectionType (previous Infection/Vaccination) and TimePoint of that protection. * @param[in] detected If the Infection is detected. + * @param[in] latest_protection The pair value of last ProtectionType (previous Infection/Vaccination) and TimePoint of that protection. */ Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, - TimePoint init_date, InfectionState init_state, const InitialInfectionStateDistribution& init_state_dist, - ProtectionEvent latest_protection = {ProtectionType::NoProtection, TimePoint(0)}, bool detected = false); + TimePoint init_date, SymptomState init_state, const InitialSymptomStateDistribution& init_state_dist, + bool detected = false, ProtectionEvent latest_protection = {ProtectionType::NoProtection, TimePoint(0)}); /** * @brief Gets the ViralLoad of the Infection at a given TimePoint. @@ -138,11 +140,11 @@ class Infection VirusVariant get_virus_variant() const; /** - * @brief Get the #InfectionState of the Infection. + * @brief Get the #SymptomState of the Infection. * @param[in] t TimePoint of the querry. - * @return #InfectionState at the given TimePoint. + * @return #SymptomState at the given TimePoint. */ - InfectionState get_infection_state(TimePoint t) const; + SymptomState get_symptom_state(TimePoint t) const; /** * @brief Set the Infection to detected. @@ -159,11 +161,18 @@ class Infection */ TimePoint get_start_date() const; + /** + * @brief If the Person has recovered from the Infection at a given TimePoint. + * @param[in] t TimePoint of the querry. + * @return True if the Person has recovered, false otherwise. + */ + bool is_recovered(TimePoint t) const; + /// This method is used by the default serialization feature. auto default_serialize() { return Members("Infection") - .add("infection_course", m_infection_course) + .add("symptom_course", m_symptom_course) .add("virus_variant", m_virus_variant) .add("viral_load", m_viral_load) .add("log_norm_alpha", m_log_norm_alpha) @@ -177,39 +186,41 @@ class Infection Infection() = default; /** - * @brief Determine Infection course subsequent to the given #InfectionState start_state. - * From the start_state, a random path through the #InfectionState tree is chosen, that is + * @brief Determine Infection course subsequent to the given #SymptomState start_state. + * From the start_state, a random path through the #SymptomState tree is chosen, that is * Susceptible -> InfectedNoSymptoms, * InfectedNoSymptoms -> InfectedSymptoms or InfectedNoSymptoms -> Recovered, * InfectedSymptoms -> Infected_Severe or InfectedSymptoms -> Recovered, * InfectedSevere -> InfectedCritical or InfectedSevere -> Recovered or InfectedSevere -> Dead, * InfectedCritical -> Recovered or InfectedCritical -> Dead, * until either Recoverd or Dead is reached. - * The duration in each #InfectionState is taken from the respective parameter. + * The duration in each #SymptomState is taken from the respective parameter. * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] age AgeGroup of the Person. * @param[in] params Parameters of the Model. * @param[in] init_date Date of initializing the Infection. - * @param[in] init_state #InfectionState at time of initializing the Infection. + * @param[in] init_state #SymptomState at time of initializing the Infection. + * @param[in] recovered If the Person is already recovered at the time of initializing the Infection. Only works if the Person has start_state = SymptomState::None. * @param[in] latest_protection Latest protection against Infection, has an influence on transition probabilities. */ - void draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, - TimePoint init_date, InfectionState init_state, - ProtectionEvent latest_protection); + void draw_symptom_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + TimePoint init_date, SymptomState init_state, bool recovered, + ProtectionEvent latest_protection); /** - * @brief Determine Infection course prior to the given #InfectionState start_state. - * From the start_state, a random path through the #InfectionState tree is chosen backwards, until Susceptible is reached. - * For more detailed information, refer to draw_infection_course_forward. + * @brief Determine Infection course prior to the given #SymptomState start_state. + * From the start_state, a random path through the #SymptomState tree is chosen backwards, until Susceptible is reached. + * For more detailed information, refer to draw_symptom_course_forward. * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] age AgeGroup of the person. * @param[in] params Parameters of the Model. * @param[in] init_date Date of initializing the Infection. - * @param[in] init_state InfectionState at time of initializing the Infection. + * @param[in] init_state #SymptomState at time of initializing the Infection. + * @param[in] recovered If the Person is already recovered at the time of initializing the Infection. Only works if the Person has start_state = SymptomState::None. * @return The starting date of the Infection. */ - TimePoint draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, - TimePoint init_date, InfectionState init_state); + TimePoint draw_symptom_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + TimePoint init_date, SymptomState init_state, bool recovered); /** * @brief Initialize the viral load parameters for the infection. @@ -233,29 +244,29 @@ class Infection const Parameters& params); /** - * @brief Get the forward transition from a given infection state. + * @brief Get the forward transition from a given symptom state. * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] age AgeGroup of the Person. * @param[in] params Parameters of the Model. - * @param[in] current_state Current infection state. + * @param[in] current_state Current symptom state. * @param[in] current_time Current time point. * @param[in] latest_protection Latest protection against Infection. * @return StateTransition representing the next transition. */ StateTransition get_forward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, - InfectionState current_state, TimePoint current_time, + SymptomState current_state, TimePoint current_time, ProtectionEvent latest_protection) const; /** - * @brief Get the backward transition from a given infection state. + * @brief Get the backward transition from a given symptom state. * @param[inout] rng PersonalRandomNumberGenerator of the Person. * @param[in] age AgeGroup of the Person. * @param[in] params Parameters of the Model. - * @param[in] current_state Current infection state. + * @param[in] current_state Current symptom state. * @return StateTransition representing the previous transition. */ StateTransition get_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, - InfectionState current_state) const; + SymptomState current_state) const; /** * @brief Get the backward transition from recovered state. @@ -296,7 +307,7 @@ class Infection ScalarType get_severity_protection_factor(const Parameters& params, ProtectionEvent latest_protection, AgeGroup age, TimePoint current_time) const; - std::vector> m_infection_course; ///< Start date of each #InfectionState. + std::vector> m_symptom_course; ///< Start date of each #SymptomState. VirusVariant m_virus_variant; ///< Variant of the Infection. ViralLoad m_viral_load; ///< ViralLoad of the Infection. ScalarType m_log_norm_alpha, diff --git a/cpp/models/abm/mobility_rules.cpp b/cpp/models/abm/mobility_rules.cpp index 60d1946c5d..a8f2470401 100644 --- a/cpp/models/abm/mobility_rules.cpp +++ b/cpp/models/abm/mobility_rules.cpp @@ -140,7 +140,7 @@ LocationType go_to_hospital(PersonalRandomNumberGenerator& /*rng*/, const Person TimeSpan /*dt*/, const Parameters& /*params*/) { auto current_loc = person.get_location_type(); - if (person.get_infection_state(t) == InfectionState::InfectedSevere) { + if (person.get_symptom_state(t) == SymptomState::Severe) { return LocationType::Hospital; } return current_loc; @@ -150,7 +150,7 @@ LocationType go_to_icu(PersonalRandomNumberGenerator& /*rng*/, const Person& per const Parameters& /*params*/) { auto current_loc = person.get_location_type(); - if (person.get_infection_state(t) == InfectionState::InfectedCritical) { + if (person.get_symptom_state(t) == SymptomState::Critical) { return LocationType::ICU; } return current_loc; @@ -161,7 +161,7 @@ LocationType return_home_when_recovered(PersonalRandomNumberGenerator& /*rng*/, { auto current_loc = person.get_location_type(); if ((current_loc == LocationType::Hospital || current_loc == LocationType::ICU) && - person.get_infection_state(t) == InfectionState::Recovered) { + person.get_symptom_state(t) == SymptomState::None) { return LocationType::Home; } return current_loc; @@ -171,7 +171,7 @@ LocationType get_buried(PersonalRandomNumberGenerator& /*rng*/, const Person& pe TimeSpan /*dt*/, const Parameters& /*params*/) { auto current_loc = person.get_location_type(); - if (person.get_infection_state(t) == InfectionState::Dead) { + if (person.get_symptom_state(t) == SymptomState::Dead) { return LocationType::Cemetery; } return current_loc; diff --git a/cpp/models/abm/model.cpp b/cpp/models/abm/model.cpp index a886edde98..a94ca707ae 100755 --- a/cpp/models/abm/model.cpp +++ b/cpp/models/abm/model.cpp @@ -188,7 +188,7 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt) auto& person = get_person(trip.person_id); auto personal_rng = PersonalRandomNumberGenerator(m_rng, person); // skip the trip if the person is in quarantine or is dead - if (person.is_in_quarantine(t, parameters) || person.get_infection_state(t) == InfectionState::Dead) { + if (person.is_in_quarantine(t, parameters) || person.get_symptom_state(t) == SymptomState::Dead) { continue; } auto& target_location = get_location(trip.destination); @@ -289,7 +289,7 @@ void Model::compute_exposure_caches(TimePoint t, TimeSpan dt) const auto num_persons = m_persons.size(); // 1) reset all cached values - // Note: we cannot easily reuse values, as they are time dependent (get_infection_state) + // Note: we cannot easily reuse values, as they are time dependent (get_symptom_state) PRAGMA_OMP(taskloop) for (size_t i = 0; i < num_locations; ++i) { mio::abm::adjust_contact_rates(m_locations[i], parameters.get_num_groups()); @@ -378,7 +378,7 @@ LocationId Model::find_location(LocationType type, const PersonId person) const return find_location(type, get_person(person)); } -size_t Model::get_subpopulation_combined(TimePoint t, InfectionState s) const +size_t Model::get_subpopulation_combined(TimePoint t, SymptomState s) const { return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, [t, s, this](size_t running_sum, const Location& loc) { @@ -386,7 +386,7 @@ size_t Model::get_subpopulation_combined(TimePoint t, InfectionState s) const }); } -size_t Model::get_subpopulation_combined_per_location_type(TimePoint t, InfectionState s, LocationType type) const +size_t Model::get_subpopulation_combined_per_location_type(TimePoint t, SymptomState s, LocationType type) const { return std::accumulate( m_locations.begin(), m_locations.end(), (size_t)0, [t, s, type, this](size_t running_sum, const Location& loc) { diff --git a/cpp/models/abm/model.h b/cpp/models/abm/model.h index 90fff62025..5758229ab8 100644 --- a/cpp/models/abm/model.h +++ b/cpp/models/abm/model.h @@ -20,7 +20,7 @@ #ifndef MIO_ABM_MODEL_H #define MIO_ABM_MODEL_H -#include "abm/infection_state.h" +#include "abm/symptom_state.h" #include "abm/model_functions.h" #include "abm/location_type.h" #include "abm/mobility_data.h" @@ -62,7 +62,7 @@ class Model using MobilityRuleType = LocationType (*)(PersonalRandomNumberGenerator&, const Person&, TimePoint, TimeSpan, const Parameters&); - using Compartments = mio::abm::InfectionState; + using Compartments = mio::abm::SymptomState; /** * @brief Create a Model. * @param[in] num_agegroups The number of AgeGroup%s in the simulated Model. Must be less than MAX_NUM_AGE_GROUPS. @@ -262,19 +262,19 @@ class Model } /** - * @brief Get the number of Persons in one #InfectionState at all Location%s. + * @brief Get the number of Persons in one #SymptomState at all Location%s. * @param[in] t Specified #TimePoint. - * @param[in] s Specified #InfectionState. + * @param[in] s Specified #SymptomState. */ - size_t get_subpopulation_combined(TimePoint t, InfectionState s) const; + size_t get_subpopulation_combined(TimePoint t, SymptomState s) const; /** - * @brief Get the number of Persons in one #InfectionState at all Location%s of a type. + * @brief Get the number of Persons in one #SymptomState at all Location%s of a type. * @param[in] t Specified #TimePoint. - * @param[in] s Specified #InfectionState. + * @param[in] s Specified #SymptomState. * @param[in] type Specified #LocationType. */ - size_t get_subpopulation_combined_per_location_type(TimePoint t, InfectionState s, LocationType type) const; + size_t get_subpopulation_combined_per_location_type(TimePoint t, SymptomState s, LocationType type) const; /** * @brief Get the mobility data. @@ -392,17 +392,16 @@ class Model } /** - * @brief Get the number of Person%s of a particular #InfectionState for all Cell%s. + * @brief Get the number of Person%s of a particular #SymptomState for all Cell%s. * @param[in] location A LocationId from the Model. * @param[in] t TimePoint of querry. - * @param[in] state #InfectionState of interest. - * @return Amount of Person%s of the #InfectionState in all Cell%s of the Location. + * @param[in] state #SymptomState of interest. + * @return Amount of Person%s of the #SymptomState in all Cell%s of the Location. */ - size_t get_subpopulation(LocationId location, TimePoint t, InfectionState state) const + size_t get_subpopulation(LocationId location, TimePoint t, SymptomState state) const { return std::count_if(m_persons.begin(), m_persons.end(), [&](auto&& p) { - return p.get_location_model_id() == m_id && p.get_location() == location && - p.get_infection_state(t) == state; + return p.get_location_model_id() == m_id && p.get_location() == location && p.get_symptom_state(t) == state; }); } diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index 7529590521..3ad0f944f5 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -76,7 +76,7 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const return cell < location.get_cells().size(); })); - if (person.get_infection_state(t) == InfectionState::Susceptible) { + if (!person.is_infected(t)) { auto& local_parameters = location.get_infection_parameters(); // TODO: we need to define what a cell is used for, as the loop may lead to incorrect results for multiple cells auto age_receiver = person.get_age(); @@ -100,10 +100,10 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const random_transition(personal_rng, VirusVariant::Count, dt, local_indiv_expected_trans); // use VirusVariant::Count for no virus submission if (virus != VirusVariant::Count) { - person.add_new_infection(Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2, - mio::abm::InfectionState::Exposed, - person.get_latest_protection(t + dt / 2), - false)); // Starting time in second order approximation + person.add_new_infection( + Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2, + mio::abm::SymptomState::None, false, false, + person.get_latest_protection(t + dt / 2))); // Starting time in second order approximation } } } diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index 1933d36722..d3ed76ba59 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -22,7 +22,7 @@ #include "abm/mask_type.h" #include "abm/time.h" -#include "abm/infection_state.h" +#include "abm/symptom_state.h" #include "abm/virus_variant.h" #include "abm/protection_event.h" #include "abm/protection_event.h" @@ -51,25 +51,10 @@ namespace mio namespace abm { -/** - * @brief Time that a Person is infected but not yet infectious in day unit - */ -struct TimeExposedToNoSymptoms { - using Type = CustomIndexArray; - static Type get_default(AgeGroup size) - { - return Type({VirusVariant::Count, size}, AbstractParameterDistribution(ParameterDistributionLogNormal(1., 1.))); - } - static std::string name() - { - return "TimeExposedToNoSymptoms"; - } -}; - /** * @brief Time that a Person is infected but presymptomatic in day unit */ -struct TimeInfectedNoSymptomsToSymptoms { +struct TimeInfectedNoSymptomsToModerate { using Type = CustomIndexArray; static Type get_default(AgeGroup size) { @@ -77,7 +62,7 @@ struct TimeInfectedNoSymptomsToSymptoms { } static std::string name() { - return "TimeInfectedNoSymptomsToSymptoms"; + return "TimeInfectedNoSymptomsToModerate"; } }; @@ -100,7 +85,7 @@ struct TimeInfectedNoSymptomsToRecovered { * @brief Time that a Person is infected and symptomatic but * who do not need to be hospitalized yet in day unit */ -struct TimeInfectedSymptomsToSevere { +struct TimeInfectedModerateToSevere { using Type = CustomIndexArray; static Type get_default(AgeGroup size) { @@ -115,7 +100,7 @@ struct TimeInfectedSymptomsToSevere { /** * @brief Time that a Person is infected and symptomatic who will recover in day unit */ -struct TimeInfectedSymptomsToRecovered { +struct TimeInfectedModerateToRecovered { using Type = CustomIndexArray; static Type get_default(AgeGroup size) { @@ -697,16 +682,16 @@ struct AgeGroupGotoWork { }; using ParametersBase = - ParameterSet; + ParameterSet; /** * @brief Maximum number of Person%s an infectious Person can infect at the respective Location. @@ -795,16 +780,8 @@ class Parameters : public ParametersBase for (auto i = AgeGroup(0); i < AgeGroup(m_num_groups); ++i) { for (auto&& v : enum_members()) { - if (this->get()[{v, i}].params()[0] < 0) { - log_error("Constraint check: Mean of parameter TimeExposedToNoSymptoms of virus variant {} and " - "age group {} smaller " - "than {}", - (uint32_t)v, (size_t)i, 0); - return true; - } - - if (this->get()[{v, i}].params()[0] < 0.0) { - log_error("Constraint check: Mean of parameter TimeInfectedNoSymptomsToSymptoms " + if (this->get()[{v, i}].params()[0] < 0.0) { + log_error("Constraint check: Mean of parameter TimeInfectedNoSymptomsToModerate " "of virus variant " "{} and age group {} smaller " "than {}", @@ -821,8 +798,8 @@ class Parameters : public ParametersBase return true; } - if (this->get()[{v, i}].params()[0] < 0.0) { - log_error("Constraint check: Mean of parameter TimeInfectedSymptomsToSevere of virus " + if (this->get()[{v, i}].params()[0] < 0.0) { + log_error("Constraint check: Mean of parameter TimeInfectedModerateToSevere of virus " "variant {} " "and age group {} smaller " "than {}", @@ -830,8 +807,8 @@ class Parameters : public ParametersBase return true; } - if (this->get()[{v, i}].params()[0] < 0.0) { - log_error("Constraint check: Mean of parameter TimeInfectedSymptomsToRecovered of virus " + if (this->get()[{v, i}].params()[0] < 0.0) { + log_error("Constraint check: Mean of parameter TimeInfectedModerateToRecovered of virus " "variant {} " "and age group {} smaller " "than {}", diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 19a66aec99..a04787152a 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -67,21 +67,19 @@ bool Person::is_infected(TimePoint t) const if (m_infections.empty()) { return false; } - // subject to change if Recovered is removed - if (m_infections.back().get_infection_state(t) == InfectionState::Susceptible || - m_infections.back().get_infection_state(t) == InfectionState::Recovered) { + if (m_infections.back().is_recovered(t)) { return false; } return true; } -InfectionState Person::get_infection_state(TimePoint t) const +SymptomState Person::get_symptom_state(TimePoint t) const { if (m_infections.empty()) { - return InfectionState::Susceptible; + return SymptomState::None; } else { - return m_infections.back().get_infection_state(t); + return m_infections.back().get_symptom_state(t); } } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index e1cea60230..fed59d01e1 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -21,7 +21,7 @@ #define MIO_ABM_PERSON_H #include "abm/infection.h" -#include "abm/infection_state.h" +#include "abm/symptom_state.h" #include "abm/location_id.h" #include "abm/location_type.h" #include "abm/parameters.h" @@ -101,11 +101,11 @@ class Person bool is_infected(TimePoint t) const; /** - * @brief Get the InfectionState of the Person at a specific TimePoint. + * @brief Get the SymptomState of the Person at a specific TimePoint. * @param[in] t TimePoint of querry. Usually the current time of the Simulation. - * @return The InfectionState of the latest Infection at time t. + * @return The SymptomState of the latest Infection at time t. */ - InfectionState get_infection_state(TimePoint t) const; + SymptomState get_symptom_state(TimePoint t) const; /** * @brief Adds a new Infection to the list of Infection%s. diff --git a/cpp/models/abm/result_simulation.h b/cpp/models/abm/result_simulation.h index 2f1d02131a..1b573a3b69 100644 --- a/cpp/models/abm/result_simulation.h +++ b/cpp/models/abm/result_simulation.h @@ -49,15 +49,15 @@ class ResultSimulation : public Simulation } /** - * @brief Return the simulation result aggregated by infection states. + * @brief Return the simulation result aggregated by symptom states. */ const mio::TimeSeries& get_result() const { return get<0>(history.get_log()); } - mio::History history{ - Eigen::Index(InfectionState::Count)}; ///< History used to create the result TimeSeries. + mio::History history{ + Eigen::Index(SymptomState::Count)}; ///< History used to create the result TimeSeries. }; } // namespace abm diff --git a/cpp/models/abm/infection_state.h b/cpp/models/abm/symptom_state.h similarity index 75% rename from cpp/models/abm/infection_state.h rename to cpp/models/abm/symptom_state.h index e20e9b262d..ad8e631df2 100644 --- a/cpp/models/abm/infection_state.h +++ b/cpp/models/abm/symptom_state.h @@ -17,8 +17,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef MIO_ABM_INFECTION_STATE_H -#define MIO_ABM_INFECTION_STATE_H +#ifndef MIO_ABM_SYMPTOM_STATE_H +#define MIO_ABM_SYMPTOM_STATE_H #include @@ -28,18 +28,15 @@ namespace abm { /** - * @brief #InfectionState in ABM. + * @brief #SymptomState in ABM. * Can be used as 0-based index. */ -enum class InfectionState : std::uint32_t +enum class SymptomState : std::uint32_t { - Susceptible = 0, - Exposed, - InfectedNoSymptoms, - InfectedSymptoms, - InfectedSevere, - InfectedCritical, - Recovered, + None = 0, + Moderate, + Severe, + Critical, Dead, Count //last!! diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index 0a601638ca..688478f57a 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -28,26 +28,26 @@ namespace mio namespace abm { -TestingCriteria::TestingCriteria(const std::vector& ages, const std::vector& infection_states) +TestingCriteria::TestingCriteria(const std::vector& ages, const std::vector& symptom_states) { for (auto age : ages) { m_ages.set(static_cast(age), true); } - for (auto infection_state : infection_states) { - m_infection_states.set(static_cast(infection_state), true); + for (auto symptom_state : symptom_states) { + m_symptom_states.set(static_cast(symptom_state), true); } } bool TestingCriteria::operator==(const TestingCriteria& other) const { - return m_ages == other.m_ages && m_infection_states == other.m_infection_states; + return m_ages == other.m_ages && m_symptom_states == other.m_symptom_states; } bool TestingCriteria::evaluate(const Person& p, TimePoint t) const { - // An empty vector of ages or none bitset of #InfectionStates% means that no condition on the corresponding property is set. + // An empty vector of ages or none bitset of #SymptomStates% means that no condition on the corresponding property is set. return (m_ages.none() || m_ages[static_cast(p.get_age())]) && - (m_infection_states.none() || m_infection_states[static_cast(p.get_infection_state(t))]); + (m_symptom_states.none() || m_symptom_states[static_cast(p.get_symptom_state(t))]); } TestingScheme::TestingScheme(const TestingCriteria& testing_criteria, TimeSpan validity_period, TimePoint start_date, diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 8996655cc5..26cfbfa1b4 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -51,11 +51,11 @@ class TestingCriteria /** * @brief Create a TestingCriteria. * @param[in] ages Vector of AgeGroup%s that are either allowed or required to be tested. - * @param[in] infection_states Vector of #InfectionState%s that are either allowed or required to be tested. - * An empty vector of ages or none bitset of #InfectionStates% means that no condition on the corresponding property + * @param[in] symptom_states Vector of #SymptomState%s that are either allowed or required to be tested. + * An empty vector of ages or none bitset of #SymptomStates% means that no condition on the corresponding property * is set! */ - TestingCriteria(const std::vector& ages, const std::vector& infection_states); + TestingCriteria(const std::vector& ages, const std::vector& symptom_states); /** * @brief Compares two TestingCriteria for functional equality. @@ -71,13 +71,13 @@ class TestingCriteria auto default_serialize() { - return Members("TestingCriteria").add("ages", m_ages).add("infection_states", m_infection_states); + return Members("TestingCriteria").add("ages", m_ages).add("symptom_states", m_symptom_states); } private: std::bitset m_ages; ///< Set of #AgeGroup%s that are either allowed or required to be tested. - std::bitset<(size_t)InfectionState::Count> - m_infection_states; /**< BitSet of #InfectionState%s that are either allowed or required to + std::bitset<(size_t)SymptomState::Count> + m_symptom_states; /**< BitSet of #SymptomState%s that are either allowed or required to be tested.*/ }; diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index bf9af22c29..e123056dde 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -22,25 +22,24 @@ #include "abm/person_id.h" #include "memilio/utils/random_number_generator.h" -mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age, - mio::abm::InfectionState infection_state, mio::abm::TimePoint t, - mio::abm::Parameters params, mio::abm::PersonId id) +mio::abm::Person make_infected_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, mio::AgeGroup age, + mio::abm::SymptomState symptom_state, bool recovered, mio::abm::TimePoint t, + mio::abm::Parameters params, mio::abm::PersonId id) { assert(age.get() < params.get_num_groups()); mio::abm::Person p(rng, location.get_type(), location.get_id(), location.get_model_id(), age, id); - if (infection_state != mio::abm::InfectionState::Susceptible) { - auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); - p.add_new_infection( - mio::abm::Infection(rng_p, static_cast(0), age, params, t, infection_state)); - } + auto rng_p = mio::abm::PersonalRandomNumberGenerator(rng, p); + p.add_new_infection( + mio::abm::Infection(rng_p, static_cast(0), age, params, t, symptom_state, recovered)); return p; } -mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, - mio::abm::InfectionState infection_state, mio::abm::TimePoint t) +mio::abm::PersonId add_infected_person(mio::abm::Model& model, mio::abm::LocationId loc_id, mio::AgeGroup age, + mio::abm::SymptomState symptom_state, bool recovered, mio::abm::TimePoint t) { - return model.add_person(make_test_person(model.get_rng(), model.get_location(loc_id), age, infection_state, t, - model.parameters, static_cast(model.get_persons().size()))); + return model.add_person(make_infected_person(model.get_rng(), model.get_location(loc_id), age, symptom_state, + recovered, t, model.parameters, + static_cast(model.get_persons().size()))); } void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, diff --git a/cpp/tests/abm_helpers.h b/cpp/tests/abm_helpers.h index 808e8dc173..5fefaa6d3a 100644 --- a/cpp/tests/abm_helpers.h +++ b/cpp/tests/abm_helpers.h @@ -91,22 +91,22 @@ struct ScopedMockDistribution { }; /** - * @brief Create a Person without a Model object. Intended for simple use in tests. + * @brief Create an infected Person without a Model object. Intended for simple use in tests. */ -mio::abm::Person make_test_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, - mio::AgeGroup age = age_group_15_to_34, - mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, - mio::abm::TimePoint t = mio::abm::TimePoint(0), - mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), - mio::abm::PersonId id = mio::abm::PersonId(0)); +mio::abm::Person make_infected_person(mio::RandomNumberGenerator& rng, mio::abm::Location& location, + mio::AgeGroup age = age_group_15_to_34, + mio::abm::SymptomState symptom_state = mio::abm::SymptomState::None, + mio::abm::TimePoint t = mio::abm::TimePoint(0), + mio::abm::Parameters params = mio::abm::Parameters(num_age_groups), + mio::abm::PersonId id = mio::abm::PersonId(0)); /** - * @brief Add a Person to the Model. Intended for simple use in tests. + * @brief Add a infected Person to the Model. Intended for simple use in tests. */ -mio::abm::PersonId add_test_person(mio::abm::Model& model, mio::abm::LocationId loc_id, - mio::AgeGroup age = age_group_15_to_34, - mio::abm::InfectionState infection_state = mio::abm::InfectionState::Susceptible, - mio::abm::TimePoint t = mio::abm::TimePoint(0)); +mio::abm::PersonId add_infected_person(mio::abm::Model& model, mio::abm::LocationId loc_id, + mio::AgeGroup age = age_group_15_to_34, + mio::abm::SymptomState symptom_state = mio::abm::SymptomState::None, + mio::abm::TimePoint t = mio::abm::TimePoint(0)); /// @brief Calls mio::abm::interact, but it computes the correct exposures for you. void interact_testing(mio::abm::PersonalRandomNumberGenerator& personal_rng, mio::abm::Person& person, diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index f8c7650de5..66fbaea415 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -55,7 +55,7 @@ TEST_F(TestInfection, init) EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(5)) // 1st infection - .WillOnce(testing::Return(0.4)) // Transition to Infected + .WillOnce(testing::Return(0.4)) // Transition to Moderate .WillOnce(testing::Return(0.6)) // Transition to Recovered .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] .params()[0])) // Viral Shed Factor @@ -68,27 +68,25 @@ TEST_F(TestInfection, init) //Distribution for stay times EXPECT_CALL(mock_logNormal_dist.get_mock(), invoke) // 1st infection - .WillOnce(testing::Return(1.)) // IncubationTime - .WillOnce(testing::Return(1.)) // TimeInfectedNoSymptomsToSymptoms + .WillOnce(testing::Return(1.)) // TimeInfectedNoSymptomsToModerate .WillOnce(testing::Return(1.)) // TimeInfectedSymptomsToRecovered // 2nd infection - .WillOnce(testing::Return(1.0)) // TimeInfectedNoSymptomsToSymptoms - .WillOnce(testing::Return(1.0)) // IncubationTime + .WillOnce(testing::Return(1.0)) // TimeInfectedNoSymptomsToModerate .WillOnce(testing::Return(1.0)) // TimeInfectedSymptomsToRecovered .WillRepeatedly(testing::Return(1.0)); auto infection = mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, age_group_15_to_34, params, - mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed, - {mio::abm::ProtectionType::NoProtection, mio::abm::TimePoint(0)}, true); + mio::abm::TimePoint(0), mio::abm::SymptomState::None, false, true, + {mio::abm::ProtectionType::NoProtection, mio::abm::TimePoint(0)}); // Test virus variant and detection status EXPECT_EQ(infection.get_virus_variant(), mio::abm::VirusVariant::Wildtype); EXPECT_EQ(infection.is_detected(), true); // Test state transitions based on time - EXPECT_EQ(infection.get_infection_state(mio::abm::TimePoint(0) + mio::abm::days(1) - mio::abm::seconds(1)), - mio::abm::InfectionState::Exposed); - EXPECT_EQ(infection.get_infection_state(mio::abm::TimePoint(0) + mio::abm::days(1)), - mio::abm::InfectionState::InfectedNoSymptoms); + EXPECT_EQ(infection.get_symptom_state(mio::abm::TimePoint(0) + mio::abm::days(1) - mio::abm::seconds(1)), + mio::abm::SymptomState::None); + EXPECT_EQ(infection.get_symptom_state(mio::abm::TimePoint(0) + mio::abm::days(1)), + mio::abm::SymptomState::Moderate); // Test viral shed at a specific time point EXPECT_NEAR(infection.get_viral_shed(mio::abm::TimePoint(0) + mio::abm::days(3)), 0.02689414213699951, 1e-14); @@ -101,14 +99,14 @@ TEST_F(TestInfection, init) mio::TimeSeriesFunctor{mio::TimeSeriesFunctorType::LinearInterpolation, {{0, 0.91}, {30, 0.81}}}; auto infection_w_previous_exp = mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, age_group_test, params, mio::abm::TimePoint(0), - mio::abm::InfectionState::InfectedSymptoms, - {mio::abm::ProtectionType::GenericVaccine, mio::abm::TimePoint(0)}, true); - // Test infection state transition + mio::abm::SymptomState::Moderate, false, true, + {mio::abm::ProtectionType::GenericVaccine, mio::abm::TimePoint(0)}); + // Test symptom state transition EXPECT_EQ( - infection_w_previous_exp.get_infection_state(mio::abm::TimePoint(0) + mio::abm::days(1) - mio::abm::seconds(1)), - mio::abm::InfectionState::InfectedSymptoms); - EXPECT_EQ(infection_w_previous_exp.get_infection_state(mio::abm::TimePoint(0) + mio::abm::days(1)), - mio::abm::InfectionState::Recovered); + infection_w_previous_exp.get_symptom_state(mio::abm::TimePoint(0) + mio::abm::days(1) - mio::abm::seconds(1)), + mio::abm::SymptomState::Moderate); + EXPECT_EQ(infection_w_previous_exp.get_symptom_state(mio::abm::TimePoint(0) + mio::abm::days(1)), + mio::abm::SymptomState::None); // Test viral shed at a specific time point EXPECT_NEAR(infection_w_previous_exp.get_viral_shed(mio::abm::TimePoint(0) + mio::abm::days(3)), 9.1105119440064545e-05, 1e-14); diff --git a/cpp/tests/test_abm_serialization.cpp b/cpp/tests/test_abm_serialization.cpp index 7fd48dda52..cc31c98f0f 100644 --- a/cpp/tests/test_abm_serialization.cpp +++ b/cpp/tests/test_abm_serialization.cpp @@ -19,7 +19,7 @@ */ #include "matchers.h" #include "abm/config.h" -#include "abm/infection_state.h" +#include "abm/symptom_state.h" #include "abm/parameters.h" #include "abm/test_type.h" #include "abm/testing_strategy.h" diff --git a/docs/source/cpp/mobility_based_abm.rst b/docs/source/cpp/mobility_based_abm.rst index de43f9958d..b86d6a6795 100644 --- a/docs/source/cpp/mobility_based_abm.rst +++ b/docs/source/cpp/mobility_based_abm.rst @@ -33,7 +33,7 @@ Disease progression The ABM implements a detailed disease progression model that captures the full course of an infection from exposure to resolution. The disease progression is modeled through the ``Infection`` class, which contains: -1. **Infection States**: Similar to the aggregated models (see, e.g., :doc:`equation based models`), an infected person progresses through states defined in `infection_state.h `_: +1. **Infection States**: Similar to the aggregated models (see, e.g., :doc:`equation based models`), an infected person progresses through states defined in `infection_state.h `_: * **Susceptible**: Initial state before infection * **Exposed**: Infected but not yet infectious