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

466 add delay in testing and planned migration #866

Merged
merged 50 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b40aedc
Add struct for TestingResult in Person
khoanguyen-dev Nov 2, 2023
1df30bc
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Nov 10, 2023
20ddc7e
Add functions for TestResult in Person
khoanguyen-dev Nov 15, 2023
6513547
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Nov 27, 2023
2cf4afc
Add functions for checking a Person require a test in advance
khoanguyen-dev Nov 27, 2023
c83b092
Fix compilation errors
khoanguyen-dev Nov 28, 2023
fe2ce6e
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Nov 28, 2023
90530ce
Add structures for agents to look ahead for tests
khoanguyen-dev Dec 7, 2023
331848b
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Dec 7, 2023
2513d4e
Resolve compilation error
khoanguyen-dev Dec 11, 2023
0f55433
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Dec 11, 2023
0d387e5
Resolve compilation error
khoanguyen-dev Dec 11, 2023
39813d6
Simplify the testing in advance function
khoanguyen-dev Dec 11, 2023
0509292
Test for getting test result in Person
khoanguyen-dev Dec 14, 2023
6f4a8eb
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Dec 20, 2023
dfe6899
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Dec 20, 2023
0ecd87d
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Dec 27, 2023
562dca4
Add test to run TestStrategy for future Trips and Migration rules
khoanguyen-dev Jan 4, 2024
267767c
Remove unused variables in the tests running TestStrategy for future …
khoanguyen-dev Jan 4, 2024
609e4f9
Remove unused variables in the tests running TestStrategy for future …
khoanguyen-dev Jan 4, 2024
e266ad9
Add migration plan for Person class
khoanguyen-dev Jan 5, 2024
0d4d789
Add check to see if the Person plan ahead of time
khoanguyen-dev Jan 8, 2024
69419cf
Add check for parameter LookAheadTime
khoanguyen-dev Jan 8, 2024
4a29d4d
Change Person's m_test_results from vector to CustomIndexArray
khoanguyen-dev Jan 19, 2024
e75e79d
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Jan 19, 2024
7458989
Fix error in test abm migration and person
khoanguyen-dev Jan 19, 2024
09bbacf
Fix an error in run_strategy()
khoanguyen-dev Jan 22, 2024
941df85
Remove some debugging cout from run_strategy()
khoanguyen-dev Jan 22, 2024
b11679d
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Feb 11, 2024
44f9388
Add structure for Person to remember its lastest planning time
khoanguyen-dev Feb 11, 2024
eb53fde
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Jul 9, 2024
c523cfc
Remove tests for planning in World
khoanguyen-dev Jul 9, 2024
b82e845
Fix the error of PRAGMA_OMP(parallel for) in World::planning
khoanguyen-dev Jul 10, 2024
26d5a67
Attemp 2
khoanguyen-dev Jul 11, 2024
921c24e
Attemp 3
khoanguyen-dev Jul 11, 2024
dc375cf
Attemp 4 with pragma omp critical
khoanguyen-dev Jul 12, 2024
f7bc458
Add PRAGMA_OMP(critical) to World
khoanguyen-dev Jul 17, 2024
ec9c878
Fix error with PRAGMA_OMP(parallel for) to World
khoanguyen-dev Jul 17, 2024
59869d9
Merge branch 'main' into 466-add-delay-in-testing-and-planned-migration
khoanguyen-dev Jul 29, 2024
4e906af
Improve comments and adjust TestResult to have a variable named is_al…
khoanguyen-dev Jul 29, 2024
aa36dfd
Remove m_time_of_last_test for Person
khoanguyen-dev Jul 29, 2024
8eed843
Fix error in pycode abm.cpp
khoanguyen-dev Jul 29, 2024
7c03e5f
Fix error in pycode test_abm.py
khoanguyen-dev Jul 29, 2024
cc625cb
Move checking old test result to run_scheme()
khoanguyen-dev Jul 29, 2024
da9db33
= 0,
khoanguyen-dev Jul 31, 2024
b44be5b
Modifications according to David comments
khoanguyen-dev Aug 1, 2024
a37bab8
Small adjustments in comments
khoanguyen-dev Aug 1, 2024
80dc615
Move validity_period from TestParameter to TestingScheme
khoanguyen-dev Aug 2, 2024
4a29b0c
Fix pycode
khoanguyen-dev Aug 2, 2024
c0f443e
Small adjustments according to David's comments
khoanguyen-dev Aug 5, 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
4 changes: 4 additions & 0 deletions cpp/models/abm/analyze_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ std::vector<Model> ensemble_params_percentile(const std::vector<std::vector<Mode
static auto result = model.parameters.template get<LockdownDate>().days();
return result;
});
param_percentil(node, [](auto&& model) -> auto& {
static auto result = model.parameters.template get<PlanAheadTime>().days();
return result;
});
}

return percentile;
Expand Down
76 changes: 56 additions & 20 deletions cpp/models/abm/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace abm
* @brief Time that a Person is infected but not yet infectious.
*/
struct IncubationPeriod {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -55,7 +55,7 @@ struct IncubationPeriod {
};

struct InfectedNoSymptomsToSymptoms {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -67,7 +67,7 @@ struct InfectedNoSymptomsToSymptoms {
};

struct InfectedNoSymptomsToRecovered {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -79,7 +79,7 @@ struct InfectedNoSymptomsToRecovered {
};

struct InfectedSymptomsToRecovered {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -91,7 +91,7 @@ struct InfectedSymptomsToRecovered {
};

struct InfectedSymptomsToSevere {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -103,7 +103,7 @@ struct InfectedSymptomsToSevere {
};

struct SevereToCritical {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -115,7 +115,7 @@ struct SevereToCritical {
};

struct SevereToRecovered {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -127,7 +127,7 @@ struct SevereToRecovered {
};

struct CriticalToRecovered {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -139,7 +139,7 @@ struct CriticalToRecovered {
};

struct CriticalToDead {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -151,7 +151,7 @@ struct CriticalToDead {
};

struct RecoveredToSusceptible {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand Down Expand Up @@ -212,7 +212,7 @@ struct InfectivityDistributions {
* @brief Probability that an Infection is detected.
*/
struct DetectInfection {
using Type = CustomIndexArray< UncertainValue<>, VirusVariant, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, VirusVariant, AgeGroup>;
static Type get_default(AgeGroup size)
{
return Type({VirusVariant::Count, size}, 1.);
Expand All @@ -227,7 +227,7 @@ struct DetectInfection {
* @brief Effectiveness of a Mask of a certain MaskType% against an Infection%.
*/
struct MaskProtection {
using Type = CustomIndexArray< UncertainValue<>, MaskType>;
using Type = CustomIndexArray<UncertainValue<>, MaskType>;
static Type get_default(AgeGroup /*size*/)
{
return Type({MaskType::Count}, 1.);
Expand Down Expand Up @@ -308,19 +308,34 @@ struct HighViralLoadProtectionFactor {
}
};

/**
* @brief The index for all the testing types.
* Can be used as 0-based index.
*/
enum class TestingTypeIndex : std::uint32_t
{
GenericTest = 0,
AntigenTest = 1,
PCRTest = 2,
Count //last!!
};

khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
/**
* @brief Parameters that describe the reliability of a test.
*/
struct TestParameters {
UncertainValue<> sensitivity;
UncertainValue<> specificity;
UncertainValue<> sensitivity;
UncertainValue<> specificity;
TimeSpan required_time;
TimeSpan validity_period;
DavidKerkmann marked this conversation as resolved.
Show resolved Hide resolved
TestingTypeIndex test_type;
};

struct GenericTest {
using Type = TestParameters;
static Type get_default()
{
return Type{0.9, 0.99};
return Type{0.9, 0.99, hours(24), hours(24), TestingTypeIndex::GenericTest};
}
static std::string name()
{
Expand All @@ -329,13 +344,13 @@ struct GenericTest {
};

/**
* @brief Reliability of an AntigenTest.
* @brief Parameters of an AntigenTest.
*/
struct AntigenTest : public GenericTest {
using Type = TestParameters;
static Type get_default()
{
return Type{0.8, 0.88};
return Type{0.8, 0.88, minutes(30), hours(24), TestingTypeIndex::AntigenTest};
}
static std::string name()
{
Expand All @@ -350,7 +365,7 @@ struct PCRTest : public GenericTest {
using Type = TestParameters;
static Type get_default()
{
return Type{0.9, 0.99};
return Type{0.9, 0.99, days(1), days(3), TestingTypeIndex::PCRTest};
}
static std::string name()
{
Expand Down Expand Up @@ -392,7 +407,7 @@ struct QuarantineDuration {
* @brief Parameter for the exponential distribution to decide if a Person goes shopping.
*/
struct BasicShoppingRate {
using Type = CustomIndexArray< UncertainValue<>, AgeGroup>;
using Type = CustomIndexArray<UncertainValue<>, AgeGroup>;
static auto get_default(AgeGroup size)
{
return Type({size}, 1.0);
Expand Down Expand Up @@ -538,14 +553,30 @@ struct AgeGroupGotoWork {
}
};

/**
* @brief The TimeSpan agents plan forward to take tests.
*/
struct PlanAheadTime {
using Type = TimeSpan;
static Type get_default(AgeGroup /*size*/)
{
return TimeSpan(seconds(0));
}
static std::string name()
{
return "PlanAheadTime";
}
};

using ParametersBase =
ParameterSet<IncubationPeriod, InfectedNoSymptomsToSymptoms, InfectedNoSymptomsToRecovered,
InfectedSymptomsToRecovered, InfectedSymptomsToSevere, SevereToCritical, SevereToRecovered,
CriticalToDead, CriticalToRecovered, RecoveredToSusceptible, ViralLoadDistributions,
InfectivityDistributions, DetectInfection, MaskProtection, AerosolTransmissionRates, LockdownDate,
QuarantineDuration, SocialEventRate, BasicShoppingRate, WorkRatio, SchoolRatio, GotoWorkTimeMinimum,
GotoWorkTimeMaximum, GotoSchoolTimeMinimum, GotoSchoolTimeMaximum, AgeGroupGotoSchool,
AgeGroupGotoWork, InfectionProtectionFactor, SeverityProtectionFactor, HighViralLoadProtectionFactor>;
AgeGroupGotoWork, InfectionProtectionFactor, SeverityProtectionFactor, HighViralLoadProtectionFactor,
PlanAheadTime>;

/**
* @brief Maximum number of Person%s an infectious Person can infect at the respective Location.
Expand Down Expand Up @@ -742,6 +773,11 @@ class Parameters : public ParametersBase
return true;
}

if (this->get<PlanAheadTime>().seconds() < 0.0) {
log_error("Constraint check: Parameter PlanAheadTime smaller {:d}", 0);
return true;
}

return false;
}

Expand Down
36 changes: 36 additions & 0 deletions cpp/models/abm/person.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age
, m_person_id(person_id)
, m_cells{0}
, m_last_transport_mode(TransportMode::Unknown)
, m_test_results({TestingTypeIndex::Count}, TestResult())
, m_migration_planning()
, m_planned_time(TimePoint(-1))
{
m_random_workgroup = UniformDistribution<double>::get_instance()(rng);
m_random_schoolgroup = UniformDistribution<double>::get_instance()(rng);
Expand Down Expand Up @@ -292,5 +295,38 @@ ScalarType Person::get_protection_factor(TimePoint t, VirusVariant virus, const
t.days() - latest_protection.second.days());
}

void Person::add_test_result(TimePoint t, TestingTypeIndex type_index, bool result)
{
TestResult test_result;
test_result.time_of_testing = t;
test_result.type = type_index;
test_result.result = result;
// Remove outdated test results or replace the old result of the same type
m_test_results[{type_index}] = test_result;
khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
}

const Person::TestResult Person::get_test_result(TestingTypeIndex type_index) const
DavidKerkmann marked this conversation as resolved.
Show resolved Hide resolved
{
return m_test_results[{type_index}];
}

void Person::add_migration_plan(TimePoint t, Location& location)
{
std::pair<TimePoint, Location&> new_plan = {t, location};
m_migration_planning.push_back(new_plan);
khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
m_planned_time = t;
}

std::vector<std::pair<TimePoint, Location&>> Person::get_migration_plan(TimePoint from_time, TimePoint to_time)
{
std::vector<std::pair<TimePoint, Location&>> result;
for (auto plan : m_migration_planning) {
if (plan.first >= from_time && plan.first < to_time) {
result.push_back(plan);
}
}
return result;
}

} // namespace abm
} // namespace mio
57 changes: 56 additions & 1 deletion cpp/models/abm/person.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ class Person
return (m_person_id == other.m_person_id);
}

/**
* @brief The TestResult of a Person.
*/
struct TestResult {
DavidKerkmann marked this conversation as resolved.
Show resolved Hide resolved
TimePoint time_of_testing; ///< The TimePoint when the Person performs the test.
TestingTypeIndex type = TestingTypeIndex::Count; ///< The TestingTypeIndex of the test.
bool result; ///< The result of the test.
};

khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
/**
* @brief Time passes and the Person interacts with the population at its current Location.
* The Person might become infected.
Expand Down Expand Up @@ -473,6 +482,11 @@ class Person
*/
std::pair<ExposureType, TimePoint> get_latest_protection() const;

/**
* @brief Get the latest #Infection or #Vaccination and its initial TimePoint of the Person.
DavidKerkmann marked this conversation as resolved.
Show resolved Hide resolved
*/
bool has_valid_test(TimePoint t) const;
khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved

/**
* serialize this.
* @see mio::serialize
Expand Down Expand Up @@ -505,6 +519,43 @@ class Person
loc, age, id);
}

/**
* @brief Add TestResult to the Person
* @param[in] t The TimePoint of the result.
* @param[in] type The TestingTypeIndex of the result.
* @param[in] result The result of the test.
*/
void add_test_result(TimePoint t, TestingTypeIndex type, bool result);

/**
* @brief Get the most recent TestResult performed from the Person based on the TestType.
* @param[in] type The TestingTypeIndex of the result.
*/
const TestResult get_test_result(TestingTypeIndex type) const;

/**
* @brief Get the latest #Infection or #Vaccination and its initial TimePoint of the Person.
* @param[in] t TimePoint of the migration plan.
* @param[in] location Location of the destination of the migration plan.
*/
void add_migration_plan(TimePoint t, Location& location);

/**
* @brief Get all the migration plans between a certain time interval.
* @param[in] from_time TimePoint of the migration plan.
* @param[in] to_time TimePoint of the migration plan.
* @return The vector of all the migration plans between mentioned time period.
*/
std::vector<std::pair<TimePoint, Location&>> get_migration_plan(TimePoint from_time, TimePoint to_time);

/**
* @brief Get the agent's lastest TimePoint of its planning.
* @return The agent's lastest TimePoint of its planning.
*/
TimePoint get_planned_time() {
return m_planned_time;
}

private:
observer_ptr<Location> m_location; ///< Current Location of the Person.
std::vector<uint32_t> m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the
Expand All @@ -525,7 +576,11 @@ class Person
uint32_t m_person_id; ///< Id of the Person.
std::vector<uint32_t> m_cells; ///< Vector with all Cell%s the Person visits at its current Location.
mio::abm::TransportMode m_last_transport_mode; ///< TransportMode the Person used to get to its current Location.
Counter<uint32_t> m_rng_counter{0}; ///< counter for RandomNumberGenerator
Counter<uint32_t> m_rng_counter{0}; ///< counter for RandomNumberGenerator.
CustomIndexArray<TestResult, TestingTypeIndex> m_test_results; ///< CustomIndexArray for TestResults.
std::vector<std::pair<TimePoint, Location&>>
m_migration_planning; ///< Vector to store all migration plans (pairs of TimePoint and Location).
khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
TimePoint m_planned_time; ///< TimePoint of the last recored planning.
khoanguyen-dev marked this conversation as resolved.
Show resolved Hide resolved
};

} // namespace abm
Expand Down
13 changes: 11 additions & 2 deletions cpp/models/abm/testing_strategy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ bool TestingStrategy::run_strategy(Person::RandomNumberGenerator& rng, Person& p
if (location.get_type() == mio::abm::LocationType::Home) {
return true;
}

//lookup schemes for this specific location as well as the location type
//lookup in std::vector instead of std::map should be much faster unless for large numbers of schemes
for (auto loc_key : {LocationId{location.get_index(), location.get_type()},
Expand All @@ -185,7 +184,17 @@ bool TestingStrategy::run_strategy(Person::RandomNumberGenerator& rng, Person& p
//apply all testing schemes that are found
auto& schemes = iter_schemes->second;
if (!std::all_of(schemes.begin(), schemes.end(), [&rng, &person, t](TestingScheme& ts) {
return !ts.is_active() || ts.run_scheme(rng, person, t);
auto test_result = person.get_test_result(ts.get_type().get_default().test_type);
// If the agent has a test result valid until now, use the result directly
if ((test_result.type != TestingTypeIndex::Count) &&
(test_result.time_of_testing + ts.get_type().get_default().validity_period >= t)) {
return test_result.result;
}
// If not, perform the test and save result
auto result = !ts.is_active() || ts.run_scheme(rng, person, t);
person.add_test_result(t + ts.get_type().get_default().required_time,
ts.get_type().get_default().test_type, result);
return result;
})) {
return false;
}
Expand Down
Loading
Loading