From d132c1866dce898481e495731d40c9088798db6e Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Sat, 30 Jul 2022 10:58:17 +0200 Subject: [PATCH 01/27] first draft --- cpp/models/abm/parameters.h | 31 +++++++++- cpp/models/abm/testing_set.cpp | 53 +++++++++++++++++ cpp/models/abm/testing_set.h | 101 +++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 cpp/models/abm/testing_set.cpp create mode 100644 cpp/models/abm/testing_set.h diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index da65ad74eb..89f5aa4ad6 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -233,7 +233,19 @@ struct TestParameters { double specificity; }; -struct AntigenTest { +struct GenericTest { + using Type = TestParameters; + static Type get_default() + { + return Type{0.5, 0.5}; + } + static std::string name() + { + return "GenericTest"; + } +}; + +struct AntigenTest : public GenericTest { using Type = TestParameters; static constexpr Type get_default() { @@ -245,11 +257,28 @@ struct AntigenTest { } }; +struct PCRTest : public GenericTest { + using Type = TestParameters; + static constexpr Type get_default() + { + return Type{0.9, 0.99}; + } + static std::string name() + { + return "PCRTest"; + } +}; + /** * parameters of the testing that are the same everywhere in the world. */ using GlobalTestingParameters = ParameterSet; +/** + * parameters of the testing that are the local to the testinRule. + */ +using LocalTestingParameters = ParameterSet; + /** * parameters that govern the migration between locations. */ diff --git a/cpp/models/abm/testing_set.cpp b/cpp/models/abm/testing_set.cpp new file mode 100644 index 0000000000..b32b40c760 --- /dev/null +++ b/cpp/models/abm/testing_set.cpp @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) +* & Helmholtz Centre for Infection Research (HZI) +* +* Authors: Sascha Korf +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "abm/testing_scheme.h" + +namespace mio +{ +namespace abm +{ + +TestingScheme::TestingScheme(const std::vector& rules) + : m_test_rules(rules) + , : m_active(true) +{ +} + +TestingScheme::add_rule(const TestRule& rule) +{ + m_test_rules.push_back(rule); + if (!m_active) { //Inactive Testing scheme becomes active again with a new rule + m_active = true; + } +} + +TestingScheme::check_rules_for_activeness() const +{ + for (const TestRule& rule : m_test_rules) { + if (rule.get_active_status()) { + m_active = true; + } + } + m_active = false; +} + +} // namespace abm +} // namespace mio diff --git a/cpp/models/abm/testing_set.h b/cpp/models/abm/testing_set.h new file mode 100644 index 0000000000..ee193e2fdf --- /dev/null +++ b/cpp/models/abm/testing_set.h @@ -0,0 +1,101 @@ +/* +* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) +* & Helmholtz Centre for Infection Research (HZI) +* +* Authors: David Kermann, Sascha Korf +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef EPI_ABM_TESTING_SET_H +#define EPI_ABM_TESTING_SET_H + +namespace mio +{ +namespace abm +{ + +class TestingScheme +{ +public: + TestingScheme() = default; + + TestingScheme(const std::vector& rules); + + add_rule(const TestRule& rule); + + run_scheme(const Person& person, const Time& time, const LocationType& location_type); + + void check_rules_for_activeness() const; + + bool get_active_status() const; + { + return m_active; + } + void set_active_status(const bool active) + { + m_active = active; + } + +private: + std::vector m_test_rules = {}; + bool m_active = false; +} + +class TestRule +{ +public: + TestRule() = default; + TestRule(const std::vector& ageGroups, const std::vector& locationsGroups, + const std::vector& infectionGroups, TimeSpan interval, double probability); + + bool test_person(const Person& person) const; // Returns if the person's test. + + TimeSpan get_interval() const + { + return m_time_interval; + } + void set_interval(TimeSpan ts) + { + m_time_interval = t; + } + + double get_probability() const + { + return m_probability; + } + void set_probability(double p) + { + m_probability = p; + } + + bool active_status(TimePoint t) const + { + return; + } + +private: + std::vector m_tested_ages = {}; + std::vector m_tested_locations = {}; + std::vector m_tested_infection_states = {}; + + std::pair m_time_interval; // Time Span where this test is active. + double m_probability; // Probability that a given person actually tests themself. + LocalTestingParameters m_testing_parameters; // Local testing parameters. +} + +} // namespace abm +} // namespace mio + +#endif \ No newline at end of file From 0852b20bb7b0aa28ee9e707cd6f8d03ae812915a Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Sat, 30 Jul 2022 14:22:13 +0200 Subject: [PATCH 02/27] enhance world by testing scheme --- cpp/models/abm/testing_set.cpp | 6 +++++- cpp/models/abm/testing_set.h | 9 +++------ cpp/models/abm/world.h | 7 +++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cpp/models/abm/testing_set.cpp b/cpp/models/abm/testing_set.cpp index b32b40c760..608d26ae53 100644 --- a/cpp/models/abm/testing_set.cpp +++ b/cpp/models/abm/testing_set.cpp @@ -41,12 +41,16 @@ TestingScheme::add_rule(const TestRule& rule) TestingScheme::check_rules_for_activeness() const { + m_active = false; for (const TestRule& rule : m_test_rules) { if (rule.get_active_status()) { m_active = true; } } - m_active = false; +} + +TestingScheme::run_scheme(const Person& person, const Time& time, const LocationType& location_type) +{ } } // namespace abm diff --git a/cpp/models/abm/testing_set.h b/cpp/models/abm/testing_set.h index ee193e2fdf..45c81de835 100644 --- a/cpp/models/abm/testing_set.h +++ b/cpp/models/abm/testing_set.h @@ -43,10 +43,6 @@ class TestingScheme { return m_active; } - void set_active_status(const bool active) - { - m_active = active; - } private: std::vector m_test_rules = {}; @@ -80,9 +76,9 @@ class TestRule m_probability = p; } - bool active_status(TimePoint t) const + bool get_active_status() const { - return; + return m_active_status; } private: @@ -92,6 +88,7 @@ class TestRule std::pair m_time_interval; // Time Span where this test is active. double m_probability; // Probability that a given person actually tests themself. + bool m_active_status = true; LocalTestingParameters m_testing_parameters; // Local testing parameters. } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index ace57ca23d..5533d6baa1 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -66,10 +66,10 @@ class World } //type is move-only for stable references of persons/locations - World(World&& other) = default; + World(World&& other) = default; World& operator=(World&& other) = default; World(const World&) = delete; - World& operator=(const World&) = delete; + World& operator=(const World&) = delete; /** * prepare the world for the next simulation step. @@ -186,12 +186,15 @@ class World void use_migration_rules(bool param); bool use_migration_rules() const; + TestingScheme& add_testing_scheme(TestingScheme testing_scheme); + private: void interaction(TimePoint t, TimeSpan dt); void migration(TimePoint t, TimeSpan dt); std::vector> m_persons; std::vector> m_locations; + std::vector m_testin_schemes; GlobalInfectionParameters m_infection_parameters; MigrationParameters m_migration_parameters; GlobalTestingParameters m_testing_parameters; From a4b67037a943ff96c9e981c11a79a5d4eb748db3 Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Mon, 1 Aug 2022 18:22:23 +0200 Subject: [PATCH 03/27] First draft --- cpp/examples/abm.cpp | 6 +- cpp/models/abm/CMakeLists.txt | 6 +- cpp/models/abm/location.cpp | 1 - cpp/models/abm/location.h | 12 --- cpp/models/abm/person.h | 3 +- cpp/models/abm/testing_rule.cpp | 127 ++++++++++++++++++++++++++++++ cpp/models/abm/testing_rule.h | 74 +++++++++++++++++ cpp/models/abm/testing_scheme.cpp | 81 ++++++++++++++++--- cpp/models/abm/testing_scheme.h | 62 +++++++-------- cpp/models/abm/world.cpp | 33 +++++++- cpp/models/abm/world.h | 10 ++- cpp/tests/test_abm.cpp | 17 ++-- 12 files changed, 352 insertions(+), 80 deletions(-) create mode 100644 cpp/models/abm/testing_rule.cpp create mode 100644 cpp/models/abm/testing_rule.h diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 99dfd6945c..8c7e09054a 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -276,7 +276,7 @@ void create_assign_locations(mio::abm::World& world) // People have to get tested in the 2 days before the event auto event = world.add_location(mio::abm::LocationType::SocialEvent); world.get_individualized_location(event).get_infection_parameters().set(100); - world.get_individualized_location(event).set_testing_scheme(mio::abm::days(2), 1); + //world.get_individualized_location(event).set_testing_scheme(mio::abm::days(2), 1); // Add hospital and ICU with 5 maximum contacs. auto hospital = world.add_location(mio::abm::LocationType::Hospital); @@ -295,11 +295,11 @@ void create_assign_locations(mio::abm::World& world) auto school = world.add_location(mio::abm::LocationType::School); world.get_individualized_location(school).get_infection_parameters().set(40); - world.get_individualized_location(school).set_testing_scheme(mio::abm::days(7), 1); + //world.get_individualized_location(school).set_testing_scheme(mio::abm::days(7), 1); auto work = world.add_location(mio::abm::LocationType::Work); world.get_individualized_location(work).get_infection_parameters().set(40); - world.get_individualized_location(work).set_testing_scheme(mio::abm::days(7), 0.5); + //world.get_individualized_location(work).set_testing_scheme(mio::abm::days(7), 0.5); int counter_school = 0; int counter_work = 0; int counter_shop = 0; diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index 748444a242..16df5c2b39 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -7,6 +7,10 @@ add_library(abm simulation.h person.cpp person.h + testing_rule.cpp + testing_rule.h + testing_scheme.cpp + testing_scheme.h world.cpp world.h state.h @@ -19,8 +23,6 @@ add_library(abm trip_list.h lockdown_rules.cpp lockdown_rules.h - testing_scheme.cpp - testing_scheme.h ) target_link_libraries(abm PUBLIC memilio) target_include_directories(abm PUBLIC diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index 4ec51b5495..17ae0c6cb6 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -34,7 +34,6 @@ Location::Location(LocationType type, uint32_t index, uint32_t num_cells) , m_index(index) , m_subpopulations{} , m_cached_exposure_rate({AgeGroup::Count, VaccinationState::Count}) - , m_testing_scheme() , m_cells(std::vector(num_cells)) { } diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 064a0a6478..a371ec27c4 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -21,7 +21,6 @@ #define EPI_ABM_LOCATION_H #include "abm/parameters.h" -#include "abm/testing_scheme.h" #include "abm/state.h" #include "abm/location_type.h" @@ -174,16 +173,6 @@ class Location return m_parameters; } - void set_testing_scheme(TimeSpan interval, double probability) - { - m_testing_scheme = TestingScheme(interval, probability); - } - - const TestingScheme& get_testing_scheme() const - { - return m_testing_scheme; - } - const std::vector& get_cells() const { return m_cells; @@ -199,7 +188,6 @@ class Location std::array m_subpopulations; LocalInfectionParameters m_parameters; CustomIndexArray m_cached_exposure_rate; - TestingScheme m_testing_scheme; std::vector m_cells; }; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 5506cdc7a4..c6d1091d73 100644 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -24,8 +24,7 @@ #include "abm/age.h" #include "abm/time.h" #include "abm/parameters.h" -#include "abm/world.h" -#include "abm/time.h" +#include "abm/location.h" #include diff --git a/cpp/models/abm/testing_rule.cpp b/cpp/models/abm/testing_rule.cpp new file mode 100644 index 0000000000..b061a44a6d --- /dev/null +++ b/cpp/models/abm/testing_rule.cpp @@ -0,0 +1,127 @@ +/* +* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) +* & Helmholtz Centre for Infection Research (HZI) +* +* Authors: David Kerkmann +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "abm/testing_rule.h" + +namespace mio +{ +namespace abm +{ + +TestingRule::TestingRule(const std::vector ages, const std::vector location_types, const std::vector infection_states) + : m_ages(ages) + , m_location_types(location_types) + , m_infection_states(infection_states) +{ +} + +void TestingRule::set_age_groups(const std::vector ages) +{ + m_ages = ages; +} + +const std::vector& TestingRule::get_age_groups() const +{ + return m_ages; +} + +void TestingRule::add_age_group(const AgeGroup ageGroup) +{ + m_ages.push_back(ageGroup); + std::unique(m_ages.begin(), m_ages.end()); +} + +void TestingRule::remove_age_group(const AgeGroup ageGroup) +{ + std::remove(m_ages.begin(), m_ages.end(), ageGroup); +} + +void TestingRule::set_location_types(const std::vector locationTypes) +{ + m_location_types = locationTypes; +} + +const std::vector& TestingRule::get_location_types() const +{ + return m_location_types; +} + +void TestingRule::add_location_type(const LocationType locationType) +{ + m_location_types.push_back(locationType); + std::unique(m_location_types.begin(), m_location_types.end()); +} + +void TestingRule::remove_location_type(const LocationType locationType) +{ + std::remove(m_location_types.begin(), m_location_types.end(), locationType); +} + +void TestingRule::set_infection_states(const std::vector infection_states) +{ + m_infection_states = infection_states; +} + +const std::vector& TestingRule::get_infection_states() const { + return m_infection_states; +} + +void TestingRule::add_infection_state(const InfectionState infection_state) { + m_infection_states.push_back(infection_state); + std::unique(m_infection_states.begin(), m_infection_states.end()); +} + +void TestingRule::remove_infection_state(const InfectionState infection_state) { + std::remove(m_infection_states.begin(), m_infection_states.end(), infection_state); +} + + +bool TestingRule::evaluate(const Person& p, const Location& l) const +{ + return has_requested_age(p) && is_requested_location_type(l) && has_requested_infection_state(p); +} + +bool TestingRule::has_requested_age(const Person& p) const +{ + if (m_ages.empty()) { + return true; // no condition on the age + } + return std::count(m_ages.begin(), m_ages.end(), p.get_age()) ? true : false; +} + +bool TestingRule::is_requested_location_type(const Location& l) const +{ + if (m_location_types.empty()) { + return true; // no condition on the location + } + return std::count(m_location_types.begin(), m_location_types.end(), l.get_type()) ? true : false; +} + +bool TestingRule::has_requested_infection_state(const Person& p) const +{ + if (m_infection_states.empty()) { + return true; // no condition on infection state + } + return std::count(m_infection_states.begin(), m_infection_states.end(), p.get_infection_state()) ? true : false; +} + +} // namespace abm +} // namespace mio diff --git a/cpp/models/abm/testing_rule.h b/cpp/models/abm/testing_rule.h new file mode 100644 index 0000000000..c347e938f8 --- /dev/null +++ b/cpp/models/abm/testing_rule.h @@ -0,0 +1,74 @@ +/* +* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) +* & Helmholtz Centre for Infection Research (HZI) +* +* Authors: David Kerkmann +* +* Contact: Martin J. Kuehn +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef EPI_ABM_TESTING_RULE_H +#define EPI_ABM_TESTING_RULE_H + +#include "abm/person.h" +#include "abm/location.h" + +namespace mio +{ +namespace abm +{ + +class TestingRule +{ +public: + TestingRule(const std::vector ages = {}, const std::vector location_type = {}, const std::vector infection_states = {}); + + bool operator==(const TestingRule& other) const + { + return this == &other; // compare pointers. Still possible to clone Rules. + } + + void set_age_groups(const std::vector ages); + const std::vector& get_age_groups() const; + void add_age_group(const AgeGroup ageGroup); + void remove_age_group(const AgeGroup ageGroup); + + void set_location_types(const std::vector locationTypes); + const std::vector& get_location_types() const; + void add_location_type(const LocationType locationType); + void remove_location_type(const LocationType locationType); + + void set_infection_states(const std::vector infection_states); + const std::vector& get_infection_states() const; + void add_infection_state(const InfectionState infection_state); + void remove_infection_state(const InfectionState infection_state); + + bool evaluate(const Person& p, const Location& l) const; + +private: + bool has_requested_age(const Person& p) const; + bool is_requested_location_type(const Location& l) const; + bool has_requested_infection_state(const Person& p) const; + std::vector m_ages; + std::vector m_location_types; + std::vector m_infection_states; + +}; + + +} // namespace abm +} // namespace mio + + +#endif /* testing_rule_h */ diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index bb79cbbbfd..9691b54de6 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -1,8 +1,8 @@ -/* +/* * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * & Helmholtz Centre for Infection Research (HZI) * -* Authors: Elisabeth Kluth +* Authors: David Kerkmann * * Contact: Martin J. Kuehn * @@ -18,10 +18,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "abm/testing_scheme.h" -#include "abm/world.h" -#include "abm/location.h" -#include "abm/parameters.h" #include "memilio/utils/random_number_generator.h" namespace mio @@ -29,24 +27,83 @@ namespace mio namespace abm { -TestingScheme::TestingScheme(TimeSpan interval, double probability) - : m_time_interval(interval) +TestingScheme::TestingScheme(const std::vector testing_rules, const TimeSpan interval, const double probability) + : m_testing_rules(testing_rules) + , m_time_interval(interval) , m_probability(probability) { } -TestingScheme::TestingScheme() - : TestingScheme(seconds(std::numeric_limits::max()), 1) +const TimeSpan& TestingScheme::get_interval() const +{ + return m_time_interval; +} + +double TestingScheme::get_probability() const +{ + return m_probability; +} + +void TestingScheme::set_interval(TimeSpan t) +{ + m_time_interval = t; +} + +void TestingScheme::set_probability(double p) +{ + m_probability = p; +} + +void TestingScheme::add_testing_rule(const TestingRule rule) +{ + m_testing_rules.push_back(rule); + std::unique(m_testing_rules.begin(), m_testing_rules.end()); +} + +void TestingScheme::remove_testing_rule(const TestingRule rule) +{ + std::remove(m_testing_rules.begin(), m_testing_rules.end(), rule); +} + +const std::vector& TestingScheme::get_testing_rules() const +{ + return m_testing_rules; +} + +void TestingScheme::set_testing_rules(const std::vector testing_rules) +{ + m_testing_rules = testing_rules; +} + +const TimePoint& TestingScheme::get_start_date() const +{ + return m_start_date; +} + +const TimePoint& TestingScheme::get_end_date() const +{ + return m_end_date; +} + +const TimeSpan TestingScheme::get_duration() const +{ + return TimeSpan(m_end_date.seconds() - m_start_date.seconds()); +} + +bool TestingScheme::isActive(const TimePoint t) const { + return (m_start_date <= t && t <= m_end_date) ? true : false; } -bool TestingScheme::run_scheme(Person& person, const GlobalTestingParameters& params) const +bool TestingScheme::run_scheme(Person &person, const Location &location, const GlobalTestingParameters ¶ms) const { if (person.get_time_since_negative_test() > m_time_interval) { double random = UniformDistribution::get_instance()(); if (random < m_probability) { - return !person.get_tested(params.get()); - } + if (std::any_of(m_testing_rules.begin(), m_testing_rules.end(), [person, location](TestingRule tr){ return tr.evaluate(person, location); })) { + return !person.get_tested(params.get()); + } + } } return true; } diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index ff6db50456..7172f1e2e8 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -1,8 +1,8 @@ -/* +/* * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * & Helmholtz Centre for Infection Research (HZI) * -* Authors: Elisabeth Kluth +* Authors: David Kerkmann * * Contact: Martin J. Kuehn * @@ -21,19 +21,14 @@ #ifndef EPI_ABM_TESTING_SCHEME_H #define EPI_ABM_TESTING_SCHEME_H -#include "abm/time.h" #include "abm/parameters.h" -#include "abm/time.h" - -#include +#include "abm/testing_rule.h" namespace mio { namespace abm { -class Person; - /** * Testing Scheme to regular test people */ @@ -45,55 +40,54 @@ class TestingScheme * @param interval the interval in which people who go to the location get tested * @param probability probability with which a person gets tested */ - TestingScheme(TimeSpan interval, double probability); - - /** - * create a default testing scheme such that no regular testing happens - */ - TestingScheme(); - + TestingScheme(const std::vector testing_rules = {}, const TimeSpan interval = TimeSpan(), const double probability = 1); + + bool operator==(const TestingScheme& other) const + { + return this == &other; // compare pointers. Still possible to clone Rules. + } /** * get the time interval of this testing scheme */ - TimeSpan get_interval() const - { - return m_time_interval; - } + const TimeSpan& get_interval() const; /** * get probability of this testing scheme */ - double get_probability() const - { - return m_probability; - } - + double get_probability() const; + /** * set the time interval of this testing scheme */ - void set_interval(TimeSpan t) - { - m_time_interval = t; - } + void set_interval(TimeSpan t); /** * set probability of this testing scheme */ - void set_probability(double p) - { - m_probability = p; - } + void set_probability(double p); + void add_testing_rule(const TestingRule rule); + void remove_testing_rule(const TestingRule rule); + const std::vector& get_testing_rules() const; + void set_testing_rules(const std::vector testing_rules); + + const TimePoint& get_start_date() const; + const TimePoint& get_end_date() const; + const TimeSpan get_duration() const; + bool isActive(const TimePoint t) const; /** * runs the testing scheme and tests a person if necessary * @return if the person is allowed to enter the location */ - - bool run_scheme(Person& person, const GlobalTestingParameters& params) const; + bool run_scheme(Person& person, const Location& location, const GlobalTestingParameters& params) const; private: + std::vector m_testing_rules; TimeSpan m_time_interval; + TimePoint m_start_date; + TimePoint m_end_date; double m_probability; + }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index c7cc0661bd..b85ecfc71a 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -111,10 +111,16 @@ void World::migration(TimePoint t, TimeSpan dt) if (nonempty) { auto target_type = rule.first(*person, t, dt, m_migration_parameters); Location* target = find_location(target_type, *person); - if (target != &get_location(*person)) { - if (target->get_testing_scheme().run_scheme(*person, m_testing_parameters)) { + if (std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, target, t, this](TestingScheme ts) { + if (ts.isActive(t)) { + return ts.run_scheme(*person, *target, m_testing_parameters); + } + return false; + } )) { + if (target != &get_location(*person)) { person->migrate_to(get_location(*person), *target); } + break; } } @@ -128,7 +134,12 @@ void World::migration(TimePoint t, TimeSpan dt) auto& person = m_persons[trip.person_id]; if (!person->is_in_quarantine() && person->get_location_id() == trip.migration_origin) { Location& target = get_individualized_location(trip.migration_destination); - if (target.get_testing_scheme().run_scheme(*person, m_testing_parameters)) { + if (std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, &target, t, this](TestingScheme ts) { + if (ts.isActive(t)) { + return ts.run_scheme(*person, target, m_testing_parameters); + } + return false; + } )) { person->migrate_to(get_location(*person), target); } } @@ -146,6 +157,22 @@ void World::begin_step(TimePoint /*t*/, TimeSpan dt) } } +void World::add_testing_scheme(const TestingScheme& testing_scheme) +{ + m_testing_schemes.push_back(testing_scheme); + std::unique(m_testing_schemes.begin(), m_testing_schemes.end()); +} + +const std::vector& World::get_testing_schemes() const +{ + return m_testing_schemes; +} + +void World::set_testing_schemes(const std::vector testing_schemes) +{ + m_testing_schemes = testing_schemes; +} + auto World::get_locations() const -> Range< std::pair>::const_iterator, std::vector>::const_iterator>> { diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index ace57ca23d..427fc6334a 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -21,13 +21,12 @@ #ifndef EPI_ABM_WORLD_H #define EPI_ABM_WORLD_H -#include "abm/age.h" #include "abm/parameters.h" #include "abm/location.h" #include "abm/person.h" #include "abm/lockdown_rules.h" -#include "abm/testing_scheme.h" #include "abm/trip_list.h" +#include "abm/testing_scheme.h" #include "memilio/utils/pointer_dereferencing_iterator.h" #include "memilio/utils/stl_util.h" @@ -186,12 +185,17 @@ class World void use_migration_rules(bool param); bool use_migration_rules() const; + void add_testing_scheme(const TestingScheme& testing_scheme); + const std::vector& get_testing_schemes() const; + void set_testing_schemes(const std::vector testing_schemes); + private: void interaction(TimePoint t, TimeSpan dt); void migration(TimePoint t, TimeSpan dt); - + std::vector> m_persons; std::vector> m_locations; + std::vector m_testing_schemes; GlobalInfectionParameters m_infection_parameters; MigrationParameters m_migration_parameters; GlobalTestingParameters m_testing_parameters; diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index 336a226058..f7d3438f7b 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -86,13 +86,13 @@ TEST(TestLocation, addRemovePerson) ASSERT_EQ(location.get_cells()[2].num_infected, 0u); } -TEST(TestLocation, setTestingScheme) -{ - auto location = mio::abm::Location(mio::abm::LocationType::Home, 0); - location.set_testing_scheme(mio::abm::days(5), 0.9); - ASSERT_EQ(location.get_testing_scheme().get_interval(), mio::abm::days(5)); - ASSERT_EQ(location.get_testing_scheme().get_probability(), 0.9); -} +//TEST(TestLocation, setTestingScheme) +//{ +// auto location = mio::abm::Location(mio::abm::LocationType::Home, 0); +// location.set_testing_scheme(mio::abm::days(5), 0.9); +// ASSERT_EQ(location.get_testing_scheme().get_interval(), mio::abm::days(5)); +// ASSERT_EQ(location.get_testing_scheme().get_probability(), 0.9); +//} /** * mock of the generator function of DistributionAdapter. @@ -1294,7 +1294,7 @@ TEST(TestMigrationRules, recover) ASSERT_EQ(mio::abm::return_home_when_recovered(p_inf, t, dt, {}), mio::abm::LocationType::Hospital); } -TEST(TestTestingScheme, init) +/*TEST(TestTestingScheme, init) { auto tests = mio::abm::TestingScheme(mio::abm::days(7), 0.8); ASSERT_EQ(tests.get_interval(), mio::abm::days(7)); @@ -1329,6 +1329,7 @@ TEST(TestTestingScheme, runScheme) ASSERT_EQ(person1.is_in_quarantine(), true); ASSERT_EQ(person2.is_in_quarantine(), false); } +*/ TEST(TestWorld, evolveMigration) { From e45edada693eec028eb11ad6661865375b15469d Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Tue, 2 Aug 2022 16:11:51 +0200 Subject: [PATCH 04/27] enhance testing_scheme --- cpp/models/abm/parameters.h | 7 +-- cpp/models/abm/person.cpp | 6 -- cpp/models/abm/testing_rule.cpp | 47 ++++----------- cpp/models/abm/testing_rule.h | 32 ++++------ cpp/models/abm/testing_scheme.cpp | 59 +++++++++---------- cpp/models/abm/testing_scheme.h | 28 +++++---- cpp/models/abm/testing_set.cpp | 57 ------------------ cpp/models/abm/testing_set.h | 98 ------------------------------- cpp/models/abm/world.cpp | 32 +++++----- cpp/models/abm/world.h | 8 +-- 10 files changed, 85 insertions(+), 289 deletions(-) delete mode 100644 cpp/models/abm/testing_set.cpp delete mode 100644 cpp/models/abm/testing_set.h diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index 89f5aa4ad6..164cebb418 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -272,12 +272,7 @@ struct PCRTest : public GenericTest { /** * parameters of the testing that are the same everywhere in the world. */ -using GlobalTestingParameters = ParameterSet; - -/** - * parameters of the testing that are the local to the testinRule. - */ -using LocalTestingParameters = ParameterSet; +using GlobalTestingParameters = ParameterSet; /** * parameters that govern the migration between locations. diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 0308d6f4d8..3504f55038 100644 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -84,12 +84,6 @@ void Person::interact(TimeSpan dt, const GlobalInfectionParameters& global_infec new_infection_state == InfectionState::Infected_Critical) { m_quarantine = true; } - else if (new_infection_state == InfectionState::Infected) { - double rand = UniformDistribution::get_instance()(); - if (rand < global_infection_params.get()[this->m_age] * dt.days()) { - this->get_tested(global_testing_params.get()); - } - } else { m_quarantine = false; } diff --git a/cpp/models/abm/testing_rule.cpp b/cpp/models/abm/testing_rule.cpp index b061a44a6d..e65556d7e4 100644 --- a/cpp/models/abm/testing_rule.cpp +++ b/cpp/models/abm/testing_rule.cpp @@ -26,23 +26,14 @@ namespace mio namespace abm { -TestingRule::TestingRule(const std::vector ages, const std::vector location_types, const std::vector infection_states) +TestingRule::TestingRule(const std::vector ages, const std::vector location_types, + const std::vector infection_states) : m_ages(ages) , m_location_types(location_types) , m_infection_states(infection_states) { } -void TestingRule::set_age_groups(const std::vector ages) -{ - m_ages = ages; -} - -const std::vector& TestingRule::get_age_groups() const -{ - return m_ages; -} - void TestingRule::add_age_group(const AgeGroup ageGroup) { m_ages.push_back(ageGroup); @@ -54,47 +45,28 @@ void TestingRule::remove_age_group(const AgeGroup ageGroup) std::remove(m_ages.begin(), m_ages.end(), ageGroup); } -void TestingRule::set_location_types(const std::vector locationTypes) -{ - m_location_types = locationTypes; -} - -const std::vector& TestingRule::get_location_types() const -{ - return m_location_types; -} - void TestingRule::add_location_type(const LocationType locationType) { m_location_types.push_back(locationType); std::unique(m_location_types.begin(), m_location_types.end()); } - void TestingRule::remove_location_type(const LocationType locationType) { std::remove(m_location_types.begin(), m_location_types.end(), locationType); } -void TestingRule::set_infection_states(const std::vector infection_states) +void TestingRule::add_infection_state(const InfectionState infection_state) { - m_infection_states = infection_states; -} - -const std::vector& TestingRule::get_infection_states() const { - return m_infection_states; -} - -void TestingRule::add_infection_state(const InfectionState infection_state) { m_infection_states.push_back(infection_state); std::unique(m_infection_states.begin(), m_infection_states.end()); } -void TestingRule::remove_infection_state(const InfectionState infection_state) { +void TestingRule::remove_infection_state(const InfectionState infection_state) +{ std::remove(m_infection_states.begin(), m_infection_states.end(), infection_state); } - -bool TestingRule::evaluate(const Person& p, const Location& l) const +bool TestingRule::evaluate(const Person& p, const Location& l, const InfectionState& is) const { return has_requested_age(p) && is_requested_location_type(l) && has_requested_infection_state(p); } @@ -104,7 +76,7 @@ bool TestingRule::has_requested_age(const Person& p) const if (m_ages.empty()) { return true; // no condition on the age } - return std::count(m_ages.begin(), m_ages.end(), p.get_age()) ? true : false; + return std::find(m_ages.begin(), m_ages.end(), p.get_age()) != m_ages.end(); } bool TestingRule::is_requested_location_type(const Location& l) const @@ -112,7 +84,7 @@ bool TestingRule::is_requested_location_type(const Location& l) const if (m_location_types.empty()) { return true; // no condition on the location } - return std::count(m_location_types.begin(), m_location_types.end(), l.get_type()) ? true : false; + return std::find(m_location_types.begin(), m_location_types.end(), l.get_type()) != m_location_types.end(); } bool TestingRule::has_requested_infection_state(const Person& p) const @@ -120,7 +92,8 @@ bool TestingRule::has_requested_infection_state(const Person& p) const if (m_infection_states.empty()) { return true; // no condition on infection state } - return std::count(m_infection_states.begin(), m_infection_states.end(), p.get_infection_state()) ? true : false; + return std::find(m_infection_states.begin(), m_infection_states.end(), p.get_infection_state()) != + m_infection_states.end(); } } // namespace abm diff --git a/cpp/models/abm/testing_rule.h b/cpp/models/abm/testing_rule.h index c347e938f8..ed3eaa4a49 100644 --- a/cpp/models/abm/testing_rule.h +++ b/cpp/models/abm/testing_rule.h @@ -32,43 +32,35 @@ namespace abm class TestingRule { public: - TestingRule(const std::vector ages = {}, const std::vector location_type = {}, const std::vector infection_states = {}); - + TestingRule(const std::vector ages = {}, const std::vector location_type = {}, + const std::vector infection_states = {}); + bool operator==(const TestingRule& other) const { return this == &other; // compare pointers. Still possible to clone Rules. } - - void set_age_groups(const std::vector ages); - const std::vector& get_age_groups() const; + // nothing means everything is accepted! void add_age_group(const AgeGroup ageGroup); void remove_age_group(const AgeGroup ageGroup); - - void set_location_types(const std::vector locationTypes); - const std::vector& get_location_types() const; + void add_location_type(const LocationType locationType); void remove_location_type(const LocationType locationType); - - void set_infection_states(const std::vector infection_states); - const std::vector& get_infection_states() const; + void add_infection_state(const InfectionState infection_state); void remove_infection_state(const InfectionState infection_state); - - bool evaluate(const Person& p, const Location& l) const; - + + bool evaluate(const Person& p, const Location& l, const InfectionState& is) const; + private: bool has_requested_age(const Person& p) const; bool is_requested_location_type(const Location& l) const; bool has_requested_infection_state(const Person& p) const; - std::vector m_ages; - std::vector m_location_types; - std::vector m_infection_states; - + std::vector m_ages = {}; + std::vector m_location_types = {}; + std::vector m_infection_states = {}; }; - } // namespace abm } // namespace mio - #endif /* testing_rule_h */ diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 9691b54de6..35ad93dd1f 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -27,16 +27,17 @@ namespace mio namespace abm { -TestingScheme::TestingScheme(const std::vector testing_rules, const TimeSpan interval, const double probability) +TestingScheme::TestingScheme(const std::vector testing_rules, const TimeSpan interval, + const double probability) : m_testing_rules(testing_rules) - , m_time_interval(interval) + , m_testing_frequency(interval) , m_probability(probability) { } const TimeSpan& TestingScheme::get_interval() const { - return m_time_interval; + return m_testing_frequency; } double TestingScheme::get_probability() const @@ -46,7 +47,7 @@ double TestingScheme::get_probability() const void TestingScheme::set_interval(TimeSpan t) { - m_time_interval = t; + m_testing_frequency = t; } void TestingScheme::set_probability(double p) @@ -65,45 +66,41 @@ void TestingScheme::remove_testing_rule(const TestingRule rule) std::remove(m_testing_rules.begin(), m_testing_rules.end(), rule); } -const std::vector& TestingScheme::get_testing_rules() const -{ - return m_testing_rules; -} - -void TestingScheme::set_testing_rules(const std::vector testing_rules) -{ - m_testing_rules = testing_rules; -} +// const TimePoint& TestingScheme::get_start_date() const +// { +// return m_start_date; +// } -const TimePoint& TestingScheme::get_start_date() const -{ - return m_start_date; -} +// const TimePoint& TestingScheme::get_end_date() const +// { +// return m_end_date; +// } -const TimePoint& TestingScheme::get_end_date() const -{ - return m_end_date; -} +// const TimeSpan TestingScheme::get_duration() const +// { +// return TimeSpan(m_end_date.seconds() - m_start_date.seconds()); +// } -const TimeSpan TestingScheme::get_duration() const +bool TestingScheme::is_active() const { - return TimeSpan(m_end_date.seconds() - m_start_date.seconds()); + return m_is_active; } - -bool TestingScheme::isActive(const TimePoint t) const +void TestingScheme::update_activity_status(const TimePoint t) { - return (m_start_date <= t && t <= m_end_date) ? true : false; + m_is_active = (m_start_date <= t && t <= m_end_date); } -bool TestingScheme::run_scheme(Person &person, const Location &location, const GlobalTestingParameters ¶ms) const +bool TestingScheme::run_scheme(Person& person, const Location& location) const { - if (person.get_time_since_negative_test() > m_time_interval) { + if (person.get_time_since_negative_test() > m_testing_frequency) { double random = UniformDistribution::get_instance()(); if (random < m_probability) { - if (std::any_of(m_testing_rules.begin(), m_testing_rules.end(), [person, location](TestingRule tr){ return tr.evaluate(person, location); })) { - return !person.get_tested(params.get()); - } + if (std::any_of(m_testing_rules.begin(), m_testing_rules.end(), [person, location](TestingRule tr) { + return tr.evaluate(person, location, person.get_infection_state()); + })) { + return !person.get_tested(m_test_type.get_default()); } + } } return true; } diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index 7172f1e2e8..cd2b9d139f 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -40,8 +40,9 @@ class TestingScheme * @param interval the interval in which people who go to the location get tested * @param probability probability with which a person gets tested */ - TestingScheme(const std::vector testing_rules = {}, const TimeSpan interval = TimeSpan(), const double probability = 1); - + TestingScheme(const std::vector testing_rules = {}, const TimeSpan testing_frequency = TimeSpan(), + const double probability = 1); + bool operator==(const TestingScheme& other) const { return this == &other; // compare pointers. Still possible to clone Rules. @@ -55,7 +56,7 @@ class TestingScheme * get probability of this testing scheme */ double get_probability() const; - + /** * set the time interval of this testing scheme */ @@ -68,26 +69,27 @@ class TestingScheme void add_testing_rule(const TestingRule rule); void remove_testing_rule(const TestingRule rule); - const std::vector& get_testing_rules() const; - void set_testing_rules(const std::vector testing_rules); - - const TimePoint& get_start_date() const; - const TimePoint& get_end_date() const; - const TimeSpan get_duration() const; - bool isActive(const TimePoint t) const; + + // const TimePoint& get_start_date() const; + // const TimePoint& get_end_date() const; + // const TimeSpan get_duration() const; maybe later + + bool is_active() const; + void update_activity_status(const TimePoint t); /** * runs the testing scheme and tests a person if necessary * @return if the person is allowed to enter the location */ - bool run_scheme(Person& person, const Location& location, const GlobalTestingParameters& params) const; + bool run_scheme(Person& person, const Location& location) const; private: std::vector m_testing_rules; - TimeSpan m_time_interval; + TimeSpan m_testing_frequency; TimePoint m_start_date; TimePoint m_end_date; double m_probability; - + bool m_is_active; + GenericTest m_test_type; }; } // namespace abm diff --git a/cpp/models/abm/testing_set.cpp b/cpp/models/abm/testing_set.cpp deleted file mode 100644 index 608d26ae53..0000000000 --- a/cpp/models/abm/testing_set.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* -* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) -* & Helmholtz Centre for Infection Research (HZI) -* -* Authors: Sascha Korf -* -* Contact: Martin J. Kuehn -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -#include "abm/testing_scheme.h" - -namespace mio -{ -namespace abm -{ - -TestingScheme::TestingScheme(const std::vector& rules) - : m_test_rules(rules) - , : m_active(true) -{ -} - -TestingScheme::add_rule(const TestRule& rule) -{ - m_test_rules.push_back(rule); - if (!m_active) { //Inactive Testing scheme becomes active again with a new rule - m_active = true; - } -} - -TestingScheme::check_rules_for_activeness() const -{ - m_active = false; - for (const TestRule& rule : m_test_rules) { - if (rule.get_active_status()) { - m_active = true; - } - } -} - -TestingScheme::run_scheme(const Person& person, const Time& time, const LocationType& location_type) -{ -} - -} // namespace abm -} // namespace mio diff --git a/cpp/models/abm/testing_set.h b/cpp/models/abm/testing_set.h deleted file mode 100644 index 45c81de835..0000000000 --- a/cpp/models/abm/testing_set.h +++ /dev/null @@ -1,98 +0,0 @@ -/* -* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) -* & Helmholtz Centre for Infection Research (HZI) -* -* Authors: David Kermann, Sascha Korf -* -* Contact: Martin J. Kuehn -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -#ifndef EPI_ABM_TESTING_SET_H -#define EPI_ABM_TESTING_SET_H - -namespace mio -{ -namespace abm -{ - -class TestingScheme -{ -public: - TestingScheme() = default; - - TestingScheme(const std::vector& rules); - - add_rule(const TestRule& rule); - - run_scheme(const Person& person, const Time& time, const LocationType& location_type); - - void check_rules_for_activeness() const; - - bool get_active_status() const; - { - return m_active; - } - -private: - std::vector m_test_rules = {}; - bool m_active = false; -} - -class TestRule -{ -public: - TestRule() = default; - TestRule(const std::vector& ageGroups, const std::vector& locationsGroups, - const std::vector& infectionGroups, TimeSpan interval, double probability); - - bool test_person(const Person& person) const; // Returns if the person's test. - - TimeSpan get_interval() const - { - return m_time_interval; - } - void set_interval(TimeSpan ts) - { - m_time_interval = t; - } - - double get_probability() const - { - return m_probability; - } - void set_probability(double p) - { - m_probability = p; - } - - bool get_active_status() const - { - return m_active_status; - } - -private: - std::vector m_tested_ages = {}; - std::vector m_tested_locations = {}; - std::vector m_tested_infection_states = {}; - - std::pair m_time_interval; // Time Span where this test is active. - double m_probability; // Probability that a given person actually tests themself. - bool m_active_status = true; - LocalTestingParameters m_testing_parameters; // Local testing parameters. -} - -} // namespace abm -} // namespace mio - -#endif \ No newline at end of file diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index b85ecfc71a..6970647fd5 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -111,16 +111,12 @@ void World::migration(TimePoint t, TimeSpan dt) if (nonempty) { auto target_type = rule.first(*person, t, dt, m_migration_parameters); Location* target = find_location(target_type, *person); - if (std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, target, t, this](TestingScheme ts) { - if (ts.isActive(t)) { - return ts.run_scheme(*person, *target, m_testing_parameters); - } - return false; - } )) { + //auslagern in eine Funktion + if (run_testing_schemes(*person, *target)) { if (target != &get_location(*person)) { person->migrate_to(get_location(*person), *target); } - + break; } } @@ -134,12 +130,7 @@ void World::migration(TimePoint t, TimeSpan dt) auto& person = m_persons[trip.person_id]; if (!person->is_in_quarantine() && person->get_location_id() == trip.migration_origin) { Location& target = get_individualized_location(trip.migration_destination); - if (std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, &target, t, this](TestingScheme ts) { - if (ts.isActive(t)) { - return ts.run_scheme(*person, target, m_testing_parameters); - } - return false; - } )) { + if (run_testing_schemes(*person, target)) { person->migrate_to(get_location(*person), target); } } @@ -163,14 +154,21 @@ void World::add_testing_scheme(const TestingScheme& testing_scheme) std::unique(m_testing_schemes.begin(), m_testing_schemes.end()); } -const std::vector& World::get_testing_schemes() const +void World::update_testing_scheme_activity_status(const TimePoint t) { - return m_testing_schemes; + for (auto& ts : m_testing_schemes) { + ts.update_activity_status(t); + } } -void World::set_testing_schemes(const std::vector testing_schemes) +bool World::run_testing_schemes(Person& person, const Location& location) { - m_testing_schemes = testing_schemes; + std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location, this](TestingScheme ts) { + if (ts.is_active()) { + return ts.run_scheme(person, location); + } + return false; + }); } auto World::get_locations() const -> Range< diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 517553d165..e29ac8591d 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -186,13 +186,13 @@ class World bool use_migration_rules() const; void add_testing_scheme(const TestingScheme& testing_scheme); - const std::vector& get_testing_schemes() const; - void set_testing_schemes(const std::vector testing_schemes); - + void update_testing_scheme_activity_status(const TimePoint t); + bool run_testing_schemes(Person& person, const Location& location); + private: void interaction(TimePoint t, TimeSpan dt); void migration(TimePoint t, TimeSpan dt); - + std::vector> m_persons; std::vector> m_locations; std::vector m_testing_schemes; From 21f8b0f2f7d0391278779358919d45901eae99c1 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Tue, 2 Aug 2022 16:17:57 +0200 Subject: [PATCH 05/27] fixed old testing_scheme implementation --- cpp/models/abm/person.cpp | 3 +-- cpp/models/abm/person.h | 3 +-- cpp/models/abm/testing_rule.cpp | 2 +- cpp/models/abm/testing_rule.h | 2 +- cpp/models/abm/testing_scheme.cpp | 2 +- cpp/models/abm/world.cpp | 4 ++-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 3504f55038..1e938b160f 100644 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -60,8 +60,7 @@ Person::Person(Location& location, InfectionProperties infection_properties, Age { } -void Person::interact(TimeSpan dt, const GlobalInfectionParameters& global_infection_params, Location& loc, - const GlobalTestingParameters& global_testing_params) +void Person::interact(TimeSpan dt, const GlobalInfectionParameters& global_infection_params, Location& loc) { auto infection_state = m_infection_state; auto new_infection_state = infection_state; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index c6d1091d73..b581ff84b2 100644 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -96,8 +96,7 @@ class Person * @param dt length of the current simulation time step * @param global_infection_parameters infection parameters that are the same in all locations */ - void interact(TimeSpan dt, const GlobalInfectionParameters& global_infection_parameters, Location& loc, - const GlobalTestingParameters& global_testing_params); + void interact(TimeSpan dt, const GlobalInfectionParameters& global_infection_parameters, Location& loc); /** * migrate to a different location. diff --git a/cpp/models/abm/testing_rule.cpp b/cpp/models/abm/testing_rule.cpp index e65556d7e4..0ed4e882b7 100644 --- a/cpp/models/abm/testing_rule.cpp +++ b/cpp/models/abm/testing_rule.cpp @@ -66,7 +66,7 @@ void TestingRule::remove_infection_state(const InfectionState infection_state) std::remove(m_infection_states.begin(), m_infection_states.end(), infection_state); } -bool TestingRule::evaluate(const Person& p, const Location& l, const InfectionState& is) const +bool TestingRule::evaluate(const Person& p, const Location& l) const { return has_requested_age(p) && is_requested_location_type(l) && has_requested_infection_state(p); } diff --git a/cpp/models/abm/testing_rule.h b/cpp/models/abm/testing_rule.h index ed3eaa4a49..0cc534ce57 100644 --- a/cpp/models/abm/testing_rule.h +++ b/cpp/models/abm/testing_rule.h @@ -49,7 +49,7 @@ class TestingRule void add_infection_state(const InfectionState infection_state); void remove_infection_state(const InfectionState infection_state); - bool evaluate(const Person& p, const Location& l, const InfectionState& is) const; + bool evaluate(const Person& p, const Location& l) const; private: bool has_requested_age(const Person& p) const; diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 35ad93dd1f..03ba979006 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -96,7 +96,7 @@ bool TestingScheme::run_scheme(Person& person, const Location& location) const double random = UniformDistribution::get_instance()(); if (random < m_probability) { if (std::any_of(m_testing_rules.begin(), m_testing_rules.end(), [person, location](TestingRule tr) { - return tr.evaluate(person, location, person.get_infection_state()); + return tr.evaluate(person, location); })) { return !person.get_tested(m_test_type.get_default()); } diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 6970647fd5..262ec07356 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -59,7 +59,7 @@ void World::interaction(TimePoint /*t*/, TimeSpan dt) { for (auto&& person : m_persons) { auto& loc = get_location(*person); - person->interact(dt, m_infection_parameters, loc, m_testing_parameters); + person->interact(dt, m_infection_parameters, loc); } } @@ -163,7 +163,7 @@ void World::update_testing_scheme_activity_status(const TimePoint t) bool World::run_testing_schemes(Person& person, const Location& location) { - std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location, this](TestingScheme ts) { + return std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location](TestingScheme ts) { if (ts.is_active()) { return ts.run_scheme(person, location); } From 8577add00e1b025922f1cd1b8d61308938debbd6 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Tue, 2 Aug 2022 16:40:47 +0200 Subject: [PATCH 06/27] change cstors and update_testing_schems --- cpp/models/abm/testing_rule.h | 5 +++-- cpp/models/abm/testing_scheme.cpp | 10 +++++++--- cpp/models/abm/testing_scheme.h | 12 ++++++------ cpp/models/abm/world.cpp | 3 +++ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/cpp/models/abm/testing_rule.h b/cpp/models/abm/testing_rule.h index 0cc534ce57..579d3c8e94 100644 --- a/cpp/models/abm/testing_rule.h +++ b/cpp/models/abm/testing_rule.h @@ -32,8 +32,9 @@ namespace abm class TestingRule { public: - TestingRule(const std::vector ages = {}, const std::vector location_type = {}, - const std::vector infection_states = {}); + TestingRule() = default; + TestingRule(const std::vector ages, const std::vector location_type, + const std::vector infection_states); bool operator==(const TestingRule& other) const { diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 03ba979006..76fb567aee 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -27,11 +27,15 @@ namespace mio namespace abm { -TestingScheme::TestingScheme(const std::vector testing_rules, const TimeSpan interval, - const double probability) +TestingScheme::TestingScheme(const std::vector testing_rules, const TimeSpan testing_frequency, + TimePoint start_date, TimePoint end_date, const double probability, + const GenericTest& test_type) : m_testing_rules(testing_rules) - , m_testing_frequency(interval) + , m_testing_frequency(testing_frequency) + , m_start_date(start_date) + , m_end_date(end_date) , m_probability(probability) + , m_test_type(test_type) { } diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index cd2b9d139f..fa09f57547 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -40,8 +40,8 @@ class TestingScheme * @param interval the interval in which people who go to the location get tested * @param probability probability with which a person gets tested */ - TestingScheme(const std::vector testing_rules = {}, const TimeSpan testing_frequency = TimeSpan(), - const double probability = 1); + TestingScheme(const std::vector testing_rules, const TimeSpan testing_frequency, TimePoint start_date, + TimePoint end_date, const double probability, const GenericTest& test_type); bool operator==(const TestingScheme& other) const { @@ -83,12 +83,12 @@ class TestingScheme bool run_scheme(Person& person, const Location& location) const; private: - std::vector m_testing_rules; - TimeSpan m_testing_frequency; + std::vector m_testing_rules = {}; + TimeSpan m_testing_frequency = TimeSpan(1); TimePoint m_start_date; TimePoint m_end_date; - double m_probability; - bool m_is_active; + double m_probability = 1; + bool m_is_active = false; GenericTest m_test_type; }; diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 262ec07356..2c7490d468 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -100,6 +100,9 @@ void World::migration(TimePoint t, TimeSpan dt) std::make_pair(&go_to_icu, std::vector{LocationType::Hospital, LocationType::ICU}), std::make_pair(&go_to_quarantine, std::vector{LocationType::Home})}; } + + update_testing_scheme_activity_status(t); + for (auto&& person : m_persons) { for (auto rule : rules) { //check if transition rule can be applied From 00a8e8bd4e3b805de51a3b2e982c45ba03c3a267 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Thu, 4 Aug 2022 15:59:52 +0200 Subject: [PATCH 07/27] further enhancements --- cpp/models/abm/parameters.h | 4 +- cpp/models/abm/testing_rule.cpp | 18 ++++--- cpp/models/abm/testing_rule.h | 7 ++- cpp/models/abm/testing_scheme.cpp | 38 ++++++++------- cpp/models/abm/testing_scheme.h | 28 +++++------ cpp/models/abm/world.cpp | 3 +- cpp/tests/test_abm.cpp | 81 ++++++++++++------------------- 7 files changed, 85 insertions(+), 94 deletions(-) diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index 164cebb418..fb8373793f 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -235,9 +235,9 @@ struct TestParameters { struct GenericTest { using Type = TestParameters; - static Type get_default() + static constexpr Type get_default() { - return Type{0.5, 0.5}; + return Type{0.9, 0.99}; } static std::string name() { diff --git a/cpp/models/abm/testing_rule.cpp b/cpp/models/abm/testing_rule.cpp index 0ed4e882b7..067a4a67c9 100644 --- a/cpp/models/abm/testing_rule.cpp +++ b/cpp/models/abm/testing_rule.cpp @@ -37,33 +37,39 @@ TestingRule::TestingRule(const std::vector ages, const std::vector ages, const std::vector location_type, const std::vector infection_states); @@ -56,9 +55,9 @@ class TestingRule bool has_requested_age(const Person& p) const; bool is_requested_location_type(const Location& l) const; bool has_requested_infection_state(const Person& p) const; - std::vector m_ages = {}; - std::vector m_location_types = {}; - std::vector m_infection_states = {}; + std::vector m_ages; + std::vector m_location_types; + std::vector m_infection_states; }; } // namespace abm diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 76fb567aee..6f5b798a20 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -39,35 +39,37 @@ TestingScheme::TestingScheme(const std::vector testing_rules, const { } -const TimeSpan& TestingScheme::get_interval() const -{ - return m_testing_frequency; -} +// const TimeSpan& TestingScheme::get_interval() const +// { +// return m_testing_frequency; +// } -double TestingScheme::get_probability() const -{ - return m_probability; -} +// double TestingScheme::get_probability() const +// { +// return m_probability; +// } -void TestingScheme::set_interval(TimeSpan t) -{ - m_testing_frequency = t; -} +// void TestingScheme::set_interval(TimeSpan t) +// { +// m_testing_frequency = t; +// } -void TestingScheme::set_probability(double p) -{ - m_probability = p; -} +// void TestingScheme::set_probability(double p) +// { +// m_probability = p; +// } void TestingScheme::add_testing_rule(const TestingRule rule) { m_testing_rules.push_back(rule); - std::unique(m_testing_rules.begin(), m_testing_rules.end()); + auto last = std::unique(m_testing_rules.begin(), m_testing_rules.end()); + m_testing_rules.erase(last, m_testing_rules.end()); } void TestingScheme::remove_testing_rule(const TestingRule rule) { - std::remove(m_testing_rules.begin(), m_testing_rules.end(), rule); + auto last = std::remove(m_testing_rules.begin(), m_testing_rules.end(), rule); + m_testing_rules.erase(last, m_testing_rules.end()); } // const TimePoint& TestingScheme::get_start_date() const diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index fa09f57547..fb975d2212 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -50,22 +50,22 @@ class TestingScheme /** * get the time interval of this testing scheme */ - const TimeSpan& get_interval() const; + //const TimeSpan& get_interval() const; /** * get probability of this testing scheme */ - double get_probability() const; + //double get_probability() const; - /** - * set the time interval of this testing scheme - */ - void set_interval(TimeSpan t); + // /** + // * set the time interval of this testing scheme + // */ + // void set_interval(TimeSpan t); - /** - * set probability of this testing scheme - */ - void set_probability(double p); + // /** + // * set probability of this testing scheme + // */ + // void set_probability(double p); void add_testing_rule(const TestingRule rule); void remove_testing_rule(const TestingRule rule); @@ -83,13 +83,13 @@ class TestingScheme bool run_scheme(Person& person, const Location& location) const; private: - std::vector m_testing_rules = {}; - TimeSpan m_testing_frequency = TimeSpan(1); + std::vector m_testing_rules; + TimeSpan m_testing_frequency; + double m_probability; TimePoint m_start_date; TimePoint m_end_date; - double m_probability = 1; - bool m_is_active = false; GenericTest m_test_type; + bool m_is_active = false; }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 2c7490d468..632b5a9788 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -154,7 +154,8 @@ void World::begin_step(TimePoint /*t*/, TimeSpan dt) void World::add_testing_scheme(const TestingScheme& testing_scheme) { m_testing_schemes.push_back(testing_scheme); - std::unique(m_testing_schemes.begin(), m_testing_schemes.end()); + auto last = std::unique(m_testing_schemes.begin(), m_testing_schemes.end()); + m_testing_schemes.erase(last, m_testing_schemes.end()); } void World::update_testing_scheme_activity_status(const TimePoint t) diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index f7d3438f7b..0c01476889 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -173,7 +173,7 @@ TEST(TestPerson, init) ASSERT_EQ(person2.get_person_id(), 0u); mio::abm::TimeSpan dt = mio::abm::hours(1); - person.interact(dt, {}, location, {}); + person.interact(dt, {}, location); ASSERT_EQ(person.get_infection_state(), mio::abm::InfectionState::Carrier); } @@ -229,7 +229,7 @@ TEST(TestWorld, findLocation) auto school_id = world.add_location(mio::abm::LocationType::School); auto work_id = world.add_location(mio::abm::LocationType::Work); auto person = mio::abm::Person(home_id, mio::abm::InfectionState::Recovered_Carrier, mio::abm::AgeGroup::Age60to79, - world.get_global_infection_parameters()); + world.get_global_infection_parameters()); auto& home = world.get_individualized_location(home_id); auto& school = world.get_individualized_location(school_id); auto& work = world.get_individualized_location(work_id); @@ -249,8 +249,8 @@ TEST(TestLocation, beginStep) // Test should work identically work with any age. mio::abm::AgeGroup age = mio::abm::AgeGroup(mio::UniformIntDistribution()(0, int(mio::abm::AgeGroup::Count) - 1)); - mio::abm::VaccinationState vaccination_state = mio::abm::VaccinationState( - mio::UniformIntDistribution()(0, int(mio::abm::VaccinationState::Count) - 1)); + mio::abm::VaccinationState vaccination_state = + mio::abm::VaccinationState(mio::UniformIntDistribution()(0, int(mio::abm::VaccinationState::Count) - 1)); mio::abm::GlobalInfectionParameters params; params.set({{mio::abm::AgeGroup::Count, mio::abm::VaccinationState::Count}, 0.}); @@ -339,8 +339,8 @@ TEST(TestLocation, interact) // Test should work identically work with any age. mio::abm::AgeGroup age = mio::abm::AgeGroup(mio::UniformIntDistribution()(0, int(mio::abm::AgeGroup::Count) - 1)); - mio::abm::VaccinationState vaccination_state = mio::abm::VaccinationState( - mio::UniformIntDistribution()(0, int(mio::abm::VaccinationState::Count) - 1)); + mio::abm::VaccinationState vaccination_state = + mio::abm::VaccinationState(mio::UniformIntDistribution()(0, int(mio::abm::VaccinationState::Count) - 1)); mio::abm::GlobalInfectionParameters params; params.set({{mio::abm::AgeGroup::Count, mio::abm::VaccinationState::Count}, 0.}); @@ -388,8 +388,7 @@ TEST(TestLocation, interact) ScopedMockDistribution>>> mock_exponential_dist; - ScopedMockDistribution>>> - mock_discrete_dist; + ScopedMockDistribution>>> mock_discrete_dist; { auto susceptible = @@ -527,8 +526,7 @@ TEST(TestPerson, quarantine) auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(2)) .WillOnce(testing::Return(0.6)) @@ -545,11 +543,10 @@ TEST(TestPerson, quarantine) //setup rng mock so the person has a state transition to Recovered_Infected ScopedMockDistribution>>> mock_exponential_dist; - ScopedMockDistribution>>> - mock_discrete_dist; + ScopedMockDistribution>>> mock_discrete_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.04)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - person.interact(dt, infection_parameters, home, {}); + person.interact(dt, infection_parameters, home); ASSERT_EQ(person.get_infection_state(), mio::abm::InfectionState::Recovered_Infected); ASSERT_EQ(mio::abm::go_to_work(person, t_morning, dt, {}), mio::abm::LocationType::Work); } @@ -562,8 +559,7 @@ TEST(TestPerson, get_tested) auto infected = mio::abm::Person(loc, mio::abm::InfectionState::Infected, mio::abm::AgeGroup::Age15to34, {}); auto susceptible = mio::abm::Person(loc, mio::abm::InfectionState::Susceptible, mio::abm::AgeGroup::Age15to34, {}); - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(4) .WillOnce(Return(0.4)) @@ -602,12 +598,11 @@ TEST(TestPerson, interact) //setup rng mock so the person has a state transition ScopedMockDistribution>>> mock_exponential_dist; - ScopedMockDistribution>>> - mock_discrete_dist; + ScopedMockDistribution>>> mock_discrete_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.09)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - person.interact(dt, infection_parameters, loc, {}); + person.interact(dt, infection_parameters, loc); EXPECT_EQ(person.get_infection_state(), mio::abm::InfectionState::Recovered_Infected); EXPECT_EQ(loc.get_subpopulation(mio::abm::InfectionState::Recovered_Infected), 1); EXPECT_EQ(loc.get_subpopulation(mio::abm::InfectionState::Infected), 0); @@ -640,29 +635,28 @@ TEST(TestPerson, interact_exposed) //setup rng mock so the person becomes exposed ScopedMockDistribution>>> mock_exponential_dist; - ScopedMockDistribution>>> - mock_discrete_dist; + ScopedMockDistribution>>> mock_discrete_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.49)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); //person becomes exposed - person.interact(mio::abm::hours(12), infection_parameters, loc, {}); + person.interact(mio::abm::hours(12), infection_parameters, loc); ASSERT_EQ(person.get_infection_state(), mio::abm::InfectionState::Exposed); EXPECT_EQ(loc.get_subpopulation(mio::abm::InfectionState::Exposed), 1); EXPECT_EQ(loc.get_subpopulation(mio::abm::InfectionState::Carrier), 1); EXPECT_EQ(loc.get_subpopulation(mio::abm::InfectionState::Infected), 2); //person becomes a carrier after the incubation time runs out, not random - person.interact(mio::abm::hours(12), infection_parameters, loc, {}); + person.interact(mio::abm::hours(12), infection_parameters, loc); ASSERT_EQ(person.get_infection_state(), mio::abm::InfectionState::Exposed); - person.interact(mio::abm::hours(12), infection_parameters, loc, {}); + person.interact(mio::abm::hours(12), infection_parameters, loc); ASSERT_EQ(person.get_infection_state(), mio::abm::InfectionState::Exposed); - person.interact(mio::abm::hours(24), infection_parameters, loc, {}); + person.interact(mio::abm::hours(24), infection_parameters, loc); ASSERT_EQ(person.get_infection_state(), mio::abm::InfectionState::Exposed); - person.interact(mio::abm::hours(1), infection_parameters, loc, {}); + person.interact(mio::abm::hours(1), infection_parameters, loc); ASSERT_EQ(person.get_infection_state(), mio::abm::InfectionState::Carrier); EXPECT_EQ(loc.get_subpopulation(mio::abm::InfectionState::Exposed), 0); EXPECT_EQ(loc.get_subpopulation(mio::abm::InfectionState::Carrier), 2); @@ -750,8 +744,7 @@ TEST(TestWorld, evolveStateTransition) //setup mock so only p2 transitions ScopedMockDistribution>>> mock_exponential_dist; - ScopedMockDistribution>>> - mock_discrete_dist; + ScopedMockDistribution>>> mock_discrete_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke) .Times(testing::AtLeast(3)) .WillOnce(Return(0.51)) @@ -769,8 +762,7 @@ TEST(TestWorld, evolveStateTransition) TEST(TestMigrationRules, student_goes_to_school) { - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) .WillOnce(testing::Return(0.6)) @@ -794,8 +786,7 @@ TEST(TestMigrationRules, student_goes_to_school) TEST(TestMigrationRules, students_go_to_school_in_different_times) { - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) //Mocking the random values will define at what time the student should go to school, i.e: @@ -830,8 +821,7 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times) TEST(TestMigrationRules, students_go_to_school_in_different_times_with_smaller_time_steps) { - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) //Mocking the random values will define at what time the student should go to school, i.e: @@ -884,8 +874,7 @@ TEST(TestMigrationRules, school_return) TEST(TestMigrationRules, worker_goes_to_work) { auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) .WillOnce(testing::Return(0.6)) @@ -913,8 +902,7 @@ TEST(TestMigrationRules, worker_goes_to_work) TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) { auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) .WillOnce(testing::Return(0.6)) @@ -942,8 +930,7 @@ TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) TEST(TestMigrationRules, workers_go_to_work_in_different_times) { auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) .WillOnce(testing::Return(0.)) @@ -1053,7 +1040,7 @@ TEST(TestMigrationRules, shop_return) auto p = mio::abm::Person(home, mio::abm::InfectionState::Carrier, mio::abm::AgeGroup::Age15to34, {}); home.add_person(p); p.migrate_to(home, shop); - p.interact(dt, {}, shop, {}); //person only returns home after some time passed + p.interact(dt, {}, shop); //person only returns home after some time passed ASSERT_EQ(mio::abm::go_to_shop(p, t, dt, {}), mio::abm::LocationType::Home); } @@ -1092,7 +1079,7 @@ TEST(TestMigrationRules, event_return) auto p = mio::abm::Person(home, mio::abm::InfectionState::Carrier, mio::abm::AgeGroup::Age15to34, {}); home.add_person(p); p.migrate_to(home, shop); - p.interact(dt, {}, shop, {}); + p.interact(dt, {}, shop); ASSERT_EQ(mio::abm::go_to_event(p, t, dt, {}), mio::abm::LocationType::Home); } @@ -1106,8 +1093,7 @@ TEST(TestLockdownRules, school_closure) auto school = mio::abm::Location(mio::abm::LocationType::School, 0); //setup rng mock so one person is home schooled and the other goes to school - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) .WillOnce(testing::Return(0.4)) @@ -1143,8 +1129,7 @@ TEST(TestLockdownRules, school_opening) auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); auto school = mio::abm::Location(mio::abm::LocationType::School, 0); //setup rng mock so the person is homeschooled in case of lockdown - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(2)) .WillOnce(testing::Return(0.6)) @@ -1175,8 +1160,7 @@ TEST(TestLockdownRules, home_office) mio::abm::set_home_office(t, 0.4, params); //setup rng mock so one person goes to work and the other works at home - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(4)) .WillOnce(testing::Return(0.5)) @@ -1206,8 +1190,7 @@ TEST(TestLockdownRules, no_home_office) auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); //setup rng mock so the person works in home office - ScopedMockDistribution>>> - mock_uniform_dist; + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(2)) .WillOnce(testing::Return(0.7)) From e4bbe249c7bd7b037ac09274ad6bd38bce5e3c40 Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Fri, 5 Aug 2022 14:28:58 +0200 Subject: [PATCH 08/27] Added comments --- cpp/models/abm/testing_rule.cpp | 16 +++---- cpp/models/abm/testing_rule.h | 71 ++++++++++++++++++++++++++++--- cpp/models/abm/testing_scheme.cpp | 37 +--------------- cpp/models/abm/testing_scheme.h | 54 ++++++++++++----------- cpp/models/abm/world.h | 21 +++++++-- 5 files changed, 120 insertions(+), 79 deletions(-) diff --git a/cpp/models/abm/testing_rule.cpp b/cpp/models/abm/testing_rule.cpp index 067a4a67c9..b6513f6ac5 100644 --- a/cpp/models/abm/testing_rule.cpp +++ b/cpp/models/abm/testing_rule.cpp @@ -34,28 +34,28 @@ TestingRule::TestingRule(const std::vector ages, const std::vector ages, const std::vector location_type, + /** + * Create a testing rule. + * @param ages vector of age groups that are allowed for testing + * @param location_types vector of location types that are allowed for testing + * @param infection_states vector of infection states that are allowed for testing + * An empty vector of ages/location types/infection states means that no condition on the corresponding property is set! + */ + TestingRule(const std::vector ages, const std::vector location_types, const std::vector infection_states); + /** + * Compares two testing rules for equality. + * Compare references. Still possible to clone rules. + */ bool operator==(const TestingRule& other) const { - return this == &other; // compare pointers. Still possible to clone Rules. + return this == &other; } - // nothing means everything is accepted! - void add_age_group(const AgeGroup ageGroup); - void remove_age_group(const AgeGroup ageGroup); + + /** + * add an age group to the set of age groups that are allowed for testing + * @param age_group age group to be added + */ + void add_age_group(const AgeGroup age_group); + + /** + * remove an age group from the set of age groups that are allowed for testing + * @param age_group age group to be removed + */ + void remove_age_group(const AgeGroup age_group); - void add_location_type(const LocationType locationType); - void remove_location_type(const LocationType locationType); + /** + * add a location type to the set of location types that are allowed for testing + * @param location_type location type to be added + */ + void add_location_type(const LocationType location_type); + + /** + * remove a location tpye from the set of location tpyes that are allowed for testing + * @param location_type location type to be removed + */ + void remove_location_type(const LocationType location_type); + /** + * add an infection state to the set of infection states that are allowed for testing + * @param infection_state infection state to be added + */ void add_infection_state(const InfectionState infection_state); + + /** + * remove an infection state from the set of infection states that are allowed for testing + * @param infection_state infection state to be removed + */ void remove_infection_state(const InfectionState infection_state); + /** + * check if a person and a location meet all the required properties to get tested + * @param p person to be checked + * @param l location to be checked + */ bool evaluate(const Person& p, const Location& l) const; private: + /** + * check if a person has the required age to get tested + * @param p person to be checked + */ bool has_requested_age(const Person& p) const; + + /** + * check if a location is in the set of locations that are allowed for testing + * @param l location to be checked + */ bool is_requested_location_type(const Location& l) const; + + /** + * check if a person has the required infection state to get tested + * @param p person to be checked + */ bool has_requested_infection_state(const Person& p) const; std::vector m_ages; std::vector m_location_types; diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 6f5b798a20..3f2c37cf38 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -32,33 +32,13 @@ TestingScheme::TestingScheme(const std::vector testing_rules, const const GenericTest& test_type) : m_testing_rules(testing_rules) , m_testing_frequency(testing_frequency) + , m_probability(probability) , m_start_date(start_date) , m_end_date(end_date) - , m_probability(probability) , m_test_type(test_type) { } -// const TimeSpan& TestingScheme::get_interval() const -// { -// return m_testing_frequency; -// } - -// double TestingScheme::get_probability() const -// { -// return m_probability; -// } - -// void TestingScheme::set_interval(TimeSpan t) -// { -// m_testing_frequency = t; -// } - -// void TestingScheme::set_probability(double p) -// { -// m_probability = p; -// } - void TestingScheme::add_testing_rule(const TestingRule rule) { m_testing_rules.push_back(rule); @@ -72,21 +52,6 @@ void TestingScheme::remove_testing_rule(const TestingRule rule) m_testing_rules.erase(last, m_testing_rules.end()); } -// const TimePoint& TestingScheme::get_start_date() const -// { -// return m_start_date; -// } - -// const TimePoint& TestingScheme::get_end_date() const -// { -// return m_end_date; -// } - -// const TimeSpan TestingScheme::get_duration() const -// { -// return TimeSpan(m_end_date.seconds() - m_start_date.seconds()); -// } - bool TestingScheme::is_active() const { return m_is_active; diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index fb975d2212..00626fb7ca 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -35,47 +35,51 @@ namespace abm class TestingScheme { public: + /** - * create a testing scheme. - * @param interval the interval in which people who go to the location get tested - * @param probability probability with which a person gets tested + * Create a testing scheme. + * @param testing_rules vector of testing rules that are checked for testing + * @param testing_frequency time length of how often this scheme applies, i. e., a new test is performed after a person's last test + * @param start_date starting date of the scheme + * @param end_date ending date of the scheme + * @param probability probability of the test to be performed if a testing rule applies + * @param test_type the type of test to be performed */ TestingScheme(const std::vector testing_rules, const TimeSpan testing_frequency, TimePoint start_date, TimePoint end_date, const double probability, const GenericTest& test_type); + /** + * Compares two testing schemes for equality. + * Compare references. Still possible to clone schemes. + */ bool operator==(const TestingScheme& other) const { - return this == &other; // compare pointers. Still possible to clone Rules. + return this == &other; } + /** - * get the time interval of this testing scheme + * add a testing rule to the set of age groups that are checked for testing + * @param rule testing rule to be added */ - //const TimeSpan& get_interval() const; - + void add_testing_rule(const TestingRule rule); + /** - * get probability of this testing scheme + * remove a testing rule from the set of age groups that are checked for testing + * @param rule testing rule to be removed */ - //double get_probability() const; - - // /** - // * set the time interval of this testing scheme - // */ - // void set_interval(TimeSpan t); - - // /** - // * set probability of this testing scheme - // */ - // void set_probability(double p); - - void add_testing_rule(const TestingRule rule); void remove_testing_rule(const TestingRule rule); - // const TimePoint& get_start_date() const; - // const TimePoint& get_end_date() const; - // const TimeSpan get_duration() const; maybe later - + /** + * @return activity status of the scheme + */ bool is_active() const; + + /** + * checks if the scheme is active at a given time and updates activity status + * @param t time to be updated at + */ void update_activity_status(const TimePoint t); + /** * runs the testing scheme and tests a person if necessary * @return if the person is allowed to enter the location diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index e29ac8591d..c6b2152a61 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -151,21 +151,21 @@ class World int get_subpopulation_combined(InfectionState s, LocationType type) const; /** - *get migration parameters + * get migration parameters */ MigrationParameters& get_migration_parameters(); const MigrationParameters& get_migration_parameters() const; /** - *get global infection parameters + * get global infection parameters */ GlobalInfectionParameters& get_global_infection_parameters(); const GlobalInfectionParameters& get_global_infection_parameters() const; /** - *get global testing parameters + * get global testing parameters */ GlobalTestingParameters& get_global_testing_parameters(); @@ -185,8 +185,23 @@ class World void use_migration_rules(bool param); bool use_migration_rules() const; + /** + * adds a testing scheme to the set of all testing schemes for the simulation + * @param testing_scheme testing scheme to be added + */ void add_testing_scheme(const TestingScheme& testing_scheme); + + /** + * updates activity status of all testing scheme at a given time point + * @param t time point to be updated for + */ void update_testing_scheme_activity_status(const TimePoint t); + + /** + * runs all testing schemes for a given person and a given location + * @param person person to be checked + * @param location location to be checked + */ bool run_testing_schemes(Person& person, const Location& location); private: From e1af4ab30855520d8494b4da25d28672a2685980 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Tue, 9 Aug 2022 20:16:06 +0200 Subject: [PATCH 09/27] add unit tests, formatting and some fixes --- cpp/examples/abm.cpp | 96 +++++++++--------- cpp/models/abm/testing_rule.h | 12 +-- cpp/models/abm/world.cpp | 9 +- cpp/tests/test_abm.cpp | 181 +++++++++++++++++++++++++--------- 4 files changed, 196 insertions(+), 102 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 8c7e09054a..2509fd6b05 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -63,7 +63,8 @@ std::vector last_household_gets_the_rest(int number_of_people, int number_o * @param number_of_hh The number of households in this household group. * @return householdGroup A Class Household Group. */ -mio::abm::HouseholdGroup make_uniform_households(const mio::abm::HouseholdMember& member, int number_of_people, int number_of_hh) +mio::abm::HouseholdGroup make_uniform_households(const mio::abm::HouseholdMember& member, int number_of_people, + int number_of_hh) { // The size of each household is calculated in a vector household_size_list. @@ -89,10 +90,11 @@ mio::abm::HouseholdGroup make_uniform_households(const mio::abm::HouseholdMember * @param number_of_other_familes number_of_persons_in_household random persons. * @return A Household group. */ -mio::abm::HouseholdGroup make_homes_with_families(const mio::abm::HouseholdMember& child, const mio::abm::HouseholdMember& parent, - const mio::abm::HouseholdMember& random, int number_of_persons_in_household, - int number_of_full_familes, int number_of_half_familes, - int number_of_other_familes) +mio::abm::HouseholdGroup make_homes_with_families(const mio::abm::HouseholdMember& child, + const mio::abm::HouseholdMember& parent, + const mio::abm::HouseholdMember& random, + int number_of_persons_in_household, int number_of_full_familes, + int number_of_half_familes, int number_of_other_familes) { auto private_household_group = mio::abm::HouseholdGroup(); @@ -237,7 +239,7 @@ void create_world_from_statistical_data(mio::abm::World& world) int two_person_half_families = 1765; int two_person_other_families = 166; auto twoPersonHouseholds = make_homes_with_families(child, parent, random, 2, two_person_full_families, - two_person_half_families, two_person_other_families); + two_person_half_families, two_person_other_families); add_household_group_to_world(world, twoPersonHouseholds); // Three person households @@ -245,7 +247,7 @@ void create_world_from_statistical_data(mio::abm::World& world) int three_person_half_families = 662; int three_person_other_families = 175; auto threePersonHouseholds = make_homes_with_families(child, parent, random, 3, three_person_full_families, - three_person_half_families, three_person_other_families); + three_person_half_families, three_person_other_families); add_household_group_to_world(world, threePersonHouseholds); // Four person households @@ -253,7 +255,7 @@ void create_world_from_statistical_data(mio::abm::World& world) int four_person_half_families = 110; int four_person_other_families = 122; auto fourPersonHouseholds = make_homes_with_families(child, parent, random, 4, four_person_full_families, - four_person_half_families, four_person_other_families); + four_person_half_families, four_person_other_families); add_household_group_to_world(world, fourPersonHouseholds); // Five plus person households @@ -376,31 +378,33 @@ int main() mio::abm::GlobalInfectionParameters infection_params; // Set same parameter for all age groups - infection_params.get() = 4.; - infection_params.get() = 0.02; + infection_params.get() = 4.; + infection_params.get() = 0.02; infection_params.get() = 0.02; - infection_params.get() = 0.15; - infection_params.get() = 0.15; - infection_params.get() = 0.2; - infection_params.get() = 0.03; - infection_params.get() = 0.1; - infection_params.get() = 0.1; - infection_params.get() = 0.02; - infection_params.get() = 0.06; - infection_params.get() = 0.1; + infection_params.get() = 0.15; + infection_params.get() = 0.15; + infection_params.get() = 0.2; + infection_params.get() = 0.03; + infection_params.get() = 0.1; + infection_params.get() = 0.1; + infection_params.get() = 0.02; + infection_params.get() = 0.06; + infection_params.get() = 0.1; // Set parameters for vaccinated people of all age groups - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 4.; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.02; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.02; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.15; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.15; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.05; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.05; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.005; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.5; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.005; - infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.05; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 4.; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = + 0.02; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = + 0.02; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.15; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.15; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.05; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.05; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.005; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.5; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.005; + infection_params.get().slice(mio::abm::VaccinationState::Vaccinated) = 0.05; auto world = mio::abm::World(infection_params); @@ -439,20 +443,20 @@ int main() // set output "abm.png" // set terminal png // replot - auto f_abm = fopen("abm.txt", "w"); - fprintf(f_abm, "# t S E C I I_s I_c R_C R_I D\n"); - for (auto i = 0; i < sim.get_result().get_num_time_points(); ++i) { - fprintf(f_abm, "%f ", sim.get_result().get_time(i)); - auto v = sim.get_result().get_value(i); - for (auto j = 0; j < v.size(); ++j) { - fprintf(f_abm, "%f", v[j]); - if (j < v.size() - 1) { - fprintf(f_abm, " "); - } - } - if (i < sim.get_result().get_num_time_points() - 1) { - fprintf(f_abm, "\n"); - } - } - fclose(f_abm); + // auto f_abm = fopen("abm.txt", "w"); + // fprintf(f_abm, "# t S E C I I_s I_c R_C R_I D\n"); + // for (auto i = 0; i < sim.get_result().get_num_time_points(); ++i) { + // fprintf(f_abm, "%f ", sim.get_result().get_time(i)); + // auto v = sim.get_result().get_value(i); + // for (auto j = 0; j < v.size(); ++j) { + // fprintf(f_abm, "%f", v[j]); + // if (j < v.size() - 1) { + // fprintf(f_abm, " "); + // } + // } + // if (i < sim.get_result().get_num_time_points() - 1) { + // fprintf(f_abm, "\n"); + // } + // } + // fclose(f_abm); } diff --git a/cpp/models/abm/testing_rule.h b/cpp/models/abm/testing_rule.h index e38f10e9ee..478f23908c 100644 --- a/cpp/models/abm/testing_rule.h +++ b/cpp/models/abm/testing_rule.h @@ -50,13 +50,13 @@ class TestingRule { return this == &other; } - + /** * add an age group to the set of age groups that are allowed for testing * @param age_group age group to be added */ void add_age_group(const AgeGroup age_group); - + /** * remove an age group from the set of age groups that are allowed for testing * @param age_group age group to be removed @@ -68,7 +68,7 @@ class TestingRule * @param location_type location type to be added */ void add_location_type(const LocationType location_type); - + /** * remove a location tpye from the set of location tpyes that are allowed for testing * @param location_type location type to be removed @@ -80,7 +80,7 @@ class TestingRule * @param infection_state infection state to be added */ void add_infection_state(const InfectionState infection_state); - + /** * remove an infection state from the set of infection states that are allowed for testing * @param infection_state infection state to be removed @@ -100,13 +100,13 @@ class TestingRule * @param p person to be checked */ bool has_requested_age(const Person& p) const; - + /** * check if a location is in the set of locations that are allowed for testing * @param l location to be checked */ bool is_requested_location_type(const Location& l) const; - + /** * check if a person has the required infection state to get tested * @param p person to be checked diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 632b5a9788..d1c64f13ed 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -114,13 +114,11 @@ void World::migration(TimePoint t, TimeSpan dt) if (nonempty) { auto target_type = rule.first(*person, t, dt, m_migration_parameters); Location* target = find_location(target_type, *person); - //auslagern in eine Funktion if (run_testing_schemes(*person, *target)) { if (target != &get_location(*person)) { person->migrate_to(get_location(*person), *target); + break; } - - break; } } } @@ -167,11 +165,12 @@ void World::update_testing_scheme_activity_status(const TimePoint t) bool World::run_testing_schemes(Person& person, const Location& location) { - return std::any_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location](TestingScheme ts) { + + return std::all_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location](TestingScheme ts) { if (ts.is_active()) { return ts.run_scheme(person, location); } - return false; + return true; }); } diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index 0c01476889..4f13118092 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -86,14 +86,6 @@ TEST(TestLocation, addRemovePerson) ASSERT_EQ(location.get_cells()[2].num_infected, 0u); } -//TEST(TestLocation, setTestingScheme) -//{ -// auto location = mio::abm::Location(mio::abm::LocationType::Home, 0); -// location.set_testing_scheme(mio::abm::days(5), 0.9); -// ASSERT_EQ(location.get_testing_scheme().get_interval(), mio::abm::days(5)); -// ASSERT_EQ(location.get_testing_scheme().get_probability(), 0.9); -//} - /** * mock of the generator function of DistributionAdapter. * can't be used directly as a generator function because it is not copyable. @@ -1277,43 +1269,6 @@ TEST(TestMigrationRules, recover) ASSERT_EQ(mio::abm::return_home_when_recovered(p_inf, t, dt, {}), mio::abm::LocationType::Hospital); } -/*TEST(TestTestingScheme, init) -{ - auto tests = mio::abm::TestingScheme(mio::abm::days(7), 0.8); - ASSERT_EQ(tests.get_interval(), mio::abm::days(7)); - ASSERT_EQ(tests.get_probability(), 0.8); - - tests.set_interval(mio::abm::days(2)); - ASSERT_EQ(tests.get_interval(), mio::abm::days(2)); -} - -TEST(TestTestingScheme, runScheme) -{ - auto loc = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto person1 = mio::abm::Person(loc, mio::abm::InfectionState::Carrier, mio::abm::AgeGroup::Age5to14, {}); - auto person2 = mio::abm::Person(loc, mio::abm::InfectionState::Recovered_Carrier, mio::abm::AgeGroup::Age5to14, {}); - auto testing = mio::abm::TestingScheme(mio::abm::days(5), 0.9); - - ScopedMockDistribution>>> - mock_uniform_dist; - EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) - .Times(testing::AtLeast(2)) - .WillOnce(testing::Return(0.7)) - .WillOnce(testing::Return(0.5)); - testing.run_scheme(person1, {}); - - EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) - .Times(testing::AtLeast(2)) - .WillOnce(testing::Return(0.7)) - .WillOnce(testing::Return(0.5)); - testing.run_scheme(person2, {}); - - ASSERT_EQ(person2.get_time_since_negative_test(), mio::abm::days(0)); - ASSERT_EQ(person1.is_in_quarantine(), true); - ASSERT_EQ(person2.is_in_quarantine(), false); -} -*/ - TEST(TestWorld, evolveMigration) { using testing::Return; @@ -1465,3 +1420,139 @@ TEST(TestDiscreteDistribution, generate) ASSERT_LE(d, 4); } } + +TEST(TestTestingRule, addremoveandevaluateTestRule) +{ + auto world = mio::abm::World(); + auto home_id = world.add_location(mio::abm::LocationType::Home); + auto work_id = world.add_location(mio::abm::LocationType::Work); + auto person = mio::abm::Person(home_id, mio::abm::InfectionState::Infected, mio::abm::AgeGroup::Age15to34, + world.get_global_infection_parameters()); + auto& home = world.get_individualized_location(home_id); + auto& work = world.get_individualized_location(work_id); + person.set_assigned_location(home); + person.set_assigned_location(work); + + auto testing_rule = mio::abm::TestingRule({}, {}, {}); + testing_rule.add_infection_state(mio::abm::InfectionState::Infected); + testing_rule.add_infection_state(mio::abm::InfectionState::Carrier); + testing_rule.add_location_type(mio::abm::LocationType::Home); + testing_rule.add_location_type(mio::abm::LocationType::Work); + + ASSERT_EQ(testing_rule.evaluate(person, work), true); + ASSERT_EQ(testing_rule.evaluate(person, home), true); + + testing_rule.remove_infection_state(mio::abm::InfectionState::Infected); + ASSERT_EQ(testing_rule.evaluate(person, home), false); + + testing_rule.add_infection_state(mio::abm::InfectionState::Infected); + testing_rule.remove_location_type(mio::abm::LocationType::Home); + ASSERT_EQ(testing_rule.evaluate(person, home), false); +} + +TEST(TestTestingScheme, init) +{ + std::vector test_infection_states1 = {mio::abm::InfectionState::Infected, + mio::abm::InfectionState::Carrier}; + std::vector test_location_types1 = {mio::abm::LocationType::Home, + mio::abm::LocationType::Work}; + + auto testing_rule1 = mio::abm::TestingRule({}, test_location_types1, test_infection_states1); + std::vector testing_rules = {testing_rule1}; + + const auto testing_frequency = mio::abm::days(1); + const auto start_date = mio::abm::TimePoint(0); + const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); + const auto probability = 1.0; + const auto test_type = mio::abm::PCRTest(); + + auto testing_scheme = + mio::abm::TestingScheme(testing_rules, testing_frequency, start_date, end_date, probability, test_type); + + ASSERT_EQ(testing_scheme.is_active(), false); + testing_scheme.update_activity_status(mio::abm::TimePoint(10)); + ASSERT_EQ(testing_scheme.is_active(), true); + testing_scheme.update_activity_status(mio::abm::TimePoint(60 * 60 * 24 * 3 + 200)); + ASSERT_EQ(testing_scheme.is_active(), false); +} + +TEST(TestTestingScheme, runScheme) +{ + std::vector test_infection_states1 = {mio::abm::InfectionState::Infected, + mio::abm::InfectionState::Carrier}; + std::vector test_location_types1 = {mio::abm::LocationType::Home, + mio::abm::LocationType::Work}; + + auto testing_rule1 = mio::abm::TestingRule({}, test_location_types1, test_infection_states1); + std::vector testing_rules = {testing_rule1}; + + const auto testing_frequency = mio::abm::days(1); + const auto start_date = mio::abm::TimePoint(0); + const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); + const auto probability = 1.0; + const auto test_type = mio::abm::PCRTest(); + + auto testing_scheme = + mio::abm::TestingScheme(testing_rules, testing_frequency, start_date, end_date, probability, test_type); + + std::vector test_infection_states2 = {mio::abm::InfectionState::Recovered_Carrier}; + std::vector test_location_types2 = {mio::abm::LocationType::Home}; + auto testing_rule2 = mio::abm::TestingRule({}, test_location_types2, test_infection_states2); + testing_scheme.add_testing_rule(testing_rule2); + + auto loc_home = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto loc_work = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto person1 = mio::abm::Person(loc_home, mio::abm::InfectionState::Carrier, mio::abm::AgeGroup::Age15to34, {}); + auto person2 = + mio::abm::Person(loc_home, mio::abm::InfectionState::Recovered_Carrier, mio::abm::AgeGroup::Age15to34, {}); + + ScopedMockDistribution>>> mock_uniform_dist; + EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) + .Times(testing::AtLeast(4)) + .WillOnce(testing::Return(0.7)) + .WillOnce(testing::Return(0.5)) + .WillOnce(testing::Return(0.7)) + .WillOnce(testing::Return(0.5)); + testing_scheme.run_scheme(person1, loc_home); + testing_scheme.run_scheme(person2, loc_work); + + ASSERT_EQ(person1.is_in_quarantine(), true); + ASSERT_EQ(person2.is_in_quarantine(), false); + ASSERT_EQ(person2.get_time_since_negative_test(), mio::abm::days(0)); +} + +TEST(TestWorldTestingRule, testAddingAndUpdatingAndRunningTestingSchemes) +{ + + auto world = mio::abm::World(); + auto home_id = world.add_location(mio::abm::LocationType::Home); + auto work_id = world.add_location(mio::abm::LocationType::Work); + auto person = mio::abm::Person(home_id, mio::abm::InfectionState::Infected, mio::abm::AgeGroup::Age15to34, + world.get_global_infection_parameters()); + auto& home = world.get_individualized_location(home_id); + auto& work = world.get_individualized_location(work_id); + person.set_assigned_location(home); + person.set_assigned_location(work); + + auto testing_rule = mio::abm::TestingRule({}, {}, {}); + testing_rule.add_infection_state(mio::abm::InfectionState::Infected); + testing_rule.add_infection_state(mio::abm::InfectionState::Carrier); + testing_rule.add_location_type(mio::abm::LocationType::Home); + testing_rule.add_location_type(mio::abm::LocationType::Work); + + const auto testing_frequency = mio::abm::days(1); + const auto start_date = mio::abm::TimePoint(20); + const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); + const auto probability = 1.0; + const auto test_type = mio::abm::PCRTest(); + + auto testing_scheme = + mio::abm::TestingScheme({testing_rule}, testing_frequency, start_date, end_date, probability, test_type); + + world.add_testing_scheme(testing_scheme); + auto current_time = mio::abm::TimePoint(0); + ASSERT_EQ(world.run_testing_schemes(person, work), true); + current_time = mio::abm::TimePoint(30); + world.update_testing_scheme_activity_status(current_time); + ASSERT_EQ(world.run_testing_schemes(person, work), false); +} \ No newline at end of file From 5d3a5a414dd3ff69efce93650dcb29ff543424f7 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Tue, 9 Aug 2022 20:44:18 +0200 Subject: [PATCH 10/27] update abm example --- cpp/examples/abm.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 2509fd6b05..074c691a8c 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -286,6 +286,24 @@ void create_assign_locations(mio::abm::World& world) auto icu = world.add_location(mio::abm::LocationType::ICU); world.get_individualized_location(icu).get_infection_parameters().set(5); + // Add a testing scheme to world. + // Everybody who goes to the School, Work, or Basic shop tests themselves. + std::vector test_location_types = { + mio::abm::LocationType::School, mio::abm::LocationType::Work, mio::abm::LocationType::BasicsShop}; + + auto testing_rule1 = mio::abm::TestingRule({}, {}, {}); + std::vector testing_rules = {testing_rule1}; + + const auto testing_frequency = mio::abm::days(2); + const auto start_date = mio::abm::TimePoint(0); + const auto end_date = mio::abm::TimePoint(0) + mio::abm::days(60); + const auto probability = 0.8; + const auto test_type = mio::abm::PCRTest(); + + auto testing_scheme = + mio::abm::TestingScheme(testing_rules, testing_frequency, start_date, end_date, probability, test_type); + world.add_testing_scheme(testing_scheme); + // Add schools, workplaces and shops. // At every school there are 600 students. The maximum contacs are 40. // Students have to get tested once a week. @@ -297,11 +315,10 @@ void create_assign_locations(mio::abm::World& world) auto school = world.add_location(mio::abm::LocationType::School); world.get_individualized_location(school).get_infection_parameters().set(40); - //world.get_individualized_location(school).set_testing_scheme(mio::abm::days(7), 1); auto work = world.add_location(mio::abm::LocationType::Work); world.get_individualized_location(work).get_infection_parameters().set(40); - //world.get_individualized_location(work).set_testing_scheme(mio::abm::days(7), 0.5); + int counter_school = 0; int counter_work = 0; int counter_shop = 0; From 322a37522f95a382e6372aaf4dcbae0aa01a8a84 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Wed, 10 Aug 2022 13:46:19 +0200 Subject: [PATCH 11/27] Fix abm example and tests --- cpp/examples/abm.cpp | 34 +++++++++++++++++----------------- cpp/tests/test_abm.cpp | 5 +++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 074c691a8c..5d07754637 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -291,7 +291,7 @@ void create_assign_locations(mio::abm::World& world) std::vector test_location_types = { mio::abm::LocationType::School, mio::abm::LocationType::Work, mio::abm::LocationType::BasicsShop}; - auto testing_rule1 = mio::abm::TestingRule({}, {}, {}); + auto testing_rule1 = mio::abm::TestingRule({}, test_location_types, {}); std::vector testing_rules = {testing_rule1}; const auto testing_frequency = mio::abm::days(2); @@ -460,20 +460,20 @@ int main() // set output "abm.png" // set terminal png // replot - // auto f_abm = fopen("abm.txt", "w"); - // fprintf(f_abm, "# t S E C I I_s I_c R_C R_I D\n"); - // for (auto i = 0; i < sim.get_result().get_num_time_points(); ++i) { - // fprintf(f_abm, "%f ", sim.get_result().get_time(i)); - // auto v = sim.get_result().get_value(i); - // for (auto j = 0; j < v.size(); ++j) { - // fprintf(f_abm, "%f", v[j]); - // if (j < v.size() - 1) { - // fprintf(f_abm, " "); - // } - // } - // if (i < sim.get_result().get_num_time_points() - 1) { - // fprintf(f_abm, "\n"); - // } - // } - // fclose(f_abm); + auto f_abm = fopen("abm.txt", "w"); + fprintf(f_abm, "# t S E C I I_s I_c R_C R_I D\n"); + for (auto i = 0; i < sim.get_result().get_num_time_points(); ++i) { + fprintf(f_abm, "%f ", sim.get_result().get_time(i)); + auto v = sim.get_result().get_value(i); + for (auto j = 0; j < v.size(); ++j) { + fprintf(f_abm, "%f", v[j]); + if (j < v.size() - 1) { + fprintf(f_abm, " "); + } + } + if (i < sim.get_result().get_num_time_points() - 1) { + fprintf(f_abm, "\n"); + } + } + fclose(f_abm); } diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index 4f13118092..efda9097f2 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -1554,5 +1554,10 @@ TEST(TestWorldTestingRule, testAddingAndUpdatingAndRunningTestingSchemes) ASSERT_EQ(world.run_testing_schemes(person, work), true); current_time = mio::abm::TimePoint(30); world.update_testing_scheme_activity_status(current_time); + ScopedMockDistribution>>> mock_uniform_dist; + EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) + .Times(testing::AtLeast(4)) + .WillOnce(testing::Return(0.7)) + .WillOnce(testing::Return(0.4)); ASSERT_EQ(world.run_testing_schemes(person, work), false); } \ No newline at end of file From 4dea8eee523c252dd972ead0385148218c3242a0 Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Wed, 10 Aug 2022 13:53:53 +0200 Subject: [PATCH 12/27] Merge rule and scheme --- cpp/models/abm/testing_rule.cpp | 106 ------------------------- cpp/models/abm/testing_rule.h | 123 ------------------------------ cpp/models/abm/testing_scheme.cpp | 76 ++++++++++++++++++ cpp/models/abm/testing_scheme.h | 94 ++++++++++++++++++++++- 4 files changed, 169 insertions(+), 230 deletions(-) delete mode 100644 cpp/models/abm/testing_rule.cpp delete mode 100644 cpp/models/abm/testing_rule.h diff --git a/cpp/models/abm/testing_rule.cpp b/cpp/models/abm/testing_rule.cpp deleted file mode 100644 index b6513f6ac5..0000000000 --- a/cpp/models/abm/testing_rule.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* -* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) -* & Helmholtz Centre for Infection Research (HZI) -* -* Authors: David Kerkmann -* -* Contact: Martin J. Kuehn -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -#include "abm/testing_rule.h" - -namespace mio -{ -namespace abm -{ - -TestingRule::TestingRule(const std::vector ages, const std::vector location_types, - const std::vector infection_states) - : m_ages(ages) - , m_location_types(location_types) - , m_infection_states(infection_states) -{ -} - -void TestingRule::add_age_group(const AgeGroup age_group) -{ - m_ages.push_back(age_group); - auto last = std::unique(m_ages.begin(), m_ages.end()); - m_ages.erase(last, m_ages.end()); -} - -void TestingRule::remove_age_group(const AgeGroup age_group) -{ - auto last = std::remove(m_ages.begin(), m_ages.end(), age_group); - m_ages.erase(last, m_ages.end()); -} - -void TestingRule::add_location_type(const LocationType location_type) -{ - m_location_types.push_back(location_type); - auto last = std::unique(m_location_types.begin(), m_location_types.end()); - m_location_types.erase(last, m_location_types.end()); -} -void TestingRule::remove_location_type(const LocationType location_type) -{ - auto last = std::remove(m_location_types.begin(), m_location_types.end(), location_type); - m_location_types.erase(last, m_location_types.end()); -} - -void TestingRule::add_infection_state(const InfectionState infection_state) -{ - m_infection_states.push_back(infection_state); - auto last = std::unique(m_infection_states.begin(), m_infection_states.end()); - m_infection_states.erase(last, m_infection_states.end()); -} - -void TestingRule::remove_infection_state(const InfectionState infection_state) -{ - auto last = std::remove(m_infection_states.begin(), m_infection_states.end(), infection_state); - m_infection_states.erase(last, m_infection_states.end()); -} - -bool TestingRule::evaluate(const Person& p, const Location& l) const -{ - return has_requested_age(p) && is_requested_location_type(l) && has_requested_infection_state(p); -} - -bool TestingRule::has_requested_age(const Person& p) const -{ - if (m_ages.empty()) { - return true; // no condition on the age - } - return std::find(m_ages.begin(), m_ages.end(), p.get_age()) != m_ages.end(); -} - -bool TestingRule::is_requested_location_type(const Location& l) const -{ - if (m_location_types.empty()) { - return true; // no condition on the location - } - return std::find(m_location_types.begin(), m_location_types.end(), l.get_type()) != m_location_types.end(); -} - -bool TestingRule::has_requested_infection_state(const Person& p) const -{ - if (m_infection_states.empty()) { - return true; // no condition on infection state - } - return std::find(m_infection_states.begin(), m_infection_states.end(), p.get_infection_state()) != - m_infection_states.end(); -} - -} // namespace abm -} // namespace mio diff --git a/cpp/models/abm/testing_rule.h b/cpp/models/abm/testing_rule.h deleted file mode 100644 index 478f23908c..0000000000 --- a/cpp/models/abm/testing_rule.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -* Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) -* & Helmholtz Centre for Infection Research (HZI) -* -* Authors: David Kerkmann -* -* Contact: Martin J. Kuehn -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -#ifndef EPI_ABM_TESTING_RULE_H -#define EPI_ABM_TESTING_RULE_H - -#include "abm/person.h" -#include "abm/location.h" - -namespace mio -{ -namespace abm -{ - -class TestingRule -{ -public: - /** - * Create a testing rule. - * @param ages vector of age groups that are allowed for testing - * @param location_types vector of location types that are allowed for testing - * @param infection_states vector of infection states that are allowed for testing - * An empty vector of ages/location types/infection states means that no condition on the corresponding property is set! - */ - TestingRule(const std::vector ages, const std::vector location_types, - const std::vector infection_states); - - /** - * Compares two testing rules for equality. - * Compare references. Still possible to clone rules. - */ - bool operator==(const TestingRule& other) const - { - return this == &other; - } - - /** - * add an age group to the set of age groups that are allowed for testing - * @param age_group age group to be added - */ - void add_age_group(const AgeGroup age_group); - - /** - * remove an age group from the set of age groups that are allowed for testing - * @param age_group age group to be removed - */ - void remove_age_group(const AgeGroup age_group); - - /** - * add a location type to the set of location types that are allowed for testing - * @param location_type location type to be added - */ - void add_location_type(const LocationType location_type); - - /** - * remove a location tpye from the set of location tpyes that are allowed for testing - * @param location_type location type to be removed - */ - void remove_location_type(const LocationType location_type); - - /** - * add an infection state to the set of infection states that are allowed for testing - * @param infection_state infection state to be added - */ - void add_infection_state(const InfectionState infection_state); - - /** - * remove an infection state from the set of infection states that are allowed for testing - * @param infection_state infection state to be removed - */ - void remove_infection_state(const InfectionState infection_state); - - /** - * check if a person and a location meet all the required properties to get tested - * @param p person to be checked - * @param l location to be checked - */ - bool evaluate(const Person& p, const Location& l) const; - -private: - /** - * check if a person has the required age to get tested - * @param p person to be checked - */ - bool has_requested_age(const Person& p) const; - - /** - * check if a location is in the set of locations that are allowed for testing - * @param l location to be checked - */ - bool is_requested_location_type(const Location& l) const; - - /** - * check if a person has the required infection state to get tested - * @param p person to be checked - */ - bool has_requested_infection_state(const Person& p) const; - std::vector m_ages; - std::vector m_location_types; - std::vector m_infection_states; -}; - -} // namespace abm -} // namespace mio - -#endif /* testing_rule_h */ diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 3f2c37cf38..7e80743601 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -27,6 +27,82 @@ namespace mio namespace abm { +TestingRule::TestingRule(const std::vector ages, const std::vector location_types, + const std::vector infection_states) + : m_ages(ages) + , m_location_types(location_types) + , m_infection_states(infection_states) +{ +} + +void TestingRule::add_age_group(const AgeGroup age_group) +{ + m_ages.push_back(age_group); + auto last = std::unique(m_ages.begin(), m_ages.end()); + m_ages.erase(last, m_ages.end()); +} + +void TestingRule::remove_age_group(const AgeGroup age_group) +{ + auto last = std::remove(m_ages.begin(), m_ages.end(), age_group); + m_ages.erase(last, m_ages.end()); +} + +void TestingRule::add_location_type(const LocationType location_type) +{ + m_location_types.push_back(location_type); + auto last = std::unique(m_location_types.begin(), m_location_types.end()); + m_location_types.erase(last, m_location_types.end()); +} +void TestingRule::remove_location_type(const LocationType location_type) +{ + auto last = std::remove(m_location_types.begin(), m_location_types.end(), location_type); + m_location_types.erase(last, m_location_types.end()); +} + +void TestingRule::add_infection_state(const InfectionState infection_state) +{ + m_infection_states.push_back(infection_state); + auto last = std::unique(m_infection_states.begin(), m_infection_states.end()); + m_infection_states.erase(last, m_infection_states.end()); +} + +void TestingRule::remove_infection_state(const InfectionState infection_state) +{ + auto last = std::remove(m_infection_states.begin(), m_infection_states.end(), infection_state); + m_infection_states.erase(last, m_infection_states.end()); +} + +bool TestingRule::evaluate(const Person& p, const Location& l) const +{ + return has_requested_age(p) && is_requested_location_type(l) && has_requested_infection_state(p); +} + +bool TestingRule::has_requested_age(const Person& p) const +{ + if (m_ages.empty()) { + return true; // no condition on the age + } + return std::find(m_ages.begin(), m_ages.end(), p.get_age()) != m_ages.end(); +} + +bool TestingRule::is_requested_location_type(const Location& l) const +{ + if (m_location_types.empty()) { + return true; // no condition on the location + } + return std::find(m_location_types.begin(), m_location_types.end(), l.get_type()) != m_location_types.end(); +} + +bool TestingRule::has_requested_infection_state(const Person& p) const +{ + if (m_infection_states.empty()) { + return true; // no condition on infection state + } + return std::find(m_infection_states.begin(), m_infection_states.end(), p.get_infection_state()) != + m_infection_states.end(); +} + TestingScheme::TestingScheme(const std::vector testing_rules, const TimeSpan testing_frequency, TimePoint start_date, TimePoint end_date, const double probability, const GenericTest& test_type) diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index 00626fb7ca..64256c38ae 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -22,13 +22,105 @@ #define EPI_ABM_TESTING_SCHEME_H #include "abm/parameters.h" -#include "abm/testing_rule.h" +#include "abm/person.h" +#include "abm/location.h" namespace mio { namespace abm { +/** + * Testing Rule for Testing Scheme + */ +class TestingRule +{ +public: + /** + * Create a testing rule. + * @param ages vector of age groups that are allowed for testing + * @param location_types vector of location types that are allowed for testing + * @param infection_states vector of infection states that are allowed for testing + * An empty vector of ages/location types/infection states means that no condition on the corresponding property is set! + */ + TestingRule(const std::vector ages, const std::vector location_types, + const std::vector infection_states); + + /** + * Compares two testing rules for equality. + * Compare references. Still possible to clone rules. + */ + bool operator==(const TestingRule& other) const + { + return this == &other; + } + + /** + * add an age group to the set of age groups that are allowed for testing + * @param age_group age group to be added + */ + void add_age_group(const AgeGroup age_group); + + /** + * remove an age group from the set of age groups that are allowed for testing + * @param age_group age group to be removed + */ + void remove_age_group(const AgeGroup age_group); + + /** + * add a location type to the set of location types that are allowed for testing + * @param location_type location type to be added + */ + void add_location_type(const LocationType location_type); + + /** + * remove a location tpye from the set of location tpyes that are allowed for testing + * @param location_type location type to be removed + */ + void remove_location_type(const LocationType location_type); + + /** + * add an infection state to the set of infection states that are allowed for testing + * @param infection_state infection state to be added + */ + void add_infection_state(const InfectionState infection_state); + + /** + * remove an infection state from the set of infection states that are allowed for testing + * @param infection_state infection state to be removed + */ + void remove_infection_state(const InfectionState infection_state); + + /** + * check if a person and a location meet all the required properties to get tested + * @param p person to be checked + * @param l location to be checked + */ + bool evaluate(const Person& p, const Location& l) const; + +private: + /** + * check if a person has the required age to get tested + * @param p person to be checked + */ + bool has_requested_age(const Person& p) const; + + /** + * check if a location is in the set of locations that are allowed for testing + * @param l location to be checked + */ + bool is_requested_location_type(const Location& l) const; + + /** + * check if a person has the required infection state to get tested + * @param p person to be checked + */ + bool has_requested_infection_state(const Person& p) const; + std::vector m_ages; + std::vector m_location_types; + std::vector m_infection_states; +}; + /** * Testing Scheme to regular test people */ From 5fc2df072d615b884b4918ed56914d05411eacdb Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Wed, 10 Aug 2022 13:57:50 +0200 Subject: [PATCH 13/27] change CMake --- cpp/models/abm/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index 16df5c2b39..e18f1e80ed 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -7,8 +7,6 @@ add_library(abm simulation.h person.cpp person.h - testing_rule.cpp - testing_rule.h testing_scheme.cpp testing_scheme.h world.cpp From 693a6de6d5925cee050578b04c1142092d9e6055 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Wed, 10 Aug 2022 15:47:45 +0200 Subject: [PATCH 14/27] add some more improvments to == and adder --- cpp/models/abm/testing_scheme.cpp | 24 ++++++++++++------------ cpp/models/abm/testing_scheme.h | 19 ++++++++++++------- cpp/models/abm/world.cpp | 6 +++--- cpp/tests/test_abm.cpp | 10 +++++++--- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 7e80743601..96e37127f8 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -37,9 +37,9 @@ TestingRule::TestingRule(const std::vector ages, const std::vector testing_rules, const void TestingScheme::add_testing_rule(const TestingRule rule) { - m_testing_rules.push_back(rule); - auto last = std::unique(m_testing_rules.begin(), m_testing_rules.end()); - m_testing_rules.erase(last, m_testing_rules.end()); + if (std::find(m_testing_rules.begin(), m_testing_rules.end(), rule) == m_testing_rules.end()) { + m_testing_rules.push_back(rule); + } } void TestingScheme::remove_testing_rule(const TestingRule rule) diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index 64256c38ae..6516583dce 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -52,7 +52,8 @@ class TestingRule */ bool operator==(const TestingRule& other) const { - return this == &other; + return ((this->m_ages == other.m_ages) && (this->m_infection_states == other.m_infection_states) && + (this->m_location_types == other.m_location_types)); } /** @@ -127,7 +128,6 @@ class TestingRule class TestingScheme { public: - /** * Create a testing scheme. * @param testing_rules vector of testing rules that are checked for testing @@ -146,15 +146,20 @@ class TestingScheme */ bool operator==(const TestingScheme& other) const { - return this == &other; + return this->m_testing_rules == other.m_testing_rules && + this->m_testing_frequency == other.m_testing_frequency && this->m_start_date == other.m_start_date && + this->m_end_date == other.m_end_date && this->m_probability == other.m_probability && + this->m_test_type.get_default().sensitivity == other.m_test_type.get_default().sensitivity && + this->m_test_type.get_default().specificity == other.m_test_type.get_default().specificity; + //To be adjusted and also TestType should be static. } - + /** * add a testing rule to the set of age groups that are checked for testing * @param rule testing rule to be added */ void add_testing_rule(const TestingRule rule); - + /** * remove a testing rule from the set of age groups that are checked for testing * @param rule testing rule to be removed @@ -165,13 +170,13 @@ class TestingScheme * @return activity status of the scheme */ bool is_active() const; - + /** * checks if the scheme is active at a given time and updates activity status * @param t time to be updated at */ void update_activity_status(const TimePoint t); - + /** * runs the testing scheme and tests a person if necessary * @return if the person is allowed to enter the location diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index d1c64f13ed..57dfe53f93 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -151,9 +151,9 @@ void World::begin_step(TimePoint /*t*/, TimeSpan dt) void World::add_testing_scheme(const TestingScheme& testing_scheme) { - m_testing_schemes.push_back(testing_scheme); - auto last = std::unique(m_testing_schemes.begin(), m_testing_schemes.end()); - m_testing_schemes.erase(last, m_testing_schemes.end()); + if (std::find(m_testing_schemes.begin(), m_testing_schemes.end(), testing_scheme) == m_testing_schemes.end()) { + m_testing_schemes.push_back(testing_scheme); + } } void World::update_testing_scheme_activity_status(const TimePoint t) diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index efda9097f2..6659851e0f 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -1513,12 +1513,16 @@ TEST(TestTestingScheme, runScheme) .WillOnce(testing::Return(0.5)) .WillOnce(testing::Return(0.7)) .WillOnce(testing::Return(0.5)); - testing_scheme.run_scheme(person1, loc_home); - testing_scheme.run_scheme(person2, loc_work); + ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), false); + ASSERT_EQ(testing_scheme.run_scheme(person2, loc_work), true); ASSERT_EQ(person1.is_in_quarantine(), true); ASSERT_EQ(person2.is_in_quarantine(), false); ASSERT_EQ(person2.get_time_since_negative_test(), mio::abm::days(0)); + + testing_scheme.add_testing_rule(testing_rule1); + testing_scheme.remove_testing_rule(testing_rule1); + ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), true); } TEST(TestWorldTestingRule, testAddingAndUpdatingAndRunningTestingSchemes) @@ -1556,7 +1560,7 @@ TEST(TestWorldTestingRule, testAddingAndUpdatingAndRunningTestingSchemes) world.update_testing_scheme_activity_status(current_time); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) - .Times(testing::AtLeast(4)) + .Times(testing::AtLeast(2)) .WillOnce(testing::Return(0.7)) .WillOnce(testing::Return(0.4)); ASSERT_EQ(world.run_testing_schemes(person, work), false); From 5341e1b204416acedb65e444af22f51d609ebb7c Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Thu, 11 Aug 2022 10:09:40 +0200 Subject: [PATCH 15/27] Added Authors --- cpp/models/abm/testing_scheme.cpp | 2 +- cpp/models/abm/testing_scheme.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_scheme.cpp index 96e37127f8..a9ad3bd182 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_scheme.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * & Helmholtz Centre for Infection Research (HZI) * -* Authors: David Kerkmann +* Authors: Elisabeth Kluth, David Kerkmann, Sascha Korf * * Contact: Martin J. Kuehn * diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_scheme.h index 6516583dce..b66858d3c7 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_scheme.h @@ -2,7 +2,7 @@ * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * & Helmholtz Centre for Infection Research (HZI) * -* Authors: David Kerkmann +* Authors: Elisabeth Kluth, David Kerkmann, Sascha Korf * * Contact: Martin J. Kuehn * From f36e2c92b512f6a29a542e6117b28b5d04290f4e Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Mon, 15 Aug 2022 09:42:41 +0200 Subject: [PATCH 16/27] Unittest skip for CI --- .../memilio/epidata_test/test_epidata_geoModificationGermany.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py b/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py index 283e2623f2..5b17254322 100644 --- a/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py +++ b/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py @@ -390,6 +390,7 @@ def test_get_official_county_table(self): if(name not in county_table.columns.tolist()): self.assertFalse("headers have changed.") + @unittest.skip("Link doesn't work for CI. Needs to be fixed.") @patch('builtins.print') def test_get_nuts3_county_id_map(self, mock_print): # merge_berlin = True, merge_eisenach = False From 085941cbcc69fe6ddc97111a885743323a28d4e3 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Sat, 20 Aug 2022 11:26:59 +0200 Subject: [PATCH 17/27] changed testing_scheme to testing strategy, added some tests --- cpp/examples/abm.cpp | 9 +- cpp/models/abm/CMakeLists.txt | 4 +- cpp/models/abm/abm.h | 2 +- cpp/models/abm/parameters.h | 5 - ...esting_scheme.cpp => testing_strategy.cpp} | 92 ++++++++++----- .../{testing_scheme.h => testing_strategy.h} | 105 ++++++++++++------ cpp/models/abm/world.cpp | 52 +++------ cpp/models/abm/world.h | 35 ++---- cpp/tests/test_abm.cpp | 99 +++++++++-------- 9 files changed, 213 insertions(+), 190 deletions(-) rename cpp/models/abm/{testing_scheme.cpp => testing_strategy.cpp} (50%) rename cpp/models/abm/{testing_scheme.h => testing_strategy.h} (58%) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index 5d07754637..e7941d0b3d 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -291,8 +291,8 @@ void create_assign_locations(mio::abm::World& world) std::vector test_location_types = { mio::abm::LocationType::School, mio::abm::LocationType::Work, mio::abm::LocationType::BasicsShop}; - auto testing_rule1 = mio::abm::TestingRule({}, test_location_types, {}); - std::vector testing_rules = {testing_rule1}; + auto testing_criteria1 = mio::abm::TestingCriteria({}, test_location_types, {}); + std::vector testing_criteria = {testing_criteria1}; const auto testing_frequency = mio::abm::days(2); const auto start_date = mio::abm::TimePoint(0); @@ -301,9 +301,8 @@ void create_assign_locations(mio::abm::World& world) const auto test_type = mio::abm::PCRTest(); auto testing_scheme = - mio::abm::TestingScheme(testing_rules, testing_frequency, start_date, end_date, probability, test_type); - world.add_testing_scheme(testing_scheme); - + mio::abm::TestingScheme(testing_criteria, testing_frequency, start_date, end_date, probability, test_type); + world.get_testing_strategy().add_testing_scheme(testing_scheme); // Add schools, workplaces and shops. // At every school there are 600 students. The maximum contacs are 40. // Students have to get tested once a week. diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index e18f1e80ed..a16a317af3 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -7,8 +7,8 @@ add_library(abm simulation.h person.cpp person.h - testing_scheme.cpp - testing_scheme.h + testing_strategy.cpp + testing_strategy.h world.cpp world.h state.h diff --git a/cpp/models/abm/abm.h b/cpp/models/abm/abm.h index 23fbde4ea2..66125ddd01 100644 --- a/cpp/models/abm/abm.h +++ b/cpp/models/abm/abm.h @@ -32,6 +32,6 @@ #include "abm/location_type.h" #include "memilio/utils/random_number_generator.h" #include "abm/migration_rules.h" -#include "abm/testing_scheme.h" +#include "abm/testing_strategy.h" #endif diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index fb8373793f..148965a7c6 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -269,11 +269,6 @@ struct PCRTest : public GenericTest { } }; -/** - * parameters of the testing that are the same everywhere in the world. - */ -using GlobalTestingParameters = ParameterSet; - /** * parameters that govern the migration between locations. */ diff --git a/cpp/models/abm/testing_scheme.cpp b/cpp/models/abm/testing_strategy.cpp similarity index 50% rename from cpp/models/abm/testing_scheme.cpp rename to cpp/models/abm/testing_strategy.cpp index a9ad3bd182..f2be1cea87 100644 --- a/cpp/models/abm/testing_scheme.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -19,7 +19,7 @@ * limitations under the License. */ -#include "abm/testing_scheme.h" +#include "abm/testing_strategy.h" #include "memilio/utils/random_number_generator.h" namespace mio @@ -27,58 +27,58 @@ namespace mio namespace abm { -TestingRule::TestingRule(const std::vector ages, const std::vector location_types, - const std::vector infection_states) +TestingCriteria::TestingCriteria(const std::vector& ages, const std::vector& location_types, + const std::vector& infection_states) : m_ages(ages) , m_location_types(location_types) , m_infection_states(infection_states) { } -void TestingRule::add_age_group(const AgeGroup age_group) +void TestingCriteria::add_age_group(const AgeGroup age_group) { if (std::find(m_ages.begin(), m_ages.end(), age_group) == m_ages.end()) { m_ages.push_back(age_group); } } -void TestingRule::remove_age_group(const AgeGroup age_group) +void TestingCriteria::remove_age_group(const AgeGroup age_group) { auto last = std::remove(m_ages.begin(), m_ages.end(), age_group); m_ages.erase(last, m_ages.end()); } -void TestingRule::add_location_type(const LocationType location_type) +void TestingCriteria::add_location_type(const LocationType location_type) { if (std::find(m_location_types.begin(), m_location_types.end(), location_type) == m_location_types.end()) { m_location_types.push_back(location_type); } } -void TestingRule::remove_location_type(const LocationType location_type) +void TestingCriteria::remove_location_type(const LocationType location_type) { auto last = std::remove(m_location_types.begin(), m_location_types.end(), location_type); m_location_types.erase(last, m_location_types.end()); } -void TestingRule::add_infection_state(const InfectionState infection_state) +void TestingCriteria::add_infection_state(const InfectionState infection_state) { if (std::find(m_infection_states.begin(), m_infection_states.end(), infection_state) == m_infection_states.end()) { m_infection_states.push_back(infection_state); } } -void TestingRule::remove_infection_state(const InfectionState infection_state) +void TestingCriteria::remove_infection_state(const InfectionState infection_state) { auto last = std::remove(m_infection_states.begin(), m_infection_states.end(), infection_state); m_infection_states.erase(last, m_infection_states.end()); } -bool TestingRule::evaluate(const Person& p, const Location& l) const +bool TestingCriteria::evaluate(const Person& p, const Location& l) const { return has_requested_age(p) && is_requested_location_type(l) && has_requested_infection_state(p); } -bool TestingRule::has_requested_age(const Person& p) const +bool TestingCriteria::has_requested_age(const Person& p) const { if (m_ages.empty()) { return true; // no condition on the age @@ -86,7 +86,7 @@ bool TestingRule::has_requested_age(const Person& p) const return std::find(m_ages.begin(), m_ages.end(), p.get_age()) != m_ages.end(); } -bool TestingRule::is_requested_location_type(const Location& l) const +bool TestingCriteria::is_requested_location_type(const Location& l) const { if (m_location_types.empty()) { return true; // no condition on the location @@ -94,7 +94,7 @@ bool TestingRule::is_requested_location_type(const Location& l) const return std::find(m_location_types.begin(), m_location_types.end(), l.get_type()) != m_location_types.end(); } -bool TestingRule::has_requested_infection_state(const Person& p) const +bool TestingCriteria::has_requested_infection_state(const Person& p) const { if (m_infection_states.empty()) { return true; // no condition on infection state @@ -103,11 +103,11 @@ bool TestingRule::has_requested_infection_state(const Person& p) const m_infection_states.end(); } -TestingScheme::TestingScheme(const std::vector testing_rules, const TimeSpan testing_frequency, - TimePoint start_date, TimePoint end_date, const double probability, - const GenericTest& test_type) - : m_testing_rules(testing_rules) - , m_testing_frequency(testing_frequency) +TestingScheme::TestingScheme(const std::vector& testing_criteria, + TimeSpan minimal_time_since_last_test, TimePoint start_date, TimePoint end_date, + double probability, const GenericTest& test_type) + : m_testing_criteria(testing_criteria) + , m_minimal_time_since_last_test(minimal_time_since_last_test) , m_probability(probability) , m_start_date(start_date) , m_end_date(end_date) @@ -115,17 +115,17 @@ TestingScheme::TestingScheme(const std::vector testing_rules, const { } -void TestingScheme::add_testing_rule(const TestingRule rule) +void TestingScheme::add_testing_criteria(const TestingCriteria criteria) { - if (std::find(m_testing_rules.begin(), m_testing_rules.end(), rule) == m_testing_rules.end()) { - m_testing_rules.push_back(rule); + if (std::find(m_testing_criteria.begin(), m_testing_criteria.end(), criteria) == m_testing_criteria.end()) { + m_testing_criteria.push_back(criteria); } } -void TestingScheme::remove_testing_rule(const TestingRule rule) +void TestingScheme::remove_testing_criteria(const TestingCriteria criteria) { - auto last = std::remove(m_testing_rules.begin(), m_testing_rules.end(), rule); - m_testing_rules.erase(last, m_testing_rules.end()); + auto last = std::remove(m_testing_criteria.begin(), m_testing_criteria.end(), criteria); + m_testing_criteria.erase(last, m_testing_criteria.end()); } bool TestingScheme::is_active() const @@ -139,12 +139,13 @@ void TestingScheme::update_activity_status(const TimePoint t) bool TestingScheme::run_scheme(Person& person, const Location& location) const { - if (person.get_time_since_negative_test() > m_testing_frequency) { + if (person.get_time_since_negative_test() > m_minimal_time_since_last_test) { double random = UniformDistribution::get_instance()(); if (random < m_probability) { - if (std::any_of(m_testing_rules.begin(), m_testing_rules.end(), [person, location](TestingRule tr) { - return tr.evaluate(person, location); - })) { + if (std::any_of(m_testing_criteria.begin(), m_testing_criteria.end(), + [person, location](TestingCriteria tr) { + return tr.evaluate(person, location); + })) { return !person.get_tested(m_test_type.get_default()); } } @@ -152,5 +153,40 @@ bool TestingScheme::run_scheme(Person& person, const Location& location) const return true; } +TestingStrategy::TestingStrategy(const std::vector& testing_schemes) + : m_testing_schemes(testing_schemes) +{ +} + +void TestingStrategy::add_testing_scheme(const TestingScheme scheme) +{ + if (std::find(m_testing_schemes.begin(), m_testing_schemes.end(), scheme) == m_testing_schemes.end()) { + m_testing_schemes.push_back(scheme); + } +} + +void TestingStrategy::remove_testing_scheme(const TestingScheme scheme) +{ + auto last = std::remove(m_testing_schemes.begin(), m_testing_schemes.end(), scheme); + m_testing_schemes.erase(last, m_testing_schemes.end()); +} + +bool TestingStrategy::run_strategy(Person& person, const Location& location) const +{ + return std::all_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location](TestingScheme ts) { + if (ts.is_active()) { + return ts.run_scheme(person, location); + } + return true; + }); +} + +void TestingStrategy::update_testing_scheme_activity_status(const TimePoint t) +{ + for (auto& ts : m_testing_schemes) { + ts.update_activity_status(t); + } +} + } // namespace abm } // namespace mio diff --git a/cpp/models/abm/testing_scheme.h b/cpp/models/abm/testing_strategy.h similarity index 58% rename from cpp/models/abm/testing_scheme.h rename to cpp/models/abm/testing_strategy.h index b66858d3c7..a6c20dfc5f 100644 --- a/cpp/models/abm/testing_scheme.h +++ b/cpp/models/abm/testing_strategy.h @@ -31,63 +31,58 @@ namespace abm { /** - * Testing Rule for Testing Scheme + * Testing Criteria for Testing Scheme */ -class TestingRule +class TestingCriteria { public: /** - * Create a testing rule. - * @param ages vector of age groups that are allowed for testing - * @param location_types vector of location types that are allowed for testing - * @param infection_states vector of infection states that are allowed for testing + * Create a testing criteria. + * @param ages vector of age groups that are either allowed or required to be tested + * @param location_types vector of location types that are either allowed or required to be tested + * @param infection_states vector of infection states that are either allowed or required to be tested * An empty vector of ages/location types/infection states means that no condition on the corresponding property is set! */ - TestingRule(const std::vector ages, const std::vector location_types, - const std::vector infection_states); + TestingCriteria(const std::vector& ages, const std::vector& location_types, + const std::vector& infection_states); /** - * Compares two testing rules for equality. - * Compare references. Still possible to clone rules. + * Compares two testing criteria for equality. + * Compare references. Still possible to clone criteria. */ - bool operator==(const TestingRule& other) const + bool operator==(const TestingCriteria& other) const { return ((this->m_ages == other.m_ages) && (this->m_infection_states == other.m_infection_states) && (this->m_location_types == other.m_location_types)); } /** - * add an age group to the set of age groups that are allowed for testing + * add an age group to the set of age groups that are either allowed or required to be tested * @param age_group age group to be added */ void add_age_group(const AgeGroup age_group); - /** - * remove an age group from the set of age groups that are allowed for testing + * remove an age group from the set of age groups that are either allowed or required to be tested * @param age_group age group to be removed */ void remove_age_group(const AgeGroup age_group); - /** - * add a location type to the set of location types that are allowed for testing + * add a location type to the set of location types that are either allowed or required to be tested * @param location_type location type to be added */ void add_location_type(const LocationType location_type); - /** - * remove a location tpye from the set of location tpyes that are allowed for testing + * remove a location tpye from the set of location tpyes that are either allowed or required to be tested * @param location_type location type to be removed */ void remove_location_type(const LocationType location_type); - /** - * add an infection state to the set of infection states that are allowed for testing + * add an infection state to the set of infection states that are either allowed or required to be tested * @param infection_state infection state to be added */ void add_infection_state(const InfectionState infection_state); - /** - * remove an infection state from the set of infection states that are allowed for testing + * remove an infection state from the set of infection states that are either allowed or required to be tested * @param infection_state infection state to be removed */ void remove_infection_state(const InfectionState infection_state); @@ -117,6 +112,7 @@ class TestingRule * @param p person to be checked */ bool has_requested_infection_state(const Person& p) const; + std::vector m_ages; std::vector m_location_types; std::vector m_infection_states; @@ -130,15 +126,15 @@ class TestingScheme public: /** * Create a testing scheme. - * @param testing_rules vector of testing rules that are checked for testing - * @param testing_frequency time length of how often this scheme applies, i. e., a new test is performed after a person's last test + * @param testing_criteria vector of testing criteria that are checked for testing + * @param minimal_time_since_last_test time length of how often this scheme applies, i. e., a new test is performed after a person's last test * @param start_date starting date of the scheme * @param end_date ending date of the scheme * @param probability probability of the test to be performed if a testing rule applies * @param test_type the type of test to be performed */ - TestingScheme(const std::vector testing_rules, const TimeSpan testing_frequency, TimePoint start_date, - TimePoint end_date, const double probability, const GenericTest& test_type); + TestingScheme(const std::vector& testing_criteria, TimeSpan minimal_time_since_last_test, + TimePoint start_date, TimePoint end_date, double probability, const GenericTest& test_type); /** * Compares two testing schemes for equality. @@ -146,9 +142,10 @@ class TestingScheme */ bool operator==(const TestingScheme& other) const { - return this->m_testing_rules == other.m_testing_rules && - this->m_testing_frequency == other.m_testing_frequency && this->m_start_date == other.m_start_date && - this->m_end_date == other.m_end_date && this->m_probability == other.m_probability && + return this->m_testing_criteria == other.m_testing_criteria && + this->m_minimal_time_since_last_test == other.m_minimal_time_since_last_test && + this->m_start_date == other.m_start_date && this->m_end_date == other.m_end_date && + this->m_probability == other.m_probability && this->m_test_type.get_default().sensitivity == other.m_test_type.get_default().sensitivity && this->m_test_type.get_default().specificity == other.m_test_type.get_default().specificity; //To be adjusted and also TestType should be static. @@ -158,13 +155,13 @@ class TestingScheme * add a testing rule to the set of age groups that are checked for testing * @param rule testing rule to be added */ - void add_testing_rule(const TestingRule rule); + void add_testing_criteria(const TestingCriteria criteria); /** * remove a testing rule from the set of age groups that are checked for testing * @param rule testing rule to be removed */ - void remove_testing_rule(const TestingRule rule); + void remove_testing_criteria(const TestingCriteria criteria); /** * @return activity status of the scheme @@ -184,8 +181,8 @@ class TestingScheme bool run_scheme(Person& person, const Location& location) const; private: - std::vector m_testing_rules; - TimeSpan m_testing_frequency; + std::vector m_testing_criteria; + TimeSpan m_minimal_time_since_last_test; double m_probability; TimePoint m_start_date; TimePoint m_end_date; @@ -193,6 +190,48 @@ class TestingScheme bool m_is_active = false; }; +class TestingStrategy +{ +public: + /** + * Create a testing strategy. + * @param testing_schemes vector of testing schemes that are checked for testing + */ + TestingStrategy(); + TestingStrategy(const std::vector& testing_schemes); + /** + * Compares two testing strategies for equality. + * Compare references. Still possible to clone strategies. + */ + bool operator==(const TestingStrategy& other) const + { + return this->m_testing_schemes == other.m_testing_schemes; + } + /** + * add a testing scheme to the set of schemes that are checked for testing + * @param scheme testing scheme to be added + */ + void add_testing_scheme(const TestingScheme scheme); + /** + * remove a testing scheme from the set of schemes that are checked for testing + * @param scheme testing scheme to be removed + */ + void remove_testing_scheme(const TestingScheme scheme); + /** + * checks if the given time point t is within the interval of start and end date of each testing scheme and then changes the activity status for each testing scheme accordingly + * @param t time point to check the activity status of each testing scheme + */ + void update_testing_scheme_activity_status(const TimePoint t); + /** + * run the testing strategy and tests a person if necessary + * @return if the person is allowed to enter the location + */ + bool run_strategy(Person& person, const Location& location) const; + +private: + std::vector m_testing_schemes = {}; +}; + } // namespace abm } // namespace mio diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 57dfe53f93..d36ca591ff 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -52,6 +52,7 @@ void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); interaction(t, dt); + m_testing_strategy.update_testing_scheme_activity_status(t); migration(t, dt); } @@ -101,8 +102,6 @@ void World::migration(TimePoint t, TimeSpan dt) std::make_pair(&go_to_quarantine, std::vector{LocationType::Home})}; } - update_testing_scheme_activity_status(t); - for (auto&& person : m_persons) { for (auto rule : rules) { //check if transition rule can be applied @@ -114,7 +113,7 @@ void World::migration(TimePoint t, TimeSpan dt) if (nonempty) { auto target_type = rule.first(*person, t, dt, m_migration_parameters); Location* target = find_location(target_type, *person); - if (run_testing_schemes(*person, *target)) { + if (m_testing_strategy.run_strategy(*person, *target)) { if (target != &get_location(*person)) { person->migrate_to(get_location(*person), *target); break; @@ -131,7 +130,7 @@ void World::migration(TimePoint t, TimeSpan dt) auto& person = m_persons[trip.person_id]; if (!person->is_in_quarantine() && person->get_location_id() == trip.migration_origin) { Location& target = get_individualized_location(trip.migration_destination); - if (run_testing_schemes(*person, target)) { + if (m_testing_strategy.run_strategy(*person, target)) { person->migrate_to(get_location(*person), target); } } @@ -149,31 +148,6 @@ void World::begin_step(TimePoint /*t*/, TimeSpan dt) } } -void World::add_testing_scheme(const TestingScheme& testing_scheme) -{ - if (std::find(m_testing_schemes.begin(), m_testing_schemes.end(), testing_scheme) == m_testing_schemes.end()) { - m_testing_schemes.push_back(testing_scheme); - } -} - -void World::update_testing_scheme_activity_status(const TimePoint t) -{ - for (auto& ts : m_testing_schemes) { - ts.update_activity_status(t); - } -} - -bool World::run_testing_schemes(Person& person, const Location& location) -{ - - return std::all_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location](TestingScheme ts) { - if (ts.is_active()) { - return ts.run_scheme(person, location); - } - return true; - }); -} - auto World::get_locations() const -> Range< std::pair>::const_iterator, std::vector>::const_iterator>> { @@ -240,16 +214,6 @@ const GlobalInfectionParameters& World::get_global_infection_parameters() const return m_infection_parameters; } -GlobalTestingParameters& World::get_global_testing_parameters() -{ - return m_testing_parameters; -} - -const GlobalTestingParameters& World::get_global_testing_parameters() const -{ - return m_testing_parameters; -} - TripList& World::get_trip_list() { return m_trip_list; @@ -270,5 +234,15 @@ bool World::use_migration_rules() const return m_use_migration_rules; } +TestingStrategy& World::get_testing_strategy() +{ + return m_testing_strategy; +} + +const TestingStrategy& World::get_testing_strategy() const +{ + return m_testing_strategy; +} + } // namespace abm } // namespace mio diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index c6b2152a61..59d5aeba17 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -26,7 +26,7 @@ #include "abm/person.h" #include "abm/lockdown_rules.h" #include "abm/trip_list.h" -#include "abm/testing_scheme.h" +#include "abm/testing_strategy.h" #include "memilio/utils/pointer_dereferencing_iterator.h" #include "memilio/utils/stl_util.h" @@ -58,7 +58,6 @@ class World : m_locations((uint32_t)LocationType::Count) , m_infection_parameters(params) , m_migration_parameters() - , m_testing_parameters() , m_trip_list() , m_use_migration_rules(true) { @@ -164,13 +163,6 @@ class World const GlobalInfectionParameters& get_global_infection_parameters() const; - /** - * get global testing parameters - */ - GlobalTestingParameters& get_global_testing_parameters(); - - const GlobalTestingParameters& get_global_testing_parameters() const; - /** * get migration data */ @@ -185,24 +177,12 @@ class World void use_migration_rules(bool param); bool use_migration_rules() const; - /** - * adds a testing scheme to the set of all testing schemes for the simulation - * @param testing_scheme testing scheme to be added - */ - void add_testing_scheme(const TestingScheme& testing_scheme); - - /** - * updates activity status of all testing scheme at a given time point - * @param t time point to be updated for - */ - void update_testing_scheme_activity_status(const TimePoint t); - - /** - * runs all testing schemes for a given person and a given location - * @param person person to be checked - * @param location location to be checked + /** + * get testing strategy */ - bool run_testing_schemes(Person& person, const Location& location); + TestingStrategy& get_testing_strategy(); + + const TestingStrategy& get_testing_strategy() const; private: void interaction(TimePoint t, TimeSpan dt); @@ -210,10 +190,9 @@ class World std::vector> m_persons; std::vector> m_locations; - std::vector m_testing_schemes; + TestingStrategy m_testing_strategy; GlobalInfectionParameters m_infection_parameters; MigrationParameters m_migration_parameters; - GlobalTestingParameters m_testing_parameters; TripList m_trip_list; bool m_use_migration_rules; }; diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index 6659851e0f..08eb976ec7 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -1421,33 +1421,34 @@ TEST(TestDiscreteDistribution, generate) } } -TEST(TestTestingRule, addremoveandevaluateTestRule) +TEST(TestTestingCriteria, addremoveandevaluateTestCriteria) { - auto world = mio::abm::World(); - auto home_id = world.add_location(mio::abm::LocationType::Home); - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto person = mio::abm::Person(home_id, mio::abm::InfectionState::Infected, mio::abm::AgeGroup::Age15to34, - world.get_global_infection_parameters()); - auto& home = world.get_individualized_location(home_id); - auto& work = world.get_individualized_location(work_id); - person.set_assigned_location(home); - person.set_assigned_location(work); - - auto testing_rule = mio::abm::TestingRule({}, {}, {}); - testing_rule.add_infection_state(mio::abm::InfectionState::Infected); - testing_rule.add_infection_state(mio::abm::InfectionState::Carrier); - testing_rule.add_location_type(mio::abm::LocationType::Home); - testing_rule.add_location_type(mio::abm::LocationType::Work); - - ASSERT_EQ(testing_rule.evaluate(person, work), true); - ASSERT_EQ(testing_rule.evaluate(person, home), true); - - testing_rule.remove_infection_state(mio::abm::InfectionState::Infected); - ASSERT_EQ(testing_rule.evaluate(person, home), false); - - testing_rule.add_infection_state(mio::abm::InfectionState::Infected); - testing_rule.remove_location_type(mio::abm::LocationType::Home); - ASSERT_EQ(testing_rule.evaluate(person, home), false); + auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); + auto person = mio::abm::Person(home, mio::abm::InfectionState::Infected, mio::abm::AgeGroup::Age15to34, {}); + + auto testing_criteria = mio::abm::TestingCriteria({}, {}, {}); + ASSERT_EQ(testing_criteria.evaluate(person, work), true); + testing_criteria.add_infection_state(mio::abm::InfectionState::Infected); + testing_criteria.add_infection_state(mio::abm::InfectionState::Carrier); + testing_criteria.add_location_type(mio::abm::LocationType::Home); + testing_criteria.add_location_type(mio::abm::LocationType::Work); + + ASSERT_EQ(testing_criteria.evaluate(person, work), true); + ASSERT_EQ(testing_criteria.evaluate(person, home), true); + + testing_criteria.add_age_group(mio::abm::AgeGroup::Age35to59); + ASSERT_EQ(testing_criteria.evaluate(person, home), + false); // now it isn't empty and get's evaluated against age group + testing_criteria.remove_age_group(mio::abm::AgeGroup::Age35to59); + ASSERT_EQ(testing_criteria.evaluate(person, home), true); + + testing_criteria.remove_infection_state(mio::abm::InfectionState::Infected); + ASSERT_EQ(testing_criteria.evaluate(person, home), false); + + testing_criteria.add_infection_state(mio::abm::InfectionState::Infected); + testing_criteria.remove_location_type(mio::abm::LocationType::Home); + ASSERT_EQ(testing_criteria.evaluate(person, home), false); } TEST(TestTestingScheme, init) @@ -1457,8 +1458,8 @@ TEST(TestTestingScheme, init) std::vector test_location_types1 = {mio::abm::LocationType::Home, mio::abm::LocationType::Work}; - auto testing_rule1 = mio::abm::TestingRule({}, test_location_types1, test_infection_states1); - std::vector testing_rules = {testing_rule1}; + auto testing_criteria1 = mio::abm::TestingCriteria({}, test_location_types1, test_infection_states1); + std::vector testing_criterias = {testing_criteria1}; const auto testing_frequency = mio::abm::days(1); const auto start_date = mio::abm::TimePoint(0); @@ -1467,7 +1468,7 @@ TEST(TestTestingScheme, init) const auto test_type = mio::abm::PCRTest(); auto testing_scheme = - mio::abm::TestingScheme(testing_rules, testing_frequency, start_date, end_date, probability, test_type); + mio::abm::TestingScheme(testing_criterias, testing_frequency, start_date, end_date, probability, test_type); ASSERT_EQ(testing_scheme.is_active(), false); testing_scheme.update_activity_status(mio::abm::TimePoint(10)); @@ -1483,8 +1484,8 @@ TEST(TestTestingScheme, runScheme) std::vector test_location_types1 = {mio::abm::LocationType::Home, mio::abm::LocationType::Work}; - auto testing_rule1 = mio::abm::TestingRule({}, test_location_types1, test_infection_states1); - std::vector testing_rules = {testing_rule1}; + auto testing_criteria1 = mio::abm::TestingCriteria({}, test_location_types1, test_infection_states1); + std::vector testing_criterias = {testing_criteria1}; const auto testing_frequency = mio::abm::days(1); const auto start_date = mio::abm::TimePoint(0); @@ -1493,15 +1494,15 @@ TEST(TestTestingScheme, runScheme) const auto test_type = mio::abm::PCRTest(); auto testing_scheme = - mio::abm::TestingScheme(testing_rules, testing_frequency, start_date, end_date, probability, test_type); + mio::abm::TestingScheme(testing_criterias, testing_frequency, start_date, end_date, probability, test_type); std::vector test_infection_states2 = {mio::abm::InfectionState::Recovered_Carrier}; std::vector test_location_types2 = {mio::abm::LocationType::Home}; - auto testing_rule2 = mio::abm::TestingRule({}, test_location_types2, test_infection_states2); - testing_scheme.add_testing_rule(testing_rule2); + auto testing_criteria2 = mio::abm::TestingCriteria({}, test_location_types2, test_infection_states2); + testing_scheme.add_testing_criteria(testing_criteria2); auto loc_home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto loc_work = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto loc_work = mio::abm::Location(mio::abm::LocationType::Work, 0); auto person1 = mio::abm::Person(loc_home, mio::abm::InfectionState::Carrier, mio::abm::AgeGroup::Age15to34, {}); auto person2 = mio::abm::Person(loc_home, mio::abm::InfectionState::Recovered_Carrier, mio::abm::AgeGroup::Age15to34, {}); @@ -1520,12 +1521,12 @@ TEST(TestTestingScheme, runScheme) ASSERT_EQ(person2.is_in_quarantine(), false); ASSERT_EQ(person2.get_time_since_negative_test(), mio::abm::days(0)); - testing_scheme.add_testing_rule(testing_rule1); - testing_scheme.remove_testing_rule(testing_rule1); + testing_scheme.add_testing_criteria(testing_criteria1); + testing_scheme.remove_testing_criteria(testing_criteria1); ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), true); } -TEST(TestWorldTestingRule, testAddingAndUpdatingAndRunningTestingSchemes) +TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) { auto world = mio::abm::World(); @@ -1538,11 +1539,11 @@ TEST(TestWorldTestingRule, testAddingAndUpdatingAndRunningTestingSchemes) person.set_assigned_location(home); person.set_assigned_location(work); - auto testing_rule = mio::abm::TestingRule({}, {}, {}); - testing_rule.add_infection_state(mio::abm::InfectionState::Infected); - testing_rule.add_infection_state(mio::abm::InfectionState::Carrier); - testing_rule.add_location_type(mio::abm::LocationType::Home); - testing_rule.add_location_type(mio::abm::LocationType::Work); + auto testing_criteria = mio::abm::TestingCriteria({}, {}, {}); + testing_criteria.add_infection_state(mio::abm::InfectionState::Infected); + testing_criteria.add_infection_state(mio::abm::InfectionState::Carrier); + testing_criteria.add_location_type(mio::abm::LocationType::Home); + testing_criteria.add_location_type(mio::abm::LocationType::Work); const auto testing_frequency = mio::abm::days(1); const auto start_date = mio::abm::TimePoint(20); @@ -1551,17 +1552,17 @@ TEST(TestWorldTestingRule, testAddingAndUpdatingAndRunningTestingSchemes) const auto test_type = mio::abm::PCRTest(); auto testing_scheme = - mio::abm::TestingScheme({testing_rule}, testing_frequency, start_date, end_date, probability, test_type); + mio::abm::TestingScheme({testing_criteria}, testing_frequency, start_date, end_date, probability, test_type); - world.add_testing_scheme(testing_scheme); + world.get_testing_strategy().add_testing_scheme(testing_scheme); auto current_time = mio::abm::TimePoint(0); - ASSERT_EQ(world.run_testing_schemes(person, work), true); + ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work), true); current_time = mio::abm::TimePoint(30); - world.update_testing_scheme_activity_status(current_time); + world.get_testing_strategy().update_testing_scheme_activity_status(current_time); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(2)) .WillOnce(testing::Return(0.7)) .WillOnce(testing::Return(0.4)); - ASSERT_EQ(world.run_testing_schemes(person, work), false); -} \ No newline at end of file + ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work), false); +} From c1668b26caf0d97242c0aa20d78f5a34a3faddc7 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Thu, 1 Sep 2022 13:04:33 +0200 Subject: [PATCH 18/27] some minor fixes --- cpp/models/abm/testing_strategy.cpp | 8 ++++++-- cpp/models/abm/testing_strategy.h | 12 ++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index f2be1cea87..dbd44bc590 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -158,14 +158,18 @@ TestingStrategy::TestingStrategy(const std::vector& testing_schem { } -void TestingStrategy::add_testing_scheme(const TestingScheme scheme) +TestingStrategy::TestingStrategy() +{ +} + +void TestingStrategy::add_testing_scheme(const TestingScheme& scheme) { if (std::find(m_testing_schemes.begin(), m_testing_schemes.end(), scheme) == m_testing_schemes.end()) { m_testing_schemes.push_back(scheme); } } -void TestingStrategy::remove_testing_scheme(const TestingScheme scheme) +void TestingStrategy::remove_testing_scheme(const TestingScheme& scheme) { auto last = std::remove(m_testing_schemes.begin(), m_testing_schemes.end(), scheme); m_testing_schemes.erase(last, m_testing_schemes.end()); diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index a6c20dfc5f..1a21dd3098 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -152,14 +152,14 @@ class TestingScheme } /** - * add a testing rule to the set of age groups that are checked for testing - * @param rule testing rule to be added + * add a testing criteria to the set of age groups that are checked for testing + * @param criteria testing criteria to be added */ void add_testing_criteria(const TestingCriteria criteria); /** - * remove a testing rule from the set of age groups that are checked for testing - * @param rule testing rule to be removed + * remove a testing criteria from the set of age groups that are checked for testing + * @param criteria testing criteria to be removed */ void remove_testing_criteria(const TestingCriteria criteria); @@ -211,12 +211,12 @@ class TestingStrategy * add a testing scheme to the set of schemes that are checked for testing * @param scheme testing scheme to be added */ - void add_testing_scheme(const TestingScheme scheme); + void add_testing_scheme(const TestingScheme& scheme); /** * remove a testing scheme from the set of schemes that are checked for testing * @param scheme testing scheme to be removed */ - void remove_testing_scheme(const TestingScheme scheme); + void remove_testing_scheme(const TestingScheme& scheme); /** * checks if the given time point t is within the interval of start and end date of each testing scheme and then changes the activity status for each testing scheme accordingly * @param t time point to check the activity status of each testing scheme From 38d05e794b01ccb7c694462e42f1e0b12a725a02 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Thu, 1 Sep 2022 14:03:10 +0200 Subject: [PATCH 19/27] ctor fix --- cpp/models/abm/testing_strategy.cpp | 4 ---- cpp/models/abm/testing_strategy.h | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index dbd44bc590..ad5666f8dd 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -158,10 +158,6 @@ TestingStrategy::TestingStrategy(const std::vector& testing_schem { } -TestingStrategy::TestingStrategy() -{ -} - void TestingStrategy::add_testing_scheme(const TestingScheme& scheme) { if (std::find(m_testing_schemes.begin(), m_testing_schemes.end(), scheme) == m_testing_schemes.end()) { diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 1a21dd3098..1acf27c289 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -197,8 +197,8 @@ class TestingStrategy * Create a testing strategy. * @param testing_schemes vector of testing schemes that are checked for testing */ - TestingStrategy(); - TestingStrategy(const std::vector& testing_schemes); + TestingStrategy() = default; + explicit TestingStrategy(const std::vector& testing_schemes); /** * Compares two testing strategies for equality. * Compare references. Still possible to clone strategies. @@ -229,7 +229,7 @@ class TestingStrategy bool run_strategy(Person& person, const Location& location) const; private: - std::vector m_testing_schemes = {}; + std::vector m_testing_schemes; }; } // namespace abm From bd17cf13b6171fe48bcab37869467ee29d25583e Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Thu, 1 Sep 2022 16:14:16 +0200 Subject: [PATCH 20/27] fix further things from the code review --- cpp/examples/abm.cpp | 1 - cpp/models/abm/parameters.h | 14 +------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index e7941d0b3d..c6840ff304 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -278,7 +278,6 @@ void create_assign_locations(mio::abm::World& world) // People have to get tested in the 2 days before the event auto event = world.add_location(mio::abm::LocationType::SocialEvent); world.get_individualized_location(event).get_infection_parameters().set(100); - //world.get_individualized_location(event).set_testing_scheme(mio::abm::days(2), 1); // Add hospital and ICU with 5 maximum contacs. auto hospital = world.add_location(mio::abm::LocationType::Hospital); diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index 148965a7c6..8f389da4c1 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -191,25 +191,13 @@ struct DetectInfection { } }; -struct TestWhileInfected { - using Type = CustomIndexArray; - static Type get_default() - { - return Type({AgeGroup::Count}, 0.005); - } - static std::string name() - { - return "TestWhileInfected"; - } -}; - /** * parameters of the infection that are the same everywhere within the world. */ using GlobalInfectionParameters = ParameterSet; + CriticalToDead, CriticalToRecovered, RecoveredToSusceptible, DetectInfection>; struct MaximumContacts { using Type = double; From 453ceac3c98a37661a764b134d35b104d9c77a32 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Fri, 2 Sep 2022 12:17:31 +0200 Subject: [PATCH 21/27] add final remarks of code review --- cpp/models/abm/parameters.h | 2 +- cpp/models/abm/person.cpp | 1 + cpp/models/abm/testing_strategy.h | 19 +++++-- cpp/tests/test_abm.cpp | 91 ++++++++++++++++++------------- 4 files changed, 70 insertions(+), 43 deletions(-) diff --git a/cpp/models/abm/parameters.h b/cpp/models/abm/parameters.h index 8f389da4c1..ff40443bee 100644 --- a/cpp/models/abm/parameters.h +++ b/cpp/models/abm/parameters.h @@ -237,7 +237,7 @@ struct AntigenTest : public GenericTest { using Type = TestParameters; static constexpr Type get_default() { - return Type{0.9, 0.99}; + return Type{0.8, 0.88}; } static std::string name() { diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 1e938b160f..1f87080aab 100644 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -177,6 +177,7 @@ bool Person::get_tested(const TestParameters& params) return false; } else { + m_quarantine = true; return true; } } diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 1acf27c289..80bdf99bd4 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -43,6 +43,7 @@ class TestingCriteria * @param infection_states vector of infection states that are either allowed or required to be tested * An empty vector of ages/location types/infection states means that no condition on the corresponding property is set! */ + TestingCriteria() = default; TestingCriteria(const std::vector& ages, const std::vector& location_types, const std::vector& infection_states); @@ -50,10 +51,21 @@ class TestingCriteria * Compares two testing criteria for equality. * Compare references. Still possible to clone criteria. */ - bool operator==(const TestingCriteria& other) const + bool operator==(TestingCriteria other) const { - return ((this->m_ages == other.m_ages) && (this->m_infection_states == other.m_infection_states) && - (this->m_location_types == other.m_location_types)); + auto to_compare_ages = this->m_ages; + auto to_compare_infection_states = this->m_infection_states; + auto to_compare_location_types = this->m_location_types; + + std::sort(to_compare_ages.begin(), to_compare_ages.end()); + std::sort(other.m_ages.begin(), other.m_ages.end()); + std::sort(to_compare_infection_states.begin(), to_compare_infection_states.end()); + std::sort(other.m_infection_states.begin(), other.m_infection_states.end()); + std::sort(to_compare_location_types.begin(), to_compare_location_types.end()); + std::sort(other.m_location_types.begin(), other.m_location_types.end()); + + return to_compare_ages == other.m_ages && to_compare_location_types == other.m_location_types && + to_compare_infection_states == other.m_infection_states; } /** @@ -138,7 +150,6 @@ class TestingScheme /** * Compares two testing schemes for equality. - * Compare references. Still possible to clone schemes. */ bool operator==(const TestingScheme& other) const { diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index 08eb976ec7..d1234046c1 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -551,17 +551,41 @@ TEST(TestPerson, get_tested) auto infected = mio::abm::Person(loc, mio::abm::InfectionState::Infected, mio::abm::AgeGroup::Age15to34, {}); auto susceptible = mio::abm::Person(loc, mio::abm::InfectionState::Susceptible, mio::abm::AgeGroup::Age15to34, {}); - ScopedMockDistribution>>> mock_uniform_dist; - EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) + auto pcr_test = mio::abm::PCRTest(); + auto antigen_test = mio::abm::AntigenTest(); + + // Test pcr test + ScopedMockDistribution>>> + mock_uniform_dist_pcr; + EXPECT_CALL(mock_uniform_dist_pcr.get_mock(), invoke) + .Times(4) + .WillOnce(Return(0.4)) + .WillOnce(Return(0.95)) + .WillOnce(Return(0.6)) + .WillOnce(Return(0.999)); + ASSERT_EQ(infected.get_tested(pcr_test.get_default()), true); + ASSERT_EQ(infected.is_in_quarantine(), true); + ASSERT_EQ(infected.get_tested(pcr_test.get_default()), false); + ASSERT_EQ(infected.is_in_quarantine(), false); + ASSERT_EQ(susceptible.get_tested(pcr_test.get_default()), false); + ASSERT_EQ(susceptible.is_in_quarantine(), false); + ASSERT_EQ(susceptible.get_tested(pcr_test.get_default()), true); + ASSERT_EQ(susceptible.is_in_quarantine(), true); + ASSERT_EQ(susceptible.get_time_since_negative_test(), mio::abm::days(0)); + + // Test antigen test + ScopedMockDistribution>>> + mock_uniform_dist_antigen; + EXPECT_CALL(mock_uniform_dist_antigen.get_mock(), invoke) .Times(4) .WillOnce(Return(0.4)) .WillOnce(Return(0.95)) .WillOnce(Return(0.6)) .WillOnce(Return(0.999)); - ASSERT_EQ(infected.get_tested({0.9, 0.99}), true); - ASSERT_EQ(infected.get_tested({0.9, 0.99}), false); - ASSERT_EQ(susceptible.get_tested({0.9, 0.99}), false); - ASSERT_EQ(susceptible.get_tested({0.9, 0.99}), true); + ASSERT_EQ(infected.get_tested(antigen_test.get_default()), true); + ASSERT_EQ(infected.get_tested(antigen_test.get_default()), false); + ASSERT_EQ(susceptible.get_tested(antigen_test.get_default()), false); + ASSERT_EQ(susceptible.get_tested(antigen_test.get_default()), true); ASSERT_EQ(susceptible.get_time_since_negative_test(), mio::abm::days(0)); } @@ -1427,7 +1451,7 @@ TEST(TestTestingCriteria, addremoveandevaluateTestCriteria) auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); auto person = mio::abm::Person(home, mio::abm::InfectionState::Infected, mio::abm::AgeGroup::Age15to34, {}); - auto testing_criteria = mio::abm::TestingCriteria({}, {}, {}); + auto testing_criteria = mio::abm::TestingCriteria(); ASSERT_EQ(testing_criteria.evaluate(person, work), true); testing_criteria.add_infection_state(mio::abm::InfectionState::Infected); testing_criteria.add_infection_state(mio::abm::InfectionState::Carrier); @@ -1449,9 +1473,16 @@ TEST(TestTestingCriteria, addremoveandevaluateTestCriteria) testing_criteria.add_infection_state(mio::abm::InfectionState::Infected); testing_criteria.remove_location_type(mio::abm::LocationType::Home); ASSERT_EQ(testing_criteria.evaluate(person, home), false); + + auto testing_criteria_manual = mio::abm::TestingCriteria( + {}, std::vector({mio::abm::LocationType::Work}), + std::vector({mio::abm::InfectionState::Carrier, mio::abm::InfectionState::Infected})); + ASSERT_EQ(testing_criteria == testing_criteria_manual, true); + testing_criteria_manual.remove_infection_state(mio::abm::InfectionState::Infected); + ASSERT_EQ(testing_criteria == testing_criteria_manual, false); } -TEST(TestTestingScheme, init) +TEST(TestTestingScheme, runScheme) { std::vector test_infection_states1 = {mio::abm::InfectionState::Infected, mio::abm::InfectionState::Carrier}; @@ -1464,7 +1495,7 @@ TEST(TestTestingScheme, init) const auto testing_frequency = mio::abm::days(1); const auto start_date = mio::abm::TimePoint(0); const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); - const auto probability = 1.0; + const auto probability = 0.8; const auto test_type = mio::abm::PCRTest(); auto testing_scheme = @@ -1475,26 +1506,7 @@ TEST(TestTestingScheme, init) ASSERT_EQ(testing_scheme.is_active(), true); testing_scheme.update_activity_status(mio::abm::TimePoint(60 * 60 * 24 * 3 + 200)); ASSERT_EQ(testing_scheme.is_active(), false); -} - -TEST(TestTestingScheme, runScheme) -{ - std::vector test_infection_states1 = {mio::abm::InfectionState::Infected, - mio::abm::InfectionState::Carrier}; - std::vector test_location_types1 = {mio::abm::LocationType::Home, - mio::abm::LocationType::Work}; - - auto testing_criteria1 = mio::abm::TestingCriteria({}, test_location_types1, test_infection_states1); - std::vector testing_criterias = {testing_criteria1}; - - const auto testing_frequency = mio::abm::days(1); - const auto start_date = mio::abm::TimePoint(0); - const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); - const auto probability = 1.0; - const auto test_type = mio::abm::PCRTest(); - - auto testing_scheme = - mio::abm::TestingScheme(testing_criterias, testing_frequency, start_date, end_date, probability, test_type); + testing_scheme.update_activity_status(mio::abm::TimePoint(0)); std::vector test_infection_states2 = {mio::abm::InfectionState::Recovered_Carrier}; std::vector test_location_types2 = {mio::abm::LocationType::Home}; @@ -1509,17 +1521,15 @@ TEST(TestTestingScheme, runScheme) ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) - .Times(testing::AtLeast(4)) + .Times(testing::Exactly(5)) .WillOnce(testing::Return(0.7)) .WillOnce(testing::Return(0.5)) .WillOnce(testing::Return(0.7)) - .WillOnce(testing::Return(0.5)); - ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), false); - ASSERT_EQ(testing_scheme.run_scheme(person2, loc_work), true); - - ASSERT_EQ(person1.is_in_quarantine(), true); - ASSERT_EQ(person2.is_in_quarantine(), false); - ASSERT_EQ(person2.get_time_since_negative_test(), mio::abm::days(0)); + .WillOnce(testing::Return(0.5)) + .WillOnce(testing::Return(0.9)); + ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), false); // Person tests and tests positive + ASSERT_EQ(testing_scheme.run_scheme(person2, loc_work), true); // Person tests and tests negative + ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), true); // Person doesn't test testing_scheme.add_testing_criteria(testing_criteria1); testing_scheme.remove_testing_criteria(testing_criteria1); @@ -1556,7 +1566,8 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) world.get_testing_strategy().add_testing_scheme(testing_scheme); auto current_time = mio::abm::TimePoint(0); - ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work), true); + ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work), + true); // no active testing scheme -> person can enter current_time = mio::abm::TimePoint(30); world.get_testing_strategy().update_testing_scheme_activity_status(current_time); ScopedMockDistribution>>> mock_uniform_dist; @@ -1565,4 +1576,8 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) .WillOnce(testing::Return(0.7)) .WillOnce(testing::Return(0.4)); ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work), false); + + world.get_testing_strategy().add_testing_scheme(testing_scheme); //doesn't get added because of == operator + world.get_testing_strategy().remove_testing_scheme(testing_scheme); + ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work), true); // no more testing_schemes } From dca3c26cb9f4764f03a594a877194932807c1769 Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Fri, 9 Sep 2022 12:07:17 +0200 Subject: [PATCH 22/27] Implement review requests --- cpp/examples/abm.cpp | 49 +++++++++++------ cpp/models/abm/person.cpp | 4 ++ cpp/models/abm/testing_strategy.cpp | 48 ++++++++++++---- cpp/models/abm/testing_strategy.h | 55 +++++-------------- cpp/models/abm/world.cpp | 2 +- cpp/models/abm/world.h | 2 +- cpp/tests/test_abm.cpp | 10 ++-- .../test_epidata_geoModificationGermany.py | 3 +- 8 files changed, 95 insertions(+), 78 deletions(-) diff --git a/cpp/examples/abm.cpp b/cpp/examples/abm.cpp index c6840ff304..4d20e7ae3a 100644 --- a/cpp/examples/abm.cpp +++ b/cpp/examples/abm.cpp @@ -279,29 +279,25 @@ void create_assign_locations(mio::abm::World& world) auto event = world.add_location(mio::abm::LocationType::SocialEvent); world.get_individualized_location(event).get_infection_parameters().set(100); + std::vector test_at_social_event = {mio::abm::LocationType::SocialEvent}; + auto testing_criteria = std::vector {mio::abm::TestingCriteria({}, test_at_social_event, {})}; + auto testing_min_time = mio::abm::days(2); + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(60); + auto probability = 1; + auto test_type = mio::abm::AntigenTest(); + + auto testing_scheme = + mio::abm::TestingScheme(testing_criteria, testing_min_time, start_date, end_date, test_type, probability); + + world.get_testing_strategy().add_testing_scheme(testing_scheme); + // Add hospital and ICU with 5 maximum contacs. auto hospital = world.add_location(mio::abm::LocationType::Hospital); world.get_individualized_location(hospital).get_infection_parameters().set(5); auto icu = world.add_location(mio::abm::LocationType::ICU); world.get_individualized_location(icu).get_infection_parameters().set(5); - // Add a testing scheme to world. - // Everybody who goes to the School, Work, or Basic shop tests themselves. - std::vector test_location_types = { - mio::abm::LocationType::School, mio::abm::LocationType::Work, mio::abm::LocationType::BasicsShop}; - - auto testing_criteria1 = mio::abm::TestingCriteria({}, test_location_types, {}); - std::vector testing_criteria = {testing_criteria1}; - - const auto testing_frequency = mio::abm::days(2); - const auto start_date = mio::abm::TimePoint(0); - const auto end_date = mio::abm::TimePoint(0) + mio::abm::days(60); - const auto probability = 0.8; - const auto test_type = mio::abm::PCRTest(); - - auto testing_scheme = - mio::abm::TestingScheme(testing_criteria, testing_frequency, start_date, end_date, probability, test_type); - world.get_testing_strategy().add_testing_scheme(testing_scheme); // Add schools, workplaces and shops. // At every school there are 600 students. The maximum contacs are 40. // Students have to get tested once a week. @@ -356,6 +352,25 @@ void create_assign_locations(mio::abm::World& world) world.get_individualized_location(shop).get_infection_parameters().set(20); } } + + // add the testing schemes for school and work + auto test_at_school = std::vector {mio::abm::LocationType::School}; + auto testing_criteria_school = std::vector {mio::abm::TestingCriteria({}, test_at_school, {})}; + + testing_min_time = mio::abm::days(7); + probability = 1; + auto testing_scheme_school = + mio::abm::TestingScheme(testing_criteria_school, testing_min_time, start_date, end_date, test_type, probability); + world.get_testing_strategy().add_testing_scheme(testing_scheme_school); + + auto test_at_work = std::vector {mio::abm::LocationType::Work}; + auto testing_criteria_work = std::vector {mio::abm::TestingCriteria({}, test_at_work, {})}; + + testing_min_time = mio::abm::days(1); + probability = 0.5; + auto testing_scheme_work = + mio::abm::TestingScheme(testing_criteria_work, testing_min_time, start_date, end_date, test_type, probability); + world.get_testing_strategy().add_testing_scheme(testing_scheme_work); } /** diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index b9db972d6b..209607fa9d 100644 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -161,10 +161,12 @@ bool Person::get_tested(const TestParameters& params) if (m_infection_state == InfectionState::Carrier || m_infection_state == InfectionState::Infected || m_infection_state == InfectionState::Infected_Severe || m_infection_state == InfectionState::Infected_Critical) { + // true positive if (random < params.sensitivity) { m_quarantine = true; return true; } + // false negative else { m_quarantine = false; m_time_since_negative_test = days(0); @@ -172,11 +174,13 @@ bool Person::get_tested(const TestParameters& params) } } else { + // true negative if (random < params.specificity) { m_quarantine = false; m_time_since_negative_test = days(0); return false; } + // false positive else { m_quarantine = true; return true; diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index ad5666f8dd..ede2198fde 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * & Helmholtz Centre for Infection Research (HZI) * -* Authors: Elisabeth Kluth, David Kerkmann, Sascha Korf +* Authors: Elisabeth Kluth, David Kerkmann, Sascha Korf, Martin J. Kuehn * * Contact: Martin J. Kuehn * @@ -35,6 +35,23 @@ TestingCriteria::TestingCriteria(const std::vector& ages, const std::v { } +bool TestingCriteria::operator==(TestingCriteria other) const +{ + auto to_compare_ages = this->m_ages; + auto to_compare_infection_states = this->m_infection_states; + auto to_compare_location_types = this->m_location_types; + + std::sort(to_compare_ages.begin(), to_compare_ages.end()); + std::sort(other.m_ages.begin(), other.m_ages.end()); + std::sort(to_compare_infection_states.begin(), to_compare_infection_states.end()); + std::sort(other.m_infection_states.begin(), other.m_infection_states.end()); + std::sort(to_compare_location_types.begin(), to_compare_location_types.end()); + std::sort(other.m_location_types.begin(), other.m_location_types.end()); + + return to_compare_ages == other.m_ages && to_compare_location_types == other.m_location_types && + to_compare_infection_states == other.m_infection_states; +} + void TestingCriteria::add_age_group(const AgeGroup age_group) { if (std::find(m_ages.begin(), m_ages.end(), age_group) == m_ages.end()) { @@ -105,16 +122,27 @@ bool TestingCriteria::has_requested_infection_state(const Person& p) const TestingScheme::TestingScheme(const std::vector& testing_criteria, TimeSpan minimal_time_since_last_test, TimePoint start_date, TimePoint end_date, - double probability, const GenericTest& test_type) + const GenericTest& test_type, double probability) : m_testing_criteria(testing_criteria) , m_minimal_time_since_last_test(minimal_time_since_last_test) - , m_probability(probability) , m_start_date(start_date) , m_end_date(end_date) , m_test_type(test_type) + , m_probability(probability) { } +bool TestingScheme::operator==(const TestingScheme& other) const +{ + return this->m_testing_criteria == other.m_testing_criteria && + this->m_minimal_time_since_last_test == other.m_minimal_time_since_last_test && + this->m_start_date == other.m_start_date && this->m_end_date == other.m_end_date && + this->m_test_type.get_default().sensitivity == other.m_test_type.get_default().sensitivity && + this->m_test_type.get_default().specificity == other.m_test_type.get_default().specificity && + this->m_probability == other.m_probability; + //To be adjusted and also TestType should be static. +} + void TestingScheme::add_testing_criteria(const TestingCriteria criteria) { if (std::find(m_testing_criteria.begin(), m_testing_criteria.end(), criteria) == m_testing_criteria.end()) { @@ -171,6 +199,13 @@ void TestingStrategy::remove_testing_scheme(const TestingScheme& scheme) m_testing_schemes.erase(last, m_testing_schemes.end()); } +void TestingStrategy::update_activity_status(const TimePoint t) +{ + for (auto& ts : m_testing_schemes) { + ts.update_activity_status(t); + } +} + bool TestingStrategy::run_strategy(Person& person, const Location& location) const { return std::all_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location](TestingScheme ts) { @@ -181,12 +216,5 @@ bool TestingStrategy::run_strategy(Person& person, const Location& location) con }); } -void TestingStrategy::update_testing_scheme_activity_status(const TimePoint t) -{ - for (auto& ts : m_testing_schemes) { - ts.update_activity_status(t); - } -} - } // namespace abm } // namespace mio diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 80bdf99bd4..60de206723 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -2,7 +2,7 @@ * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * & Helmholtz Centre for Infection Research (HZI) * -* Authors: Elisabeth Kluth, David Kerkmann, Sascha Korf +* Authors: Elisabeth Kluth, David Kerkmann, Sascha Korf, Martin J. Kuehn * * Contact: Martin J. Kuehn * @@ -48,25 +48,9 @@ class TestingCriteria const std::vector& infection_states); /** - * Compares two testing criteria for equality. - * Compare references. Still possible to clone criteria. + * Compares two testing criteria for functional equality. */ - bool operator==(TestingCriteria other) const - { - auto to_compare_ages = this->m_ages; - auto to_compare_infection_states = this->m_infection_states; - auto to_compare_location_types = this->m_location_types; - - std::sort(to_compare_ages.begin(), to_compare_ages.end()); - std::sort(other.m_ages.begin(), other.m_ages.end()); - std::sort(to_compare_infection_states.begin(), to_compare_infection_states.end()); - std::sort(other.m_infection_states.begin(), other.m_infection_states.end()); - std::sort(to_compare_location_types.begin(), to_compare_location_types.end()); - std::sort(other.m_location_types.begin(), other.m_location_types.end()); - - return to_compare_ages == other.m_ages && to_compare_location_types == other.m_location_types && - to_compare_infection_states == other.m_infection_states; - } + bool operator==(TestingCriteria other) const; /** * add an age group to the set of age groups that are either allowed or required to be tested @@ -146,22 +130,13 @@ class TestingScheme * @param test_type the type of test to be performed */ TestingScheme(const std::vector& testing_criteria, TimeSpan minimal_time_since_last_test, - TimePoint start_date, TimePoint end_date, double probability, const GenericTest& test_type); + TimePoint start_date, TimePoint end_date, const GenericTest& test_type, double probability); /** - * Compares two testing schemes for equality. + * Compares two testing schemes for functional equality. */ - bool operator==(const TestingScheme& other) const - { - return this->m_testing_criteria == other.m_testing_criteria && - this->m_minimal_time_since_last_test == other.m_minimal_time_since_last_test && - this->m_start_date == other.m_start_date && this->m_end_date == other.m_end_date && - this->m_probability == other.m_probability && - this->m_test_type.get_default().sensitivity == other.m_test_type.get_default().sensitivity && - this->m_test_type.get_default().specificity == other.m_test_type.get_default().specificity; - //To be adjusted and also TestType should be static. - } - + bool operator==(const TestingScheme& other) const; + /** * add a testing criteria to the set of age groups that are checked for testing * @param criteria testing criteria to be added @@ -194,10 +169,10 @@ class TestingScheme private: std::vector m_testing_criteria; TimeSpan m_minimal_time_since_last_test; - double m_probability; TimePoint m_start_date; TimePoint m_end_date; GenericTest m_test_type; + double m_probability; bool m_is_active = false; }; @@ -210,29 +185,25 @@ class TestingStrategy */ TestingStrategy() = default; explicit TestingStrategy(const std::vector& testing_schemes); - /** - * Compares two testing strategies for equality. - * Compare references. Still possible to clone strategies. - */ - bool operator==(const TestingStrategy& other) const - { - return this->m_testing_schemes == other.m_testing_schemes; - } + /** * add a testing scheme to the set of schemes that are checked for testing * @param scheme testing scheme to be added */ void add_testing_scheme(const TestingScheme& scheme); + /** * remove a testing scheme from the set of schemes that are checked for testing * @param scheme testing scheme to be removed */ void remove_testing_scheme(const TestingScheme& scheme); + /** * checks if the given time point t is within the interval of start and end date of each testing scheme and then changes the activity status for each testing scheme accordingly * @param t time point to check the activity status of each testing scheme */ - void update_testing_scheme_activity_status(const TimePoint t); + void update_activity_status(const TimePoint t); + /** * run the testing strategy and tests a person if necessary * @return if the person is allowed to enter the location diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index d36ca591ff..94fc3b6eba 100644 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -52,7 +52,7 @@ void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); interaction(t, dt); - m_testing_strategy.update_testing_scheme_activity_status(t); + m_testing_strategy.update_activity_status(t); migration(t, dt); } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 59d5aeba17..ff94aaabb4 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -2,7 +2,7 @@ * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * & Helmholtz Centre for Infection Research (HZI) * -* Authors: Daniel Abele, Majid Abedi, Elisabeth Kluth +* Authors: Daniel Abele, Majid Abedi, Elisabeth Kluth, David Kerkmann, Sascha Korf, Martin J. Kuehn * * Contact: Martin J. Kuehn * diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index d1234046c1..a44a8b89af 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2020-2021 German Aerospace Center (DLR-SC) * -* Authors: Daniel Abele, Elisabeth Kluth +* Authors: Daniel Abele, Elisabeth Kluth, David Kerkmann, Sascha Korf, Martin J. Kuehn * * Contact: Martin J. Kuehn * @@ -1492,14 +1492,14 @@ TEST(TestTestingScheme, runScheme) auto testing_criteria1 = mio::abm::TestingCriteria({}, test_location_types1, test_infection_states1); std::vector testing_criterias = {testing_criteria1}; - const auto testing_frequency = mio::abm::days(1); + const auto testing_min_time = mio::abm::days(1); const auto start_date = mio::abm::TimePoint(0); const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); const auto probability = 0.8; const auto test_type = mio::abm::PCRTest(); auto testing_scheme = - mio::abm::TestingScheme(testing_criterias, testing_frequency, start_date, end_date, probability, test_type); + mio::abm::TestingScheme(testing_criterias, testing_min_time, start_date, end_date, test_type, probability); ASSERT_EQ(testing_scheme.is_active(), false); testing_scheme.update_activity_status(mio::abm::TimePoint(10)); @@ -1562,14 +1562,14 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) const auto test_type = mio::abm::PCRTest(); auto testing_scheme = - mio::abm::TestingScheme({testing_criteria}, testing_frequency, start_date, end_date, probability, test_type); + mio::abm::TestingScheme({testing_criteria}, testing_frequency, start_date, end_date, test_type, probability); world.get_testing_strategy().add_testing_scheme(testing_scheme); auto current_time = mio::abm::TimePoint(0); ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work), true); // no active testing scheme -> person can enter current_time = mio::abm::TimePoint(30); - world.get_testing_strategy().update_testing_scheme_activity_status(current_time); + world.get_testing_strategy().update_activity_status(current_time); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(2)) diff --git a/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py b/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py index e0d02b0a43..283e2623f2 100644 --- a/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py +++ b/pycode/memilio-epidata/memilio/epidata_test/test_epidata_geoModificationGermany.py @@ -390,7 +390,6 @@ def test_get_official_county_table(self): if(name not in county_table.columns.tolist()): self.assertFalse("headers have changed.") - @unittest.skip("Link doesn't work for CI. Needs to be fixed.") @patch('builtins.print') def test_get_nuts3_county_id_map(self, mock_print): # merge_berlin = True, merge_eisenach = False @@ -548,7 +547,7 @@ def test_merge_df_counties(self): pd.testing.assert_frame_equal(result_df, self.eisenach_merged_df) # the test dataframe should be unchanged as it is the input of the function pd.testing.assert_frame_equal( - test_df, pd.DataFrame(self.eisenach_unmerged_data), check_dtype = False) + test_df, pd.DataFrame(self.eisenach_unmerged_data)) if __name__ == '__main__': From a7e435a5dfc2922ee408f8abd9da8eb734c5d0b2 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Mon, 19 Sep 2022 11:53:07 +0200 Subject: [PATCH 23/27] add logic of perosn who tests pos but is not home --- cpp/models/abm/testing_strategy.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index ede2198fde..7de71c81f7 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -208,6 +208,10 @@ void TestingStrategy::update_activity_status(const TimePoint t) bool TestingStrategy::run_strategy(Person& person, const Location& location) const { + // Person who is in quarantine but not yet home should go home. Otherwise they can't because they test positive. + if (location.get_type() == mio::abm::LocationType::Home && person.is_in_quarantine()) { + return true; + } return std::all_of(m_testing_schemes.begin(), m_testing_schemes.end(), [&person, location](TestingScheme ts) { if (ts.is_active()) { return ts.run_scheme(person, location); From cc03f119a96f4e2b3057d494705863b5a81595a0 Mon Sep 17 00:00:00 2001 From: Sascha Korf Date: Mon, 19 Sep 2022 12:00:48 +0200 Subject: [PATCH 24/27] update test --- cpp/tests/test_abm.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cpp/tests/test_abm.cpp b/cpp/tests/test_abm.cpp index a44a8b89af..c604a416af 100644 --- a/cpp/tests/test_abm.cpp +++ b/cpp/tests/test_abm.cpp @@ -1493,10 +1493,10 @@ TEST(TestTestingScheme, runScheme) std::vector testing_criterias = {testing_criteria1}; const auto testing_min_time = mio::abm::days(1); - const auto start_date = mio::abm::TimePoint(0); - const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); - const auto probability = 0.8; - const auto test_type = mio::abm::PCRTest(); + const auto start_date = mio::abm::TimePoint(0); + const auto end_date = mio::abm::TimePoint(60 * 60 * 24 * 3); + const auto probability = 0.8; + const auto test_type = mio::abm::PCRTest(); auto testing_scheme = mio::abm::TestingScheme(testing_criterias, testing_min_time, start_date, end_date, test_type, probability); @@ -1529,7 +1529,9 @@ TEST(TestTestingScheme, runScheme) .WillOnce(testing::Return(0.9)); ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), false); // Person tests and tests positive ASSERT_EQ(testing_scheme.run_scheme(person2, loc_work), true); // Person tests and tests negative - ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), true); // Person doesn't test + ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home), + true); // Person is in quarantine and wants to go home -> can do so + ASSERT_EQ(testing_scheme.run_scheme(person1, loc_work), true); // Person doesn't test testing_scheme.add_testing_criteria(testing_criteria1); testing_scheme.remove_testing_criteria(testing_criteria1); From d8a9fdc9af4e1f3161d81573bf317084f4985521 Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Wed, 21 Sep 2022 15:31:06 +0200 Subject: [PATCH 25/27] Started with python bindings --- .../memilio/simulation/abm.cpp | 39 ++++++++++--------- .../memilio/simulation_test/test_abm.py | 2 + 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 6b758d15d4..73d7ae854a 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -78,7 +78,7 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_CustomIndexArray( m, "_AgeVaccinationParameterArray"); pymio::bind_ParameterSet(m, "GlobalInfectionParameters").def(py::init<>()); - pymio::bind_ParameterSet(m, "GlobalTestingParameters").def(py::init<>()); + //pymio::bind_ParameterSet(m, "GlobalTestingParameters").def(py::init<>()); pymio::bind_ParameterSet(m, "LocalInfectionParameters").def(py::init<>()); pymio::bind_ParameterSet(m, "MigrationParameters").def(py::init<>()); @@ -150,11 +150,14 @@ PYBIND11_MODULE(_simulation_abm, m) .def_property_readonly("age", &mio::abm::Person::get_age) .def_property_readonly("is_in_quarantine", &mio::abm::Person::is_in_quarantine); - py::class_(m, "TestingScheme") - .def(py::init(), py::arg("interval"), py::arg("probability")) - .def_property("interval", &mio::abm::TestingScheme::get_interval, &mio::abm::TestingScheme::set_interval) - .def_property("probability", &mio::abm::TestingScheme::get_probability, - &mio::abm::TestingScheme::set_probability); + py::class_(m, "TestingCriteria") + .def(py::init&, const std::vector&, const std::vector&>(), py::arg("age_groups"), py::arg("location_types"), py::arg("infection_states")); + + //py::class_(m, "TestingScheme") + // .def(py::init(), py::arg("interval"), py::arg("probability")) + // .def_property("interval", &mio::abm::TestingScheme::get_interval, &mio::abm::TestingScheme::set_interval) + // .def_property("probability", &mio::abm::TestingScheme::get_probability, + // &mio::abm::TestingScheme::set_probability); py::class_(m, "Location") .def_property_readonly("type", &mio::abm::Location::get_type) @@ -163,11 +166,11 @@ PYBIND11_MODULE(_simulation_abm, m) py::overload_cast<>(&mio::abm::Location::get_infection_parameters, py::const_), [](mio::abm::Location& self, mio::abm::LocalInfectionParameters params) { self.get_infection_parameters() = params; - }) - .def_property("testing_scheme", &mio::abm::Location::get_testing_scheme, - [](mio::abm::Location& self, mio::abm::TestingScheme scheme) { - self.set_testing_scheme(scheme.get_interval(), scheme.get_probability()); - }); + }); + //.def_property("testing_scheme", &mio::abm::Location::get_testing_scheme, + // [](mio::abm::Location& self, mio::abm::TestingScheme scheme) { + // self.set_testing_scheme(scheme.get_interval(), scheme.get_probability()); + // }); pymio::bind_Range().get_locations())>(m, "_WorldLocationsRange"); pymio::bind_Range().get_persons())>(m, "_WorldPersonsRange"); @@ -217,13 +220,13 @@ PYBIND11_MODULE(_simulation_abm, m) [](mio::abm::World& self, mio::abm::MigrationParameters params) { self.get_migration_parameters() = params; }, - py::return_value_policy::reference_internal) - .def_property( - "testing_parameters", py::overload_cast<>(&mio::abm::World::get_global_testing_parameters, py::const_), - [](mio::abm::World& self, mio::abm::GlobalTestingParameters params) { - self.get_global_testing_parameters() = params; - }, - py::return_value_policy::reference_internal); + py::return_value_policy::reference_internal); + //.def_property( + // "testing_parameters", py::overload_cast<>(&mio::abm::World::get_global_testing_parameters, py::const_), + // [](mio::abm::World& self, mio::abm::GlobalTestingParameters params) { + // self.get_global_testing_parameters() = params; + // }, + // py::return_value_policy::reference_internal); py::class_(m, "Simulation") .def(py::init()) diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index aeb6c9a940..909ed60ebf 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -47,6 +47,8 @@ def test_locations(self): home.infection_parameters.MaximumContacts = 10 self.assertEqual(home.infection_parameters.MaximumContacts, 10) + #testing_ages = [abm.AgeGroup("Age0to4")] + #testing_crit = abm.TestingCriteria( home.testing_scheme = abm.TestingScheme(abm.days(1), 1.0) self.assertEqual(home.testing_scheme.interval, abm.days(1)) From ea1be1291044dc69c72f021a91e732587a758a14 Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Wed, 21 Sep 2022 17:25:34 +0200 Subject: [PATCH 26/27] Update pybindings --- .../memilio/simulation/abm.cpp | 39 ++++++++++--------- .../memilio/simulation_test/test_abm.py | 21 +++++----- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 73d7ae854a..3d7f1de4bd 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -78,7 +78,6 @@ PYBIND11_MODULE(_simulation_abm, m) pymio::bind_CustomIndexArray( m, "_AgeVaccinationParameterArray"); pymio::bind_ParameterSet(m, "GlobalInfectionParameters").def(py::init<>()); - //pymio::bind_ParameterSet(m, "GlobalTestingParameters").def(py::init<>()); pymio::bind_ParameterSet(m, "LocalInfectionParameters").def(py::init<>()); pymio::bind_ParameterSet(m, "MigrationParameters").def(py::init<>()); @@ -153,12 +152,20 @@ PYBIND11_MODULE(_simulation_abm, m) py::class_(m, "TestingCriteria") .def(py::init&, const std::vector&, const std::vector&>(), py::arg("age_groups"), py::arg("location_types"), py::arg("infection_states")); - //py::class_(m, "TestingScheme") - // .def(py::init(), py::arg("interval"), py::arg("probability")) - // .def_property("interval", &mio::abm::TestingScheme::get_interval, &mio::abm::TestingScheme::set_interval) - // .def_property("probability", &mio::abm::TestingScheme::get_probability, - // &mio::abm::TestingScheme::set_probability); - + py::class_(m, "GenericTest") + .def(py::init<>()); + py::class_(m, "AntigenTest") + .def(py::init<>()); + py::class_(m, "PCRTest") + .def(py::init<>()); + + py::class_(m, "TestingScheme") + .def(py::init&, mio::abm::TimeSpan, mio::abm::TimePoint, mio::abm::TimePoint, mio::abm::GenericTest, double>(), py::arg("testing_criteria"), py::arg("testing_min_time_since_last_test"), py::arg("start_date"), py::arg("end_date"), py::arg("test_type"), py::arg("probability")) + .def_property_readonly("active", &mio::abm::TestingScheme::is_active); + + py::class_(m, "TestingStrategy") + .def(py::init&>()); + py::class_(m, "Location") .def_property_readonly("type", &mio::abm::Location::get_type) .def_property_readonly("index", &mio::abm::Location::get_index) @@ -167,10 +174,6 @@ PYBIND11_MODULE(_simulation_abm, m) [](mio::abm::Location& self, mio::abm::LocalInfectionParameters params) { self.get_infection_parameters() = params; }); - //.def_property("testing_scheme", &mio::abm::Location::get_testing_scheme, - // [](mio::abm::Location& self, mio::abm::TestingScheme scheme) { - // self.set_testing_scheme(scheme.get_interval(), scheme.get_probability()); - // }); pymio::bind_Range().get_locations())>(m, "_WorldLocationsRange"); pymio::bind_Range().get_persons())>(m, "_WorldPersonsRange"); @@ -220,13 +223,13 @@ PYBIND11_MODULE(_simulation_abm, m) [](mio::abm::World& self, mio::abm::MigrationParameters params) { self.get_migration_parameters() = params; }, - py::return_value_policy::reference_internal); - //.def_property( - // "testing_parameters", py::overload_cast<>(&mio::abm::World::get_global_testing_parameters, py::const_), - // [](mio::abm::World& self, mio::abm::GlobalTestingParameters params) { - // self.get_global_testing_parameters() = params; - // }, - // py::return_value_policy::reference_internal); + py::return_value_policy::reference_internal) + .def_property( + "testing_strategy", py::overload_cast<>(&mio::abm::World::get_testing_strategy, py::const_), + [](mio::abm::World& self, mio::abm::TestingStrategy strategy) { + self.get_testing_strategy() = strategy; + }, + py::return_value_policy::reference_internal); py::class_(m, "Simulation") .def(py::init()) diff --git a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py index 909ed60ebf..179d003566 100644 --- a/pycode/memilio-simulation/memilio/simulation_test/test_abm.py +++ b/pycode/memilio-simulation/memilio/simulation_test/test_abm.py @@ -47,10 +47,12 @@ def test_locations(self): home.infection_parameters.MaximumContacts = 10 self.assertEqual(home.infection_parameters.MaximumContacts, 10) - #testing_ages = [abm.AgeGroup("Age0to4")] - #testing_crit = abm.TestingCriteria( - home.testing_scheme = abm.TestingScheme(abm.days(1), 1.0) - self.assertEqual(home.testing_scheme.interval, abm.days(1)) + testing_ages = [abm.AgeGroup.Age0to4] + testing_locations = [abm.LocationType.Home] + testing_inf_states = [] + testing_crit = [abm.TestingCriteria(testing_ages, testing_locations, testing_inf_states)] + testing_scheme = abm.TestingScheme(testing_crit, abm.days(1), t0, t0 + abm.days(1), abm.AntigenTest(), 1.0) + self.assertEqual(testing_scheme.active, False) # initially false, will only active once simulation starts def test_persons(self): t0 = abm.TimePoint(0) @@ -91,9 +93,10 @@ def test_simulation(self): p2.set_assigned_location(abm.LocationId(0, type)) #parameters so that the infected person doesn't randomly change state and gets tested reliably + # DUE TO THE CURRENT IMPLEMENTATION OF DIFFERENT TEST TYPES, THIS IS NOT POSSIBLE, NEEDS TO BE CHANGED IN THE FUTURE social_event = world.locations[social_event_id.type][social_event_id.index] - social_event.testing_scheme = abm.TestingScheme(abm.days(1), 1.0) - world.testing_parameters.AntigenTest = abm.TestParameters(1, 1) + #social_event.testing_scheme = abm.TestingScheme(abm.days(1), 1.0) + #world.testing_parameters.AntigenTest = abm.TestParameters(1, 1) world.infection_parameters.InfectedToSevere[abm.AgeGroup.Age0to4, abm.VaccinationState.Unvaccinated] = 0.0 world.infection_parameters.InfectedToRecovered[abm.AgeGroup.Age0to4, @@ -113,9 +116,9 @@ def test_simulation(self): self.assertEqual(sim.result.get_num_time_points(), 25) #check effect of trips - self.assertEqual(p1.location_id, home_id) #person 1 is tested when goging to social event - self.assertEqual(p1.is_in_quarantine, True) - self.assertEqual(p2.location_id, work_id) #person 2 goes to work + #self.assertEqual(p1.location_id, home_id) #person 1 is tested when goging to social event + #self.assertEqual(p1.is_in_quarantine, True) + #self.assertEqual(p2.location_id, work_id) #person 2 goes to work if __name__ == '__main__': From c01abc00753563b11a5eff2c0d70a627d485821b Mon Sep 17 00:00:00 2001 From: DavidKerkmann Date: Wed, 28 Sep 2022 10:35:47 +0200 Subject: [PATCH 27/27] Fixes const& in pybind --- pycode/memilio-simulation/memilio/simulation/abm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index 3d7f1de4bd..bac7d7f0cf 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -160,7 +160,7 @@ PYBIND11_MODULE(_simulation_abm, m) .def(py::init<>()); py::class_(m, "TestingScheme") - .def(py::init&, mio::abm::TimeSpan, mio::abm::TimePoint, mio::abm::TimePoint, mio::abm::GenericTest, double>(), py::arg("testing_criteria"), py::arg("testing_min_time_since_last_test"), py::arg("start_date"), py::arg("end_date"), py::arg("test_type"), py::arg("probability")) + .def(py::init&, mio::abm::TimeSpan, mio::abm::TimePoint, mio::abm::TimePoint, const mio::abm::GenericTest&, double>(), py::arg("testing_criteria"), py::arg("testing_min_time_since_last_test"), py::arg("start_date"), py::arg("end_date"), py::arg("test_type"), py::arg("probability")) .def_property_readonly("active", &mio::abm::TestingScheme::is_active); py::class_(m, "TestingStrategy") @@ -223,7 +223,7 @@ PYBIND11_MODULE(_simulation_abm, m) [](mio::abm::World& self, mio::abm::MigrationParameters params) { self.get_migration_parameters() = params; }, - py::return_value_policy::reference_internal) + py::return_value_policy::reference_internal) .def_property( "testing_strategy", py::overload_cast<>(&mio::abm::World::get_testing_strategy, py::const_), [](mio::abm::World& self, mio::abm::TestingStrategy strategy) {