@@ -43,26 +43,6 @@ bool TestingCriteria::operator==(const TestingCriteria& other) const
4343 return m_ages == other.m_ages && m_infection_states == other.m_infection_states ;
4444}
4545
46- void TestingCriteria::add_age_group (const AgeGroup age_group)
47- {
48- m_ages.set (static_cast <size_t >(age_group), true );
49- }
50-
51- void TestingCriteria::remove_age_group (const AgeGroup age_group)
52- {
53- m_ages.set (static_cast <size_t >(age_group), false );
54- }
55-
56- void TestingCriteria::add_infection_state (const InfectionState infection_state)
57- {
58- m_infection_states.set (static_cast <size_t >(infection_state), true );
59- }
60-
61- void TestingCriteria::remove_infection_state (const InfectionState infection_state)
62- {
63- m_infection_states.set (static_cast <size_t >(infection_state), false );
64- }
65-
6646bool TestingCriteria::evaluate (const Person& p, TimePoint t) const
6747{
6848 // An empty vector of ages or none bitset of #InfectionStates% means that no condition on the corresponding property is set.
@@ -79,6 +59,7 @@ TestingScheme::TestingScheme(const TestingCriteria& testing_criteria, TimeSpan v
7959 , m_test_parameters(test_parameters)
8060 , m_probability(probability)
8161{
62+ assert (start_date <= end_date && " Start date must be before or equal to end date" );
8263}
8364
8465bool TestingScheme::operator ==(const TestingScheme& other) const
@@ -91,122 +72,104 @@ bool TestingScheme::operator==(const TestingScheme& other) const
9172 // To be adjusted and also TestType should be static.
9273}
9374
94- bool TestingScheme::is_active () const
75+ bool TestingScheme::is_active (TimePoint t ) const
9576{
96- return m_is_active ;
77+ return (m_start_date <= t && t < m_end_date) ;
9778}
9879
99- void TestingScheme::update_activity_status ( TimePoint t)
80+ bool TestingScheme::run_and_test (PersonalRandomNumberGenerator& rng, Person& person, TimePoint t) const
10081{
101- m_is_active = (m_start_date <= t && t <= m_end_date);
102- }
82+ if (!is_active (t)) { // If the scheme is not active, do nothing; early return
83+ return false ;
84+ }
10385
104- bool TestingScheme::run_scheme (PersonalRandomNumberGenerator& rng, Person& person, TimePoint t) const
105- {
10686 auto test_result = person.get_test_result (m_test_parameters.type );
10787 // If the agent has a test result valid until now, use the result directly
10888 if ((test_result.time_of_testing > TimePoint (std::numeric_limits<int >::min ())) &&
10989 (test_result.time_of_testing + m_validity_period >= t)) {
110- return !test_result.result ;
90+ return test_result.result ; // If the test is positive, the entry is not allowed, and vice versa
91+ }
92+ if (person.get_compliance (InterventionType::Testing) <
93+ 1.0 && // Dont need to draw a random number if the person is compliant either way
94+ !person.is_compliant (
95+ rng, InterventionType::Testing)) { // If the person is not compliant with the testing intervention
96+ return true ;
11197 }
11298 // Otherwise, the time_of_testing in the past (i.e. the agent has already performed it).
11399 if (m_testing_criteria.evaluate (person, t - m_test_parameters.required_time )) {
114100 double random = UniformDistribution<double >::get_instance ()(rng);
115101 if (random < m_probability) {
116102 bool result = person.get_tested (rng, t - m_test_parameters.required_time , m_test_parameters);
117103 person.add_test_result (t, m_test_parameters.type , result);
118- return ! result;
104+ return result; // If the test is positive, the entry is not allowed, and vice versa
119105 }
120106 }
121- return true ;
107+ // If the test is not performed, the entry is allowed
108+ return false ;
122109}
123110
124- TestingStrategy::TestingStrategy (const std::vector<LocalStrategy>& location_to_schemes_map)
125- : m_location_to_schemes_map(location_to_schemes_map.begin(), location_to_schemes_map.end())
111+ TestingStrategy::TestingStrategy (const std::vector<LocalStrategy>& location_to_schemes_id,
112+ const std::vector<LocalStrategy>& location_to_schemes_type)
113+ : m_testing_schemes_at_location_id(location_to_schemes_id.begin(), location_to_schemes_id.end())
114+ , m_testing_schemes_at_location_type(location_to_schemes_type.begin(), location_to_schemes_type.end())
126115{
127116}
128117
129- void TestingStrategy::add_testing_scheme (const LocationType& loc_type, const LocationId& loc_id,
130- const TestingScheme& scheme)
118+ void TestingStrategy::add_scheme (const LocationId& loc_id, const TestingScheme& scheme)
131119{
132- auto iter_schemes =
133- std::find_if (m_location_to_schemes_map.begin (), m_location_to_schemes_map.end (), [&](const auto & p) {
134- return p.type == loc_type && p.id == loc_id;
135- });
136- if (iter_schemes == m_location_to_schemes_map.end ()) {
137- // no schemes for this location yet, add a new list with one scheme
138- m_location_to_schemes_map.push_back ({loc_type, loc_id, std::vector<TestingScheme>(1 , scheme)});
139- }
140- else {
141- // add scheme to existing vector if the scheme doesn't exist yet
142- auto & schemes = iter_schemes->schemes ;
143- if (std::find (schemes.begin (), schemes.end (), scheme) == schemes.end ()) {
144- schemes.push_back (scheme);
145- }
120+ if (loc_id.get () >= m_testing_schemes_at_location_id.size ()) {
121+ m_testing_schemes_at_location_id.resize (loc_id.get () + 1 );
146122 }
123+ m_testing_schemes_at_location_id[loc_id.get ()].schemes .push_back (scheme);
147124}
148125
149- void TestingStrategy::remove_testing_scheme (const LocationType& loc_type, const LocationId& loc_id,
150- const TestingScheme& scheme)
126+ void TestingStrategy::add_scheme (const LocationType& loc_type, const TestingScheme& scheme)
151127{
152- auto iter_schemes =
153- std::find_if (m_location_to_schemes_map.begin (), m_location_to_schemes_map.end (), [&](const auto & p) {
154- return p.type == loc_type && p.id == loc_id;
155- });
156- if (iter_schemes != m_location_to_schemes_map.end ()) {
157- // remove the scheme from the list
158- auto & schemes_vector = iter_schemes->schemes ;
159- auto last = std::remove (schemes_vector.begin (), schemes_vector.end (), scheme);
160- schemes_vector.erase (last, schemes_vector.end ());
161- // delete the list of schemes for this location if no schemes left
162- if (schemes_vector.empty ()) {
163- m_location_to_schemes_map.erase (iter_schemes);
164- }
128+ if ((size_t )loc_type >= m_testing_schemes_at_location_type.size ()) {
129+ m_testing_schemes_at_location_type.resize ((size_t )loc_type + 1 );
165130 }
131+ m_testing_schemes_at_location_type[(size_t )loc_type].schemes .push_back (scheme);
166132}
167133
168- void TestingStrategy::update_activity_status (TimePoint t)
134+ bool TestingStrategy::run_and_check (PersonalRandomNumberGenerator& rng, Person& person, const Location& location,
135+ TimePoint t)
169136{
170- for (auto & [_type, _id, testing_schemes] : m_location_to_schemes_map) {
171- for (auto & scheme : testing_schemes) {
172- scheme.update_activity_status (t);
173- }
174- }
175- }
137+ // Early return if no scheme defined for this location or type
138+ auto loc_id = location.get_id ().get ();
139+ auto loc_type = static_cast <size_t >(location.get_type ());
176140
177- bool TestingStrategy::run_strategy (PersonalRandomNumberGenerator& rng, Person& person, const Location& location,
178- TimePoint t)
179- {
180- // A Person is always allowed to go home and this is never called if a person is not discharged from a hospital or ICU.
181- if (location.get_type () == mio::abm::LocationType::Home) {
182- return true ;
141+ bool has_id_schemes =
142+ loc_id < m_testing_schemes_at_location_id.size () && !m_testing_schemes_at_location_id[loc_id].schemes .empty ();
143+
144+ bool has_type_schemes = loc_type < m_testing_schemes_at_location_type.size () &&
145+ !m_testing_schemes_at_location_type[loc_type].schemes .empty ();
146+
147+ if (!has_id_schemes && !has_type_schemes) {
148+ return true ; // No applicable schemes
183149 }
184150
185- // If the Person does not comply to Testing where there is a testing scheme at the target location, it is not allowed to enter.
186- if (!person.is_compliant (rng, InterventionType::Testing)) {
187- return false ;
151+ bool entry_allowed = true ; // Assume entry is allowed unless a scheme denies it
152+ // Check schemes for specific location id
153+ if (has_id_schemes) {
154+ for (const auto & scheme : m_testing_schemes_at_location_id[loc_id].schemes ) {
155+ if (scheme.run_and_test (rng, person, t)) {
156+ entry_allowed = false ; // Deny entry
157+ }
158+ }
188159 }
189160
190- // Lookup schemes for this specific location as well as the location type
191- // Lookup in std::vector instead of std::map should be much faster unless for large numbers of schemes
192- for (auto key : {std::make_pair (location.get_type (), location.get_id ()),
193- std::make_pair (location.get_type (), LocationId::invalid_id ())}) {
194- auto iter_schemes =
195- std::find_if (m_location_to_schemes_map.begin (), m_location_to_schemes_map.end (), [&](const auto & p) {
196- return p.type == key.first && p.id == key.second ;
197- });
198- if (iter_schemes != m_location_to_schemes_map.end ()) {
199- // Apply all testing schemes that are found
200- auto & schemes = iter_schemes->schemes ;
201- // Whether the Person is allowed to enter or not depends on the test result(s).
202- if (!std::all_of (schemes.begin (), schemes.end (), [&rng, &person, t](TestingScheme& ts) {
203- return !ts.is_active () || ts.run_scheme (rng, person, t);
204- })) {
205- return false ;
161+ // Check schemes for location type
162+ if (has_type_schemes) {
163+ for (const auto & scheme : m_testing_schemes_at_location_type[loc_type].schemes ) {
164+ if (scheme.run_and_test (rng, person, t)) {
165+ entry_allowed = false ; // Deny entry
206166 }
207167 }
208168 }
209- return true ;
169+
170+ // If the location is a home, entry is always allowed regardless of testing, no early return here because we still need to test
171+
172+ return entry_allowed;
210173}
211174
212175} // namespace abm
0 commit comments