Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1039 Add functions for the Person to choose whether to comply to mask,test and isolation #1040

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
943ae3e
Add functions for the Person to choose whether to comply to mask,test…
khoanguyen-dev May 24, 2024
770f9f9
Change apply_mask_intervention() so that the Person can enter the tar…
khoanguyen-dev May 27, 2024
7e687a7
Change positions of apply compliance for test and mask and name of as…
khoanguyen-dev May 30, 2024
a487487
Test if the NPI is applied and the Person cannot enter targeted location
khoanguyen-dev Jun 3, 2024
36ae54e
Add tests when the NPIs are applied, people cannot enter targeted loc…
khoanguyen-dev Jun 3, 2024
41e8e43
Remove not used variable in test_abm_world
khoanguyen-dev Jun 4, 2024
b4241b4
Remove not used variable in test_abm_world
khoanguyen-dev Jun 4, 2024
26e5c87
Correct the code as suggested by Rene's comments
khoanguyen-dev Jun 11, 2024
9798e7d
More correction in the code as suggested by Rene's comments, missing …
khoanguyen-dev Jun 14, 2024
652c983
Add TimePoint for mask initial use and improve tests
khoanguyen-dev Jun 14, 2024
51ec868
Add comments and correct tests
khoanguyen-dev Jun 14, 2024
2586963
Delete set_npi_active in benchmarks/abm.cpp
khoanguyen-dev Jun 19, 2024
b0c372f
Some fixes according to David's comments
khoanguyen-dev Jul 1, 2024
ac874ab
Fix some comment for mask compliance is world.cpp
khoanguyen-dev Jul 2, 2024
11b0f59
Remove voluntary mask wearing in World::migrate function
khoanguyen-dev Jul 3, 2024
3f8fa8b
Merge branch 'main' into 1039-implement-agents-compliance-to-mask-tes…
khoanguyen-dev Jul 22, 2024
f9bd301
Merge branch 'main' into 1039-implement-agents-compliance-to-mask-tes…
khoanguyen-dev Jul 22, 2024
6f8307d
Add test for migrateWithAppliedNPIs
khoanguyen-dev Jul 24, 2024
1b90418
Small updates according to Rene's comments
khoanguyen-dev Jul 25, 2024
0cc2218
Remove enum = 0 and optimise run_strategy()
khoanguyen-dev Jul 29, 2024
44eaf5a
Merge branch 'main' into 1039-implement-agents-compliance-to-mask-tes…
khoanguyen-dev Jul 31, 2024
f327c9b
Add TestModel, mobilityRulesWithAppliedNPIs
khoanguyen-dev Jul 31, 2024
58451d8
Modifications according to David comments
khoanguyen-dev Aug 1, 2024
e038809
Undo EXPECT_EQ is test_abm_model.cpp
khoanguyen-dev Aug 1, 2024
5c2f84f
Merge branch 'main' into 1039-implement-agents-compliance-to-mask-tes…
khoanguyen-dev Aug 5, 2024
2d4fada
Optimise perform_mobility and comments
khoanguyen-dev Aug 12, 2024
4305163
Adjust InfectionState to take into account required test time
khoanguyen-dev Aug 12, 2024
a6aa4c8
Remove test for trips of the Dead and quarantine Person
khoanguyen-dev Aug 12, 2024
27cca92
Add back test for the trips of the quarantine Person
khoanguyen-dev Aug 12, 2024
dca8915
Fix small error in trip's mobility
khoanguyen-dev Aug 13, 2024
990fe6b
Chang trip loop in perform_mobility() from while to for
khoanguyen-dev Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions cpp/benchmarks/abm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,20 @@ mio::abm::Simulation make_simulation(size_t num_persons, std::initializer_list<u
}

//equal chance of (moderate) mask refusal and (moderate) mask eagerness
auto pct_mask_values = std::array{0.05 /*-1*/, 0.2 /*-0.5*/, 0.5 /*0*/, 0.2 /*0.5*/, 0.05 /*1*/};
auto mask_value = -1 + 0.5 * mio::DiscreteDistribution<int>::get_instance()(prng, pct_mask_values);
person.set_mask_preferences({size_t(mio::abm::LocationType::Count), mask_value});
auto pct_compliance_values = std::array{0.05 /*0*/, 0.2 /*0.25*/, 0.5 /*0.5*/, 0.2 /*0.75*/, 0.05 /*1*/};
auto compliance_value = 0.25 * mio::DiscreteDistribution<int>::get_instance()(prng, pct_compliance_values);
person.set_compliance(mio::abm::InterventionType::Mask, compliance_value);
}

//masks at locations
for (auto& loc : model.get_locations()) {
//some % of locations require masks
//skip homes so persons always have a place to go, simulation might break otherwise
auto pct_require_mask = 0.2;
auto requires_mask = loc.get_type() != mio::abm::LocationType::Home &&
mio::UniformDistribution<double>::get_instance()(model.get_rng()) < pct_require_mask;
loc.set_npi_active(requires_mask);
if (loc.get_type() != mio::abm::LocationType::Home &&
mio::UniformDistribution<double>::get_instance()(model.get_rng()) < pct_require_mask) {
loc.set_required_mask(mio::abm::MaskType::Community);
}
}

//testing schemes
Expand Down
45 changes: 45 additions & 0 deletions cpp/models/abm/intervention_type.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2020-2024 MEmilio
*
* Authors: Khoa Nguyen
*
* Contact: Martin J. Kuehn <[email protected]>
*
* 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 MIO_ABM_INTERVENTION_TYPE_H
#define MIO_ABM_INTERVENTION_TYPE_H

#include <cstdint>

namespace mio
{
namespace abm
{

/**
* @brief Type of an Intervention.
*/
enum class InterventionType : std::uint32_t
{
Mask,
Testing,
Isolation,

Count
};
} // namespace abm
} // namespace mio

#endif
5 changes: 2 additions & 3 deletions cpp/models/abm/location.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* limitations under the License.
*/
#include "abm/location_type.h"
#include "abm/mask_type.h"
#include "abm/intervention_type.h"
#include "abm/location.h"
#include "abm/random_events.h"

Expand All @@ -32,8 +32,7 @@ Location::Location(LocationType loc_type, LocationId loc_id, size_t num_agegroup
, m_id(loc_id)
, m_parameters(num_agegroups)
, m_cells(num_cells)
, m_required_mask(MaskType::Community)
, m_npi_active(false)
, m_required_mask(MaskType::None)
{
assert(num_cells > 0 && "Number of cells has to be larger than 0.");
}
Expand Down
19 changes: 4 additions & 15 deletions cpp/models/abm/location.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,22 +214,12 @@ class Location
}

/**
* @brief Get the information whether NPIs are active at this Location.
* If true requires e.g. Mask%s when entering a Location.
* @return True if NPIs are active at this Location.
* @brief Get the information whether masks are required to enter this Location.
* @return True if masks are required to enter this Location.
*/
bool get_npi_active() const
bool is_mask_required() const
{
return m_npi_active;
}

/**
* @brief Activate or deactivate NPIs at this Location.
* @param[in] new_status Status of NPIs.
*/
void set_npi_active(bool new_status)
{
m_npi_active = new_status;
return m_required_mask != MaskType::None;
}

/**
Expand Down Expand Up @@ -286,7 +276,6 @@ class Location
LocalInfectionParameters m_parameters; ///< Infection parameters for the Location.
std::vector<Cell> m_cells{}; ///< A vector of all Cell%s that the Location is divided in.
MaskType m_required_mask; ///< Least secure type of Mask that is needed to enter the Location.
bool m_npi_active; ///< If true requires e.g. Mask%s to enter the Location.
GeographicalLocation m_geographical_location; ///< Geographical location (longitude and latitude) of the Location.
};

Expand Down
15 changes: 10 additions & 5 deletions cpp/models/abm/mask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@ namespace mio
{
namespace abm
{
Mask::Mask(MaskType type)
Mask::Mask(MaskType type, TimePoint t)
: m_type(type)
, m_time_used(TimeSpan(0))
, m_time_first_usage(t)
{
}

void Mask::change_mask(MaskType new_mask_type)
void Mask::change_mask(MaskType new_mask_type, TimePoint t)
{
m_type = new_mask_type;
m_time_used = TimeSpan(0);
m_type = new_mask_type;
m_time_first_usage = t;
}

const TimeSpan Mask::get_time_used(TimePoint curr_time) const
{
return curr_time - m_time_first_usage;
}

} // namespace abm
Expand Down
23 changes: 7 additions & 16 deletions cpp/models/abm/mask.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ class Mask
/**
* @brief Construct a new Mask of a certain type.
* @param[in] type The type of the Mask.
* @param[in] t The TimePoint of the Mask's initial usage.
*/
Mask(MaskType type);
Mask(MaskType type, TimePoint t);

/**
* @brief Get the MaskType of this Mask.
Expand All @@ -51,30 +52,20 @@ class Mask

/**
* @brief Get the length of time this Mask has been used.
* @param[in] curr_time The current TimePoint.
*/
const TimeSpan& get_time_used() const
{
return m_time_used;
}

/**
* @brief Increase the time this Mask was used by a timestep.
* @param[in] dt The length of the timestep.
*/
void increase_time_used(TimeSpan dt)
{
m_time_used += dt;
}
const TimeSpan get_time_used(TimePoint curr_time) const;

/**
* @brief Change the type of the Mask and reset the time it was used.
* @param[in] new_mask_type The type of the new Mask.
* @param[in] t The TimePoint of mask change.
*/
void change_mask(MaskType new_mask_type);
void change_mask(MaskType new_mask_type, TimePoint t);

private:
MaskType m_type; ///< Type of the Mask.
TimeSpan m_time_used; ///< Length of time the Mask has been used.
TimePoint m_time_first_usage; ///< TimePoint of the Mask's initial usage.
};
} // namespace abm
} // namespace mio
Expand Down
3 changes: 2 additions & 1 deletion cpp/models/abm/mask_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ namespace abm
*/
enum class MaskType : std::uint32_t
{
Community = 0,
None,
Community,
Surgical,
FFP2,

Expand Down
4 changes: 2 additions & 2 deletions cpp/models/abm/mobility_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace abm
*/
enum class TransportMode : uint32_t
{
Bike = 0,
Bike,
CarDriver,
CarPassenger,
PublicTransport,
Expand All @@ -47,7 +47,7 @@ enum class TransportMode : uint32_t
*/
enum class ActivityType : uint32_t
{
Workplace = 0,
Workplace,
Education,
Shopping,
Leisure,
Expand Down
77 changes: 52 additions & 25 deletions cpp/models/abm/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "abm/model.h"
#include "abm/location_id.h"
#include "abm/location_type.h"
#include "abm/intervention_type.h"
#include "abm/person.h"
#include "abm/location.h"
#include "abm/mobility_rules.h"
Expand Down Expand Up @@ -97,44 +98,57 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt)
Person& person = m_persons[person_id];
auto personal_rng = PersonalRandomNumberGenerator(m_rng, person);

auto try_mobility_rule = [&](auto rule) -> bool {
//run mobility rule and check if change of location can actually happen
auto try_migration_rule = [&](auto rule) -> bool {
//run migration rule and check if migration can actually happen
khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
auto target_type = rule(personal_rng, person, t, dt, parameters);
const Location& target_location = get_location(find_location(target_type, person_id));
const LocationId current_location = person.get_location();
if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) {

// Check if the Person wears mask if required at targeted location
if (!target_location.is_mask_required() || person.is_compliant(personal_rng, InterventionType::Mask)) {
// Check if the capacity of targeted Location is not reached
if (target_location.get_id() != current_location &&
get_number_persons(target_location.get_id()) < target_location.get_capacity().persons) {
bool wears_mask = person.apply_mask_intervention(personal_rng, target_location);
if (wears_mask) {
// Perform TestingStrategy if required
if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) {
change_location(person_id, target_location.get_id());
if (target_location.is_mask_required()) {
// If the current MaskProtection level is lower than required, the Person changes mask
if (parameters.get<MaskProtection>()[person.get_mask().get_type()] <
parameters.get<MaskProtection>()[target_location.get_required_mask()]) {
person.set_mask(target_location.get_required_mask(), t);
}
}
else {
person.set_mask(MaskType::None, t);
}
return true;
}
return true;
}
}
return false;
reneSchm marked this conversation as resolved.
Show resolved Hide resolved
};

//run mobility rules one after the other if the corresponding location type exists
//run migration rules one after the other if the corresponding location type exists
khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
//shortcutting of bool operators ensures the rules stop after the first rule is applied
if (m_use_mobility_rules) {
(has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) ||
(has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) ||
(has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) ||
(has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) ||
(has_locations({LocationType::School, LocationType::Home}) && try_mobility_rule(&go_to_school)) ||
(has_locations({LocationType::Work, LocationType::Home}) && try_mobility_rule(&go_to_work)) ||
(has_locations({LocationType::BasicsShop, LocationType::Home}) && try_mobility_rule(&go_to_shop)) ||
(has_locations({LocationType::SocialEvent, LocationType::Home}) && try_mobility_rule(&go_to_event)) ||
(has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine));
(has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) ||
(has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) ||
(has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) ||
(has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) ||
(has_locations({LocationType::School, LocationType::Home}) && try_migration_rule(&go_to_school)) ||
(has_locations({LocationType::Work, LocationType::Home}) && try_migration_rule(&go_to_work)) ||
(has_locations({LocationType::BasicsShop, LocationType::Home}) && try_migration_rule(&go_to_shop)) ||
(has_locations({LocationType::SocialEvent, LocationType::Home}) && try_migration_rule(&go_to_event)) ||
(has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine));
DavidKerkmann marked this conversation as resolved.
Show resolved Hide resolved
}
else {
//no daily routine mobility, just infection related
(has_locations({LocationType::Cemetery}) && try_mobility_rule(&get_buried)) ||
(has_locations({LocationType::Home}) && try_mobility_rule(&return_home_when_recovered)) ||
(has_locations({LocationType::Hospital}) && try_mobility_rule(&go_to_hospital)) ||
(has_locations({LocationType::ICU}) && try_mobility_rule(&go_to_icu)) ||
(has_locations({LocationType::Home}) && try_mobility_rule(&go_to_quarantine));
//no daily routine migration, just infection related
(has_locations({LocationType::Cemetery}) && try_migration_rule(&get_buried)) ||
(has_locations({LocationType::Home}) && try_migration_rule(&return_home_when_recovered)) ||
(has_locations({LocationType::Hospital}) && try_migration_rule(&go_to_hospital)) ||
(has_locations({LocationType::ICU}) && try_migration_rule(&go_to_icu)) ||
(has_locations({LocationType::Home}) && try_migration_rule(&go_to_quarantine));
}
}

Expand All @@ -150,9 +164,22 @@ void Model::perform_mobility(TimePoint t, TimeSpan dt)
auto personal_rng = PersonalRandomNumberGenerator(m_rng, person);
if (!person.is_in_quarantine(t, parameters) && person.get_infection_state(t) != InfectionState::Dead) {
auto& target_location = get_location(trip.destination);
if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) {
person.apply_mask_intervention(personal_rng, target_location);
change_location(person.get_id(), target_location.get_id(), trip.trip_mode);
// Check if the Person wears mask if required at targeted location
if (!target_location.is_mask_required() || person.is_compliant(personal_rng, InterventionType::Mask)) {
// Perform TestingStrategy if required
if (m_testing_strategy.run_strategy(personal_rng, person, target_location, t)) {
change_location(person.get_id(), target_location.get_id(), trip.trip_mode);
if (target_location.is_mask_required()) {
// If the current MaskProtection level is lower than required, the Person changes mask
if (parameters.get<MaskProtection>()[person.get_mask().get_type()] <
parameters.get<MaskProtection>()[target_location.get_required_mask()]) {
person.set_mask(target_location.get_required_mask(), t);
}
}
else {
person.set_mask(MaskType::None, t);
}
}
}
}
m_trip_list.increase_index();
Expand Down
Loading
Loading