diff --git a/cpp/models/abm/analyze_result.h b/cpp/models/abm/analyze_result.h index 1f01619a1f..1e863525e0 100644 --- a/cpp/models/abm/analyze_result.h +++ b/cpp/models/abm/analyze_result.h @@ -187,17 +187,17 @@ std::vector ensemble_params_percentile(const std::vector(num_runs), + node, std::vector(num_runs), [age_group, virus_variant](auto&& model) -> auto& { - return model.parameters.template get()[{virus_variant, age_group}]; + return model.parameters.template get()[{virus_variant, age_group}]; }, [](auto& dist1, auto& dist2) { - return dist1.infectivity_alpha < dist2.infectivity_alpha; + return dist1.virus_shed_alpha < dist2.virus_shed_alpha; }); param_percentile_dist( node, std::vector(num_runs), [age_group, virus_variant](auto&& model) -> auto& { - return model.parameters.template get()[{virus_variant, age_group}]; + return model.parameters.template get()[{virus_variant, age_group}]; }, [](auto& dist1, auto& dist2) { return dist1 < dist2; diff --git a/cpp/models/abm/infection.cpp b/cpp/models/abm/infection.cpp index 0dfd5dca09..671dc8b6de 100644 --- a/cpp/models/abm/infection.cpp +++ b/cpp/models/abm/infection.cpp @@ -28,56 +28,70 @@ namespace mio namespace abm { -Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, - TimePoint init_date, InfectionState init_state, ProtectionEvent latest_protection, bool detected) - : m_virus_variant(virus) - , m_detected(detected) +void Infection::initialize_viral_load(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, + const Parameters& params, TimePoint init_date, ProtectionEvent latest_protection) { - assert(age.get() < params.get_num_groups()); - m_viral_load.start_date = draw_infection_course(rng, age, params, init_date, init_state, latest_protection); - auto vl_params = params.get()[{virus, age}]; ScalarType high_viral_load_factor = 1; + if (latest_protection.type != ProtectionType::NoProtection) { high_viral_load_factor -= params.get()[{latest_protection.type, age, virus}]( init_date.days() - latest_protection.time.days()); } + m_viral_load.peak = vl_params.viral_load_peak.get(rng) * high_viral_load_factor; m_viral_load.incline = vl_params.viral_load_incline.get(rng); m_viral_load.decline = vl_params.viral_load_decline.get(rng); - m_viral_load.end_date = - m_viral_load.start_date + - days(int(m_viral_load.peak / m_viral_load.incline - m_viral_load.peak / m_viral_load.decline)); + m_viral_load.end_date = m_viral_load.start_date + + days(m_viral_load.peak / m_viral_load.incline - m_viral_load.peak / m_viral_load.decline); +} - auto inf_params = params.get()[{virus, age}]; - m_log_norm_alpha = inf_params.infectivity_alpha.get(rng); - m_log_norm_beta = inf_params.infectivity_beta.get(rng); +void Infection::initialize_viral_shed(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, + const Parameters& params) +{ + auto viral_shed_params = params.get()[{virus, age}]; + m_log_norm_alpha = viral_shed_params.viral_shed_alpha; + m_log_norm_beta = viral_shed_params.viral_shed_beta; - auto shedfactor_param = params.get()[{virus, age}]; - m_individual_virus_shed_factor = shedfactor_param.get(rng); + auto shedfactor_param = params.get()[{virus, age}]; + m_individual_viral_shed_factor = shedfactor_param.get(rng); +} + +Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, + TimePoint init_date, InfectionState init_state, ProtectionEvent latest_protection, bool detected) + : m_virus_variant(virus) + , m_detected(detected) +{ + assert(age.get() < params.get_num_groups()); + m_viral_load.start_date = draw_infection_course(rng, age, params, init_date, init_state, latest_protection); + + initialize_viral_load(rng, virus, age, params, init_date, latest_protection); + initialize_viral_shed(rng, virus, age, params); } ScalarType Infection::get_viral_load(TimePoint t) const { - if (t >= m_viral_load.start_date && t <= m_viral_load.end_date) { - if (t.days() <= m_viral_load.start_date.days() + m_viral_load.peak / m_viral_load.incline) { - return m_viral_load.incline * (t - m_viral_load.start_date).days(); - } - else { - return m_viral_load.peak + m_viral_load.decline * (t.days() - m_viral_load.peak / m_viral_load.incline - - m_viral_load.start_date.days()); - } + if (t < m_viral_load.start_date || t > m_viral_load.end_date) { + return 0.0; + } + + ScalarType time_from_start = (t - m_viral_load.start_date).days(); + ScalarType peak_time = m_viral_load.peak / m_viral_load.incline; + + if (time_from_start <= peak_time) { + return m_viral_load.incline * time_from_start; } else { - return 0.; + return m_viral_load.peak + m_viral_load.decline * (time_from_start - peak_time); } } -ScalarType Infection::get_infectivity(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 || get_infection_state(t) == InfectionState::Exposed) { return 0; - return m_individual_virus_shed_factor / (1 + exp(-(m_log_norm_alpha + m_log_norm_beta * get_viral_load(t)))); + } + return m_individual_viral_shed_factor / (1 + exp(-(m_log_norm_alpha + m_log_norm_beta * get_viral_load(t)))); } VirusVariant Infection::get_virus_variant() const @@ -87,14 +101,15 @@ VirusVariant Infection::get_virus_variant() const InfectionState Infection::get_infection_state(TimePoint t) const { - if (t < m_infection_course[0].first) + if (t < m_infection_course[0].first) { return InfectionState::Susceptible; + } - return (*std::prev(std::upper_bound(m_infection_course.begin(), m_infection_course.end(), t, - [](const TimePoint& s, std::pair state) { - return state.first > s; - }))) - .second; + auto it = std::upper_bound(m_infection_course.begin(), m_infection_course.end(), t, + [](const TimePoint& s, const std::pair& state) { + return state.first > s; + }); + return std::prev(it)->second; } void Infection::set_detected() @@ -116,211 +131,262 @@ TimePoint Infection::draw_infection_course(PersonalRandomNumberGenerator& rng, A TimePoint init_date, InfectionState init_state, ProtectionEvent latest_protection) { - assert(age.get() < params.get_num_groups()); - TimePoint start_date = draw_infection_course_backward(rng, age, params, init_date, init_state); - draw_infection_course_forward(rng, age, params, init_date, init_state, latest_protection); + TimePoint start_of_init_state = + draw_infection_course_forward(rng, age, params, init_date, init_state, latest_protection); + TimePoint start_date = draw_infection_course_backward(rng, age, params, start_of_init_state, init_state); return start_date; } -void Infection::draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, - const Parameters& params, TimePoint init_date, InfectionState start_state, - ProtectionEvent latest_protection) +StateTransition Infection::get_forward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params, InfectionState current_state, + TimePoint current_time, ProtectionEvent latest_protection) const +{ + auto& uniform_dist = UniformDistribution::get_instance(); + StateTransition transition{current_state, current_state, TimeSpan{}}; + + switch (current_state) { + case InfectionState::Susceptible: + transition.to_state = InfectionState::Exposed; + transition.duration = mio::abm::hours(0); + break; + + case InfectionState::Exposed: + transition.to_state = InfectionState::InfectedNoSymptoms; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + break; + + case InfectionState::InfectedNoSymptoms: { + 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)); + } + else { + transition.to_state = InfectionState::Recovered; + transition.duration = + days(params.get()[{m_virus_variant, age}].get(rng)); + } + break; + } + + case InfectionState::InfectedSymptoms: { + ScalarType p = uniform_dist(rng); + ScalarType severity_protection_factor = + get_severity_protection_factor(params, latest_protection, age, current_time); + ScalarType severe_probability = + (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)); + } + else { + transition.to_state = InfectionState::Recovered; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + } + break; + } + + case InfectionState::InfectedSevere: { + 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 < critical_prob) { + transition.to_state = InfectionState::InfectedCritical; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + } + else if (p < critical_prob + death_prob) { + transition.to_state = InfectionState::Dead; + 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)); + } + break; + } + + case InfectionState::InfectedCritical: { + ScalarType p = uniform_dist(rng); + if (p < params.get()[{m_virus_variant, age}]) { + transition.to_state = InfectionState::Dead; + 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)); + } + break; + } + + default: + break; + } + + return transition; +} + +StateTransition Infection::get_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params, InfectionState current_state) const +{ + StateTransition transition{current_state, current_state, TimeSpan{}}; + + switch (current_state) { + case InfectionState::InfectedNoSymptoms: + transition.to_state = InfectionState::Exposed; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + break; + + case InfectionState::InfectedSymptoms: + transition.to_state = InfectionState::InfectedNoSymptoms; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + break; + + case InfectionState::InfectedSevere: + transition.to_state = InfectionState::InfectedSymptoms; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + break; + + case InfectionState::InfectedCritical: + transition.to_state = InfectionState::InfectedSevere; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + break; + + case InfectionState::Recovered: + transition = get_recovered_backward_transition(rng, age, params); + break; + + case InfectionState::Dead: + transition = get_dead_backward_transition(rng, age, params); + break; + + default: + break; + } + + return transition; +} + +StateTransition Infection::get_recovered_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params) const { - assert(age.get() < params.get_num_groups()); - auto t = init_date; - TimeSpan time_period{}; // time period for current infection state - auto time_in_state = params.get()[{ - m_virus_variant, age}]; // time distribution parameters for current infection state - InfectionState next_state{start_state}; // next state to enter - m_infection_course.push_back(std::pair(t, next_state)); auto& uniform_dist = UniformDistribution::get_instance(); - ScalarType p; // uniform random draws from [0, 1] - while ((next_state != InfectionState::Recovered && next_state != InfectionState::Dead)) { - switch (next_state) { - case InfectionState::Exposed: { - // roll out how long until infected without symptoms - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - next_state = InfectionState::InfectedNoSymptoms; - } break; - case InfectionState::InfectedNoSymptoms: { - // roll out next infection step - - p = uniform_dist(rng); - if (p < params.get()[{m_virus_variant, age}]) { - next_state = InfectionState::InfectedSymptoms; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - else { - next_state = InfectionState::Recovered; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - } break; - case InfectionState::InfectedSymptoms: { - // roll out next infection step - - ScalarType severity_protection_factor = 0.; - p = uniform_dist(rng); - if (latest_protection.type != ProtectionType::NoProtection) { - severity_protection_factor = - params.get()[{latest_protection.type, age, m_virus_variant}]( - t.days() - latest_protection.time.days()); - } - if (p < - (1 - severity_protection_factor) * params.get()[{m_virus_variant, age}]) { - next_state = InfectionState::InfectedSevere; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - else { - next_state = InfectionState::Recovered; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - } break; - - case InfectionState::InfectedSevere: { - // roll out next infection step - - p = uniform_dist(rng); - if (p < params.get()[{m_virus_variant, age}]) { - next_state = InfectionState::InfectedCritical; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - else if (p < params.get()[{m_virus_variant, age}] + - params.get()[{m_virus_variant, age}]) { - next_state = InfectionState::Dead; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - else { - next_state = InfectionState::Recovered; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - } break; - - case InfectionState::InfectedCritical: { - // roll out next infection step - - p = uniform_dist(rng); - if (p < params.get()[{m_virus_variant, age}]) { - next_state = InfectionState::Dead; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - else { - next_state = InfectionState::Recovered; - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - } - } break; - - default: - break; + ScalarType p = uniform_dist(rng); + + // Compute death probability to factor it out + ScalarType p_death = calculate_death_probability(age, params); + assert(p_death == 1 && "Trying to create a recovered agent although the chance to die is 100%."); + ScalarType inv_death = 1 / (1 - p_death); + + ScalarType symptoms_prob = params.get()[{m_virus_variant, age}]; + ScalarType severe_prob = params.get()[{m_virus_variant, age}]; + ScalarType critical_prob = params.get()[{m_virus_variant, age}]; + + StateTransition transition{InfectionState::Recovered, InfectionState::InfectedNoSymptoms, TimeSpan{}}; + + if (p > symptoms_prob * inv_death) { + transition.to_state = InfectionState::InfectedNoSymptoms; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + } + else if (p > symptoms_prob * severe_prob * inv_death) { + transition.to_state = InfectionState::InfectedSymptoms; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + } + else if (p > symptoms_prob * severe_prob * critical_prob * inv_death) { + transition.to_state = InfectionState::InfectedSevere; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } - t = t + time_period; - m_infection_course.push_back({t, next_state}); + else { + transition.to_state = InfectionState::InfectedCritical; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); } + + return transition; } -TimePoint Infection::draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, - const Parameters& params, TimePoint init_date, - InfectionState init_state) +StateTransition Infection::get_dead_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params) const { - assert(age.get() < params.get_num_groups()); - auto start_date = init_date; - TimeSpan time_period{}; // time period for current infection state - auto time_in_state = params.get()[{ - m_virus_variant, age}]; // time distribution parameters for current infection state - InfectionState previous_state{init_state}; // previous state to enter auto& uniform_dist = UniformDistribution::get_instance(); - ScalarType p; // uniform random draws from [0, 1] - - while ((previous_state != InfectionState::Exposed)) { - switch (previous_state) { - - case InfectionState::InfectedNoSymptoms: { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::Exposed; - } break; - - case InfectionState::InfectedSymptoms: { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedNoSymptoms; - } break; - - case InfectionState::InfectedSevere: { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedSymptoms; - } break; - - case InfectionState::InfectedCritical: { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedSevere; - } break; - - case InfectionState::Recovered: { - // roll out next infection step - p = uniform_dist(rng); - // compute correct probabilities while factoring out the chance to die - auto p_death = params.get()[{m_virus_variant, age}] * - params.get()[{m_virus_variant, age}] * - params.get()[{m_virus_variant, age}] * - params.get()[{m_virus_variant, age}]; - if (p > params.get()[{m_virus_variant, age}] / (1 - p_death)) { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedNoSymptoms; - } - else if (p > params.get()[{m_virus_variant, age}] * - params.get()[{m_virus_variant, age}] / (1 - p_death)) { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedSymptoms; - } - else if (p > params.get()[{m_virus_variant, age}] * - params.get()[{m_virus_variant, age}] * - params.get()[{m_virus_variant, age}] / (1 - p_death)) { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedSevere; - } - else { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedCritical; - } - } break; - - case InfectionState::Dead: { - p = uniform_dist(rng); - if (p < params.get()[{m_virus_variant, age}]) { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedSevere; - } - else { - time_in_state = params.get()[{m_virus_variant, age}]; - time_period = days(time_in_state.get(rng)); - previous_state = InfectionState::InfectedCritical; - } - } break; - - default: - break; + ScalarType p = uniform_dist(rng); + + StateTransition transition{InfectionState::Dead, InfectionState::InfectedSevere, TimeSpan{}}; + + if (p < params.get()[{m_virus_variant, age}]) { + transition.to_state = InfectionState::InfectedSevere; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + } + else { + transition.to_state = InfectionState::InfectedCritical; + transition.duration = days(params.get()[{m_virus_variant, age}].get(rng)); + } + + return transition; +} + +ScalarType Infection::calculate_death_probability(AgeGroup age, const Parameters& params) const +{ + return params.get()[{m_virus_variant, age}] * + params.get()[{m_virus_variant, age}] * + params.get()[{m_virus_variant, age}] * + params.get()[{m_virus_variant, age}]; +} + +ScalarType Infection::get_severity_protection_factor(const Parameters& params, ProtectionEvent latest_protection, + AgeGroup age, TimePoint current_time) const +{ + if (latest_protection.type == ProtectionType::NoProtection) { + return 0.0; + } + return params.get()[{latest_protection.type, age, m_virus_variant}]( + current_time.days() - latest_protection.time.days()); +} + +TimePoint Infection::draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params, TimePoint init_date, + InfectionState start_state, ProtectionEvent latest_protection) +{ + TimePoint start_of_init_state = init_date; // since there is no state transition from Recovered or Dead + bool init = true; // the random start time of the first state cannot be drawn + auto& uniform_dist = + UniformDistribution::get_instance(); // thus, just use the init_date as the start for this + + TimePoint current_time = init_date; + InfectionState current_state = start_state; + m_infection_course.push_back({current_time, current_state}); + + while (current_state != InfectionState::Recovered && current_state != InfectionState::Dead) { + StateTransition transition = + get_forward_transition(rng, age, params, current_state, current_time, latest_protection); + if (init && current_state != InfectionState::Susceptible) { // random init within first time period + ScalarType p = uniform_dist(rng); + start_of_init_state -= transition.duration.multiply(1 - p); + transition.duration = transition.duration.multiply(p); + init = false; } - start_date = start_date - time_period; - m_infection_course.insert(m_infection_course.begin(), {start_date, previous_state}); + current_time += transition.duration; + current_state = transition.to_state; + m_infection_course.push_back({current_time, current_state}); } - return start_date; + return start_of_init_state; +} + +TimePoint Infection::draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params, TimePoint start_of_init_state, + InfectionState init_state) +{ + TimePoint current_time = start_of_init_state; + InfectionState current_state = init_state; + + while (current_state != InfectionState::Exposed) { + StateTransition transition = get_backward_transition(rng, age, params, current_state); + current_time -= transition.duration; + current_state = transition.to_state; + m_infection_course.insert(m_infection_course.begin(), {current_time, current_state}); + } + + return current_time; } } // namespace abm diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index 3a0c92d36d..f5751c8212 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -34,6 +34,15 @@ namespace mio namespace abm { +/** + * @brief Represents a transition between infection states with duration and probability. + */ +struct StateTransition { + InfectionState from_state; + InfectionState to_state; + TimeSpan duration; +}; + /** * @brief Models the ViralLoad for an Infection, modelled on a log_10 scale. * Based on https://www.science.org/doi/full/10.1126/science.abi5273 @@ -74,7 +83,7 @@ class Infection * @param[in] detected [Default: false] If the Infection is detected. */ Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params, - TimePoint start_date, InfectionState start_state = InfectionState::Exposed, + TimePoint start_date, InfectionState start_state = InfectionState::Susceptible, ProtectionEvent latest_protection = {ProtectionType::NoProtection, TimePoint(0)}, bool detected = false); /** @@ -84,17 +93,20 @@ class Infection ScalarType get_viral_load(TimePoint t) const; /** - * @brief Get infectivity at a given time. + * @brief Get viral shed at a specific time. * Computed depending on current ViralLoad and individual invlogit function of each Person * corresponding to https://www.science.org/doi/full/10.1126/science.abi5273 * The mapping corresponds to Fig. 2 C. * Formula of invlogit function can be found here: * https://github.com/VirologyCharite/SARS-CoV-2-VL-paper/tree/main * in ExtendedMethods.html, Section 3.1.2.1. + * * Also in accordance to Fig. 3d of another publication: + * https://www.nature.com/articles/s41564-022-01105-z/figures/3 + * The result is in arbitrary units and has to be scaled to the rate "infections per day". * @param[in] t TimePoint of the querry. - * @return Infectivity at given TimePoint. + * @return Viral shed at given TimePoint. */ - ScalarType get_infectivity(TimePoint t) const; + ScalarType get_viral_shed(TimePoint t) const; /** * @brief: Get VirusVariant. @@ -163,19 +175,22 @@ class Infection * InfectedSymptoms -> Infected_Severe or InfectedSymptoms -> Recovered, * InfectedSevere -> InfectedCritical or InfectedSevere -> Recovered or InfectedSevere -> Dead, * InfectedCritical -> Recovered or InfectedCritical -> Dead, - * with artifical, hardcoded probabilites, until either Recoverd or Dead is reached. - * This is subject to change when parameter distributions for these transitions are implemented. + * until either Recoverd or Dead is reached. * The duration in each #InfectionState is taken from the respective parameter. + * The first transition has a random, uniformly distributed length from 0 to the respective parameter + * for persons that are initialized somewhere in the middle of the infection, i.e. not a newly infected + * person nor a Recovered/Dead person, to reflect uncertainty in the current state. * @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] latest_protection Latest protection against Infection, has an influence on transition probabilities. + * @return The starting date of the init_state. */ - void draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, - TimePoint init_date, InfectionState init_state, - ProtectionEvent latest_protection); + TimePoint draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + TimePoint init_date, InfectionState init_state, + ProtectionEvent latest_protection); /** * @brief Determine Infection course prior to the given #InfectionState start_state. @@ -191,12 +206,98 @@ class Infection TimePoint draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, TimePoint init_date, InfectionState init_state); + /** + * @brief Initialize the viral load parameters for the infection. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. + * @param[in] virus Virus type of the Infection. + * @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] latest_protection Latest protection against Infection. + */ + void initialize_viral_load(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, + const Parameters& params, TimePoint init_date, ProtectionEvent latest_protection); + + /** + * @brief Initialize the viral shed parameters and individual factor for the infection. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. + * @param[in] virus Virus type of the Infection. + * @param[in] age AgeGroup of the Person. + * @param[in] params Parameters of the Model. + */ + void initialize_viral_shed(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, + const Parameters& params); + + /** + * @brief Get the forward transition from a given infection 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_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, + ProtectionEvent latest_protection) const; + + /** + * @brief Get the backward transition from a given infection 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. + * @return StateTransition representing the previous transition. + */ + StateTransition get_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params, + InfectionState current_state) const; + + /** + * @brief Get the backward transition from recovered state. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. + * @param[in] age AgeGroup of the Person. + * @param[in] params Parameters of the Model. + * @return StateTransition representing the transition that led to recovery. + */ + StateTransition get_recovered_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params) const; + + /** + * @brief Get the backward transition from dead state. + * @param[inout] rng PersonalRandomNumberGenerator of the Person. + * @param[in] age AgeGroup of the Person. + * @param[in] params Parameters of the Model. + * @return StateTransition representing the transition that led to death. + */ + StateTransition get_dead_backward_transition(PersonalRandomNumberGenerator& rng, AgeGroup age, + const Parameters& params) const; + + /** + * @brief Calculate the overall death probability for the infection. + * @param[in] age AgeGroup of the Person. + * @param[in] params Parameters of the Model. + * @return The probability of death for this infection. + */ + ScalarType calculate_death_probability(AgeGroup age, const Parameters& params) const; + + /** + * @brief Get the severity protection factor based on latest protection. + * @param[in] params Parameters of the Model. + * @param[in] latest_protection Latest protection against Infection. + * @param[in] age AgeGroup of the Person. + * @param[in] current_time Current time point. + * @return The protection factor against severe outcomes. + */ + 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. VirusVariant m_virus_variant; ///< Variant of the Infection. ViralLoad m_viral_load; ///< ViralLoad of the Infection. ScalarType m_log_norm_alpha, - m_log_norm_beta; ///< Parameters for the infectivity mapping, which is modelled through an invlogit function. - ScalarType m_individual_virus_shed_factor; ///< Individual virus shed factor. + m_log_norm_beta; ///< Parameters for the viral shed mapping, which is modelled through an invlogit function. + ScalarType m_individual_viral_shed_factor; ///< Individual viral shed factor. bool m_detected; ///< Whether an Infection is detected or not. }; diff --git a/cpp/models/abm/model_functions.cpp b/cpp/models/abm/model_functions.cpp index b8536947f6..eccd1fe92a 100644 --- a/cpp/models/abm/model_functions.cpp +++ b/cpp/models/abm/model_functions.cpp @@ -32,21 +32,21 @@ namespace mio namespace abm { -ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, - const VirusVariant virus, const AgeGroup age_receiver, - const LocalInfectionParameters& params) +ScalarType total_exposure_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, + const VirusVariant virus, const AgeGroup age_receiver, + const LocalInfectionParameters& params) { assert(age_receiver < rates.size()); - ScalarType prob = 0; + ScalarType total_exposure = 0; for (AgeGroup age_transmitter(0); age_transmitter < rates.size(); ++age_transmitter) { - prob += + total_exposure += rates[{cell_index, virus, age_transmitter}] * params.get()[{age_receiver, age_transmitter}]; } - return prob; + return total_exposure; } -ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, - const VirusVariant virus, const Parameters& global_params) +ScalarType total_exposure_by_air(const AirExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, + const Parameters& global_params) { return rates[{cell_index, virus}] * global_params.get()[{virus}]; } @@ -73,24 +73,26 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const ScalarType mask_protection = person.get_mask_protective_factor(global_parameters); assert(person.get_cells().size() && "Person is in multiple cells. Interact logic is incorrect at the moment."); for (auto cell_index : person.get_cells()) { - std::pair local_indiv_trans_prob[static_cast(VirusVariant::Count)]; + std::pair local_indiv_expected_trans[static_cast(VirusVariant::Count)]; for (uint32_t v = 0; v != static_cast(VirusVariant::Count); ++v) { VirusVariant virus = static_cast(v); - ScalarType local_indiv_trans_prob_v = - (daily_transmissions_by_contacts(local_contact_exposure, cell_index, virus, age_receiver, - local_parameters) + - daily_transmissions_by_air(local_air_exposure, cell_index, virus, global_parameters)) * + ScalarType exposed_viral_shed = + (total_exposure_by_contacts(local_contact_exposure, cell_index, virus, age_receiver, + local_parameters) + + total_exposure_by_air(local_air_exposure, cell_index, virus, global_parameters)) * (1 - mask_protection) * (1 - person.get_protection_factor(t, virus, global_parameters)); - - local_indiv_trans_prob[v] = std::make_pair(virus, local_indiv_trans_prob_v); + ScalarType infection_rate = + global_parameters.get()[{virus}] * exposed_viral_shed; + local_indiv_expected_trans[v] = std::make_pair(virus, infection_rate); } VirusVariant virus = random_transition(personal_rng, VirusVariant::Count, dt, - local_indiv_trans_prob); // use VirusVariant::Count for no virus submission + 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(), - false)); // Starting time in first approximation + mio::abm::InfectionState::Susceptible, + person.get_latest_protection(t + dt / 2), + false)); // Starting time in second order approximation } } } @@ -110,14 +112,14 @@ void add_exposure_contribution(AirExposureRates& local_air_exposure, ContactExpo auto& infection = person.get_infection(); auto virus = infection.get_virus_variant(); auto age = person.get_age(); - // average infectivity over the time step to second order accuracy using midpoint rule - const auto infectivity = infection.get_infectivity(t + dt / 2); + // average viral shed over the time step to second order accuracy using midpoint rule + const auto viral_shed = infection.get_viral_shed(t + dt / 2); const auto quarantine_factor = person.is_in_quarantine(t, params) ? (1.0 - params.get()) : 1.0; for (CellIndex cell : person.get_cells()) { - auto air_contribution = infectivity * quarantine_factor; - auto contact_contribution = infectivity * quarantine_factor; + auto air_contribution = viral_shed * quarantine_factor; + auto contact_contribution = viral_shed * quarantine_factor; if (location.get_infection_parameters().get()) { air_contribution *= location.get_cells()[cell.get()].compute_space_per_person_relative(); diff --git a/cpp/models/abm/model_functions.h b/cpp/models/abm/model_functions.h index a4bd16aa67..cc551688ea 100644 --- a/cpp/models/abm/model_functions.h +++ b/cpp/models/abm/model_functions.h @@ -32,28 +32,28 @@ namespace abm { /** - * @brief Compute the number of daily transmissions for contact transmission of a virus in a cell. + * @brief Compute the total virus exposure for contact transmission in a cell (unit: per day). * @param[in] rates The local exposure rates. * @param[in] cell_index Cell index of the Cell. * @param[in] virus VirusVariant of interest. * @param[in] age_receiver AgeGroup of the receiving Person. * @param[in] params The local infection parameters. - * @return Average amount of Infection%s with the virus from the AgeGroup of the transmitter per day. + * @return Total amount of virus exposure by contacts of the receiver. */ -ScalarType daily_transmissions_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, - const VirusVariant virus, const AgeGroup age_receiver, - const LocalInfectionParameters& params); +ScalarType total_exposure_by_contacts(const ContactExposureRates& rates, const CellIndex cell_index, + const VirusVariant virus, const AgeGroup age_receiver, + const LocalInfectionParameters& params); /** - * @brief Compute the number of daily transmissions for aerosol transmission of a virus in a cell. + * @brief Compute the total virus exposure for aerosol transmission in a cell (unit: per day). * @param[in] rates The local exposure rates. * @param[in] cell_index Cell index of the Cell. * @param[in] virus VirusVariant of interest. * @param[in] global_params The parameter set of the Model. - * @return Average amount of Infection%s with the virus per day. + * @return Total amount of virus exposure by air. */ -ScalarType daily_transmissions_by_air(const AirExposureRates& rates, const CellIndex cell_index, - const VirusVariant virus, const Parameters& global_params); +ScalarType total_exposure_by_air(const AirExposureRates& rates, const CellIndex cell_index, const VirusVariant virus, + const Parameters& global_params); /** * @brief Add the contribution of a person to the local exposure rates. diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index e2289e4f9a..f18073d6dc 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -314,42 +314,39 @@ struct ViralLoadDistributions { }; /** - * @brief Parameters for the Infectivity. Default values taken as constant values that match the graph 2C from + * @brief Parameters for the viral shed. Default values taken as constant values that match the graph 2C from * https://github.com/VirologyCharite/SARS-CoV-2-VL-paper/tree/main */ -struct InfectivityDistributionsParameters { - AbstractParameterDistribution infectivity_alpha; - AbstractParameterDistribution infectivity_beta; +struct ViralShedParameters { + ScalarType viral_shed_alpha; + ScalarType viral_shed_beta; /// This method is used by the default serialization feature. auto default_serialize() { - return Members("InfectivityDistributionsParameters") - .add("infectivity_alpha", infectivity_alpha) - .add("infectivity_beta", infectivity_beta); + return Members("ViralShedParameters") + .add("viral_shed_alpha", viral_shed_alpha) + .add("viral_shed_beta", viral_shed_beta); } }; -struct InfectivityDistributions { - using Type = CustomIndexArray; +struct ViralShedDistribution { + using Type = CustomIndexArray; static Type get_default(AgeGroup size) { - Type default_val( - {VirusVariant::Count, size}, - InfectivityDistributionsParameters{AbstractParameterDistribution(ParameterDistributionConstant(-7.)), - AbstractParameterDistribution(ParameterDistributionConstant(1.))}); + Type default_val({VirusVariant::Count, size}, ViralShedParameters{-7., 1.}); return default_val; } static std::string name() { - return "InfectivityDistributions"; + return "ViralShedDistribution"; } }; /** - * @brief Individual virus shed factor to account for variability in infectious viral load spread. + * @brief Individual viral shed factor to account for variability in infectious viral load spread. */ -struct VirusShedFactor { +struct ViralShedFactor { using Type = CustomIndexArray; static Type get_default(AgeGroup size) { @@ -359,7 +356,22 @@ struct VirusShedFactor { } static std::string name() { - return "VirusShedFactor"; + return "ViralShedFactor"; + } +}; + +/** + * @brief Determines the infection rate by viral shed. Used as a linear factor. +*/ +struct InfectionRateFromViralShed { + using Type = CustomIndexArray; + static Type get_default(AgeGroup /*size*/) + { + return Type({VirusVariant::Count}, 1.0); + } + static std::string name() + { + return "InfectionRateFromViralShed"; } }; @@ -701,7 +713,7 @@ using ParametersBase = TimeInfectedSevereToRecovered, TimeInfectedSevereToDead, TimeInfectedCriticalToDead, TimeInfectedCriticalToRecovered, SymptomsPerInfectedNoSymptoms, SeverePerInfectedSymptoms, CriticalPerInfectedSevere, DeathsPerInfectedSevere, DeathsPerInfectedCritical, ViralLoadDistributions, - InfectivityDistributions, VirusShedFactor, DetectInfection, MaskProtection, AerosolTransmissionRates, + ViralShedDistribution, ViralShedFactor, InfectionRateFromViralShed, DetectInfection, MaskProtection, AerosolTransmissionRates, LockdownDate, QuarantineDuration, QuarantineEffectiveness, SocialEventRate, BasicShoppingRate, WorkRatio, SchoolRatio, GotoWorkTimeMinimum, GotoWorkTimeMaximum, GotoSchoolTimeMinimum, GotoSchoolTimeMaximum, AgeGroupGotoSchool, AgeGroupGotoWork, InfectionProtectionFactor, @@ -931,6 +943,15 @@ class Parameters : public ParametersBase return true; } + if (this->get()[{v, i}].params()[0] < 0.0) { + log_error("Constraint check: Mean of parameter ViralShedFactor of virus " + "variant {} " + "and age group {:.0f} smaller " + "than {:d}", + (uint32_t)v, (size_t)i, 0); + return true; + } + if (this->get()[{v, i}] < 0.0 || this->get()[{v, i}] > 1.0) { log_error("Constraint check: Parameter DetectInfection of virus variant {} and age group {:.0f} " "smaller than {:d} or " @@ -973,6 +994,15 @@ class Parameters : public ParametersBase } } + for (auto&& v : enum_members()) { + if (this->get()[v] < 0.0) { + log_error("Constraint check: Parameter InfectionRateFromViralShed of virus " + "variant {} is smaller than {:d}", + (uint32_t)v, 0); + return true; + } + } + if (this->get()[MaskType::Community] < 0.0 || this->get()[MaskType::Community] > 1.0) { log_error( diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index a0e450a64a..165f72573c 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -225,24 +225,39 @@ bool Person::is_compliant(PersonalRandomNumberGenerator& rng, InterventionType i return compliance_check <= get_compliance(intervention); } -ProtectionEvent Person::get_latest_protection() const +ProtectionEvent Person::get_latest_protection(TimePoint t) const { ProtectionType latest_protection_type = ProtectionType::NoProtection; - TimePoint infection_time = TimePoint(0); - if (!m_infections.empty()) { - latest_protection_type = ProtectionType::NaturalInfection; - infection_time = m_infections.back().get_start_date(); + TimePoint latest_time = TimePoint(std::numeric_limits::min()); + + // Use reverse iterators to start from the most recent infection + for (auto it = m_infections.rbegin(); it != m_infections.rend(); ++it) { + if (it->get_start_date() <= t) { + latest_exposure_type = ExposureType::NaturalInfection; + latest_time = it->get_start_date(); + break; // Stop once we find the latest infection before time t + } } - if (!m_vaccinations.empty() && infection_time.days() <= m_vaccinations.back().time.days()) { - latest_protection_type = m_vaccinations.back().type; - infection_time = m_vaccinations.back().time; + + // Look for any Vaccination that happens after the latest Infection and before time t + for (auto it = m_vaccinations.rbegin(); it != m_vaccinations.rend(); ++it) { + if (it->time <= t) { + if (it->time > latest_time) { + latest_exposure_type = it->exposure_type; + latest_time = it->time; + } + } + else { + break; // Stop once we find a vaccination after time t + } } - return ProtectionEvent{latest_protection_type, infection_time}; + + return ProtectionEvent{latest_protection_type, latest_time}; } ScalarType Person::get_protection_factor(TimePoint t, VirusVariant virus, const Parameters& params) const { - auto latest_protection = get_latest_protection(); + auto latest_protection = get_latest_protection(t); // If there is no previous protection or vaccination, return 0. if (latest_protection.type == ProtectionType::NoProtection) { return 0; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 8933e85775..d9e791fa8c 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -418,8 +418,9 @@ class Person /** * @brief Get the latest #ProtectionType and its initial TimePoint of the Person. + * @param[in] t TimePoint to check. */ - ProtectionEvent get_latest_protection() const; + ProtectionEvent get_latest_protection(TimePoint t) const; /// This method is used by the default serialization feature. auto default_serialize() diff --git a/cpp/models/abm/time.h b/cpp/models/abm/time.h index a1d50d6f76..67e2d571f8 100644 --- a/cpp/models/abm/time.h +++ b/cpp/models/abm/time.h @@ -129,6 +129,17 @@ class TimeSpan { return TimeSpan{m_seconds * f}; } + + /** + * @brief Multiplication with double and rounding down afterwards to whole seconds + * @param[in] f Factor to multiply with. + * @return New TimeSpan after multiplication. + */ + TimeSpan multiply(double f) const + { + return TimeSpan{int(m_seconds * f)}; + } + TimeSpan& operator*=(int f) { m_seconds *= f; diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index ab0df1be70..8a83bbfbaf 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -457,7 +457,7 @@ void assign_infection_state(mio::abm::Model& model, mio::abm::TimePoint t, doubl if (infection_state != mio::abm::InfectionState::Susceptible) { person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), model.parameters, t, infection_state, - person.get_latest_protection(), false)); + person.get_latest_protection(t), false)); } } } diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index 383d52e921..bd8e96cbd6 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -40,7 +40,7 @@ TEST_F(TestInfection, init) auto age_group_test = age_group_15_to_34; mio::abm::Location loc(mio::abm::LocationType::Hospital, 0); - params.get()[{virus_variant_test, age_group_test}] = + params.get()[{virus_variant_test, age_group_test}] = mio::ParameterDistributionUniform(0.1, 0.2); //set up a personal RNG for infections @@ -58,12 +58,12 @@ TEST_F(TestInfection, init) // 1st infection .WillOnce(testing::Return(0.4)) // Transition to Infected .WillOnce(testing::Return(0.6)) // Transition to Recovered - .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] - .params()[0])) // Virus Shed Factor + .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] + .params()[0])) // Viral Shed Factor // 2nd infection .WillOnce(testing::Return(1.0)) // Transition to Recovered - .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] - .params()[0])) // Virus Shed Factor + .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] + .params()[0])) // Viral Shed Factor .WillRepeatedly(testing::Return(1.0)); //Distribution for stay times @@ -90,8 +90,8 @@ TEST_F(TestInfection, init) mio::abm::InfectionState::Exposed); EXPECT_EQ(infection.get_infection_state(mio::abm::TimePoint(0) + mio::abm::days(1)), mio::abm::InfectionState::InfectedNoSymptoms); - // Test infectivity at a specific time point - EXPECT_NEAR(infection.get_infectivity(mio::abm::TimePoint(0) + mio::abm::days(3)), 0.02689414213699951, 1e-14); + // 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); // Test infection with previous exposure and recovery state transition. params.get()[{mio::abm::ProtectionType::GenericVaccine, age_group_test, @@ -110,8 +110,8 @@ TEST_F(TestInfection, init) 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); - // Test infectivity at a specific time point - EXPECT_NEAR(infection_w_previous_exp.get_infectivity(mio::abm::TimePoint(0) + mio::abm::days(3)), + // 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); } @@ -202,16 +202,16 @@ TEST_F(TestInfection, drawInfectionCourseBackward) .Times(testing::AtLeast(7)) // 1st infection .WillOnce(testing::Return(0.6)) // Transition to InfectedNoSymptoms - .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] - .params()[0])) // Virus Shed Factor + .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] + .params()[0])) // Viral Shed Factor // 2nd infection .WillOnce(testing::Return(0.4)) // Transition to InfectedSymptoms - .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] - .params()[0])) // Virus Shed Factor + .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] + .params()[0])) // Viral Shed Factor // 3rd infection .WillOnce(testing::Return(0.2)) // Transition to InfectedSevere - .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] - .params()[0])) // Virus Shed Factor + .WillOnce(testing::Return(params.get()[{virus_variant_test, age_group_test}] + .params()[0])) // Viral Shed Factor // 4th infection .WillOnce(testing::Return(0.05)) // Transition to InfectedCritical .WillRepeatedly(testing::Return(1.0)); @@ -264,8 +264,9 @@ TEST_F(TestInfection, getPersonalProtectiveFactor) auto location = mio::abm::Location(mio::abm::LocationType::School, 0, num_age_groups); auto person = mio::abm::Person(this->get_rng(), location.get_type(), location.get_id(), location.get_model_id(), age_group_15_to_34); - person.add_new_vaccination(mio::abm::ProtectionType::GenericVaccine, mio::abm::TimePoint(0)); - auto latest_protection = person.get_latest_protection(); + auto t0 = mio::abm::Timepoint(0); + person.add_new_vaccination(mio::abm::ProtectionType::GenericVaccine, t0); + auto latest_protection = person.get_latest_protection(t0); mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); // Test default parameter functions diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 0d57d18fa6..1e757b1f12 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -86,16 +86,16 @@ TEST_F(TestLocation, interact) auto t = mio::abm::TimePoint(0); auto dt = mio::abm::seconds(8640); //0.1 days - // Setup model parameters for viral loads and infectivity distributions. - // Setup model parameters for viral loads and infectivity distributions. + // Setup model parameters for viral loads and viral shed distributions. + // Setup model parameters for viral loads and viral shed distributions. mio::abm::Parameters params = mio::abm::Parameters(num_age_groups); params.set_default(num_age_groups); params.get()[{variant, age}] = {mio::ParameterDistributionConstant(1.), mio::ParameterDistributionConstant(0.0001), mio::ParameterDistributionConstant(-0.0001)}; - params.set_default(num_age_groups); - params.get()[{variant, age}] = {mio::ParameterDistributionConstant(1.), - mio::ParameterDistributionConstant(1.)}; + params.set_default(num_age_groups); + params.get()[{variant, age}] = {mio::ParameterDistributionConstant(1.), + mio::ParameterDistributionConstant(1.)}; // Set incubtion period to two days so that the newly infected person is still exposed ScopedMockDistribution>>> mock_logNorm_dist; diff --git a/cpp/tests/test_abm_model.cpp b/cpp/tests/test_abm_model.cpp index b96a7d1a40..9a39351b45 100644 --- a/cpp/tests/test_abm_model.cpp +++ b/cpp/tests/test_abm_model.cpp @@ -368,11 +368,11 @@ TEST_F(TestModel, evolveMobilityTrips) EXPECT_CALL(mock_uniform_dist2.get_mock(), invoke) .Times(testing::Exactly(6)) .WillOnce(testing::Return(1.0)) // draw transition to Recovered p1 - .WillOnce(testing::Return(0.8)) // draw random virus shed p1 + .WillOnce(testing::Return(0.8)) // draw random viral shed p1 .WillOnce(testing::Return(1.0)) // draw transition to Recovered p3 - .WillOnce(testing::Return(0.8)) // draw random virus shed p3 + .WillOnce(testing::Return(0.8)) // draw random viral shed p3 .WillOnce(testing::Return(0.0)) // draw transition from InfectedCritical p4 - .WillOnce(testing::Return(0.8)) // draw random virus shed p4 + .WillOnce(testing::Return(0.8)) // draw random viral shed p4 .RetiresOnSaturation(); auto rng_p1 = mio::abm::PersonalRandomNumberGenerator(p1); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index a605486fed..3ecec9a414 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -344,14 +344,14 @@ TEST_F(TestPerson, getLatestProtection) auto t = mio::abm::TimePoint(0); person.add_new_vaccination(mio::abm::ProtectionType::GenericVaccine, t); // Verify that the latest protection is a vaccination. - auto latest_protection = person.get_latest_protection(); + auto latest_protection = person.get_latest_protection(t); EXPECT_EQ(latest_protection.type, mio::abm::ProtectionType::GenericVaccine); EXPECT_EQ(latest_protection.time.days(), t.days()); t = mio::abm::TimePoint(40 * 24 * 60 * 60); person.add_new_infection(mio::abm::Infection(prng, static_cast(0), age_group_15_to_34, params, t, mio::abm::InfectionState::Exposed)); - latest_protection = person.get_latest_protection(); + latest_protection = person.get_latest_protection(t); // Verify that the latest protection is a natural infection. EXPECT_EQ(latest_protection.type, mio::abm::ProtectionType::NaturalInfection); EXPECT_EQ(latest_protection.time.days(), t.days());