Skip to content
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f272d9c
add ABM simulation node and edge
jubicker Jun 7, 2024
1f29e1c
graph abm implementation
jubicker Jul 11, 2024
5e91137
remove warning surpression for boost
jubicker Jul 11, 2024
22a2a60
merge main
jubicker Jul 11, 2024
4368cb5
add CMakeList
jubicker Jul 12, 2024
81df1a8
CMake List Bug fix
jubicker Jul 12, 2024
5b2c64a
fix include
jubicker Jul 12, 2024
414c3f4
bug fix
jubicker Jul 12, 2024
4e2a733
test
jubicker Jul 15, 2024
8f1d7cb
test apply_mobility
jubicker Jul 15, 2024
7400802
extend apply_mobility test
jubicker Jul 16, 2024
b314715
error in abm_minimal example
jubicker Jul 16, 2024
dbbd068
test for apply mobility
jubicker Jul 17, 2024
196275f
finx clang bug
jubicker Jul 18, 2024
a5a4e88
abm minimal bug fix
jubicker Jul 18, 2024
3367fa6
Merge branch '646-use-graph-model-with-abms-v2' of https://github.com…
jubicker Jul 18, 2024
8cce390
graph_abm example
jubicker Jul 18, 2024
2fc9026
graph abm example
jubicker Jul 19, 2024
ef4c0b1
merge main
jubicker Jul 22, 2024
0ece6be
fix merge conflicts
jubicker Jul 23, 2024
b773193
fix test
jubicker Jul 23, 2024
b4d44d5
graph abm example
jubicker Jul 25, 2024
ae95301
remove unnecessary comment
jubicker Jul 25, 2024
5ebd62f
Merge branch '646-use-graph-model-with-abms-v2' of https://github.com…
jubicker Jul 25, 2024
e22009b
fix bug
jubicker Jul 25, 2024
11c3ae8
delete dbugging comments
jubicker Jul 26, 2024
1f85548
Merge branch '646-use-graph-model-with-abms-v2' of https://github.com…
jubicker Jul 26, 2024
fdad4da
benchmark inactive persons
jubicker Jul 31, 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
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ if(MEMILIO_BUILD_MODELS)
add_subdirectory(models/ode_sir)
add_subdirectory(models/sde_sir)
add_subdirectory(models/sde_sirs)
add_subdirectory(models/graph_abm)
endif()

if(MEMILIO_BUILD_EXAMPLES)
Expand Down
10 changes: 7 additions & 3 deletions cpp/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ add_executable(history_example history.cpp)
target_link_libraries(history_example PRIVATE memilio)
target_compile_options(history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS})

add_executable(graph_abm_example graph_abm.cpp)
target_link_libraries(graph_abm_example PRIVATE memilio graph_abm abm)
target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS})

if(MEMILIO_HAS_JSONCPP)
add_executable(ode_secir_read_graph_example ode_secir_read_graph.cpp)
target_link_libraries(ode_secir_read_graph_example PRIVATE memilio ode_secir)
Expand Down Expand Up @@ -144,7 +148,7 @@ if(MEMILIO_HAS_HDF5)
endif()

if(MEMILIO_HAS_JSONCPP)
add_executable(ide_initialization_example ide_initialization.cpp)
target_link_libraries(ide_initialization_example PRIVATE memilio ide_secir)
target_compile_options(ide_initialization_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS})
add_executable(ide_initialization_example ide_initialization.cpp)
target_link_libraries(ide_initialization_example PRIVATE memilio ide_secir)
target_compile_options(ide_initialization_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS})
endif()
6 changes: 3 additions & 3 deletions cpp/examples/abm_minimal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ int main()
world.parameters.get<mio::abm::IncubationPeriod>() = 4.;

// Set the age group the can go to school is AgeGroup(1) (i.e. 5-14)
world.parameters.get<mio::abm::AgeGroupGotoSchool>() = false;
world.parameters.get<mio::abm::AgeGroupGotoSchool>() = false;
world.parameters.get<mio::abm::AgeGroupGotoSchool>()[age_group_5_to_14] = true;
// Set the age group the can go to work is AgeGroup(2) and AgeGroup(3) (i.e. 15-34 and 35-59)
world.parameters.get<mio::abm::AgeGroupGotoWork>().set_multiple({age_group_15_to_34, age_group_35_to_59}, true);
Expand All @@ -55,7 +55,7 @@ int main()
// For more than 1 family households we need families. These are parents and children and randoms (which are distributed like the data we have for these households).
auto child = mio::abm::HouseholdMember(num_age_groups); // A child is 50/50% 0-4 or 5-14.
child.set_age_weight(age_group_0_to_4, 1);
child.set_age_weight(age_group_0_to_4, 1);
child.set_age_weight(age_group_5_to_14, 1);

auto parent = mio::abm::HouseholdMember(num_age_groups); // A parent is 50/50% 15-34 or 35-59.
parent.set_age_weight(age_group_15_to_34, 1);
Expand Down Expand Up @@ -138,7 +138,7 @@ int main()
world.assign_location(id, hospital);
world.assign_location(id, icu);
//assign work/school to people depending on their age
if (person.get_age() == age_group_0_to_4) {
if (person.get_age() == age_group_5_to_14) {
world.assign_location(id, school);
}
if (person.get_age() == age_group_15_to_34 || person.get_age() == age_group_35_to_59) {
Expand Down
274 changes: 274 additions & 0 deletions cpp/examples/graph_abm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/*
* Copyright (C) 2020-2024 MEmilio
*
* Authors: Julia Bicker
*
* Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
*
* 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/household.h"
#include "abm/world.h"
#include "abm/infection_state.h"
#include "abm/location_type.h"
#include "abm/time.h"
#include "abm/person_id.h"
#include "graph_abm/graph_abm_mobility.h"
#include "graph_abm/mobility_rules.h"
#include "memilio/io/history.h"
#include "memilio/mobility/graph.h"
#include <cstddef>
#include <cstdint>
#include <map>
#include <tuple>
#include <utility>
#include <vector>

//Logger
struct Logger : mio::LogAlways {
Comment on lines +38 to +39

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A slightly more descriptive name would be neat. It is fine as long as this stays in the example.

/**
* A vector of tuples with the Location information i.e. each tuple contains the following information:
* - The LocationId (including the world id)
* - The total number of Persons at the location
* - A map containing the number of Persons per InfectionState at the location
*/
using Type = std::vector<std::tuple<int, mio::abm::LocationType, mio::abm::LocationId, size_t,
std::map<mio::abm::InfectionState, size_t>>>;
static Type log(const mio::abm::Simulation& sim)
{
Type location_information{};
auto t = sim.get_time();
for (auto&& loc : sim.get_world().get_locations()) {
std::map<mio::abm::InfectionState, size_t> persons_per_infection_state;
for (size_t i = 0; i < static_cast<size_t>(mio::abm::InfectionState::Count); ++i) {
auto inf_state = mio::abm::InfectionState(i);
persons_per_infection_state.insert(
{inf_state, sim.get_world().get_subpopulation(loc.get_id(), t, inf_state)});
}
location_information.push_back(std::make_tuple(loc.get_world_id(), loc.get_type(), loc.get_id(),
sim.get_world().get_number_persons(loc.get_id()),
persons_per_infection_state));
Comment on lines +59 to +61

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this causes too many reallocations, you could reserve #worlds * #locations in world 0 vector entries.

}
return location_information;
}
};

int main()
{
// This is an example with three age groups representing children, adults and seniors.
size_t num_age_groups = 3;
const auto age_group_children = mio::AgeGroup(0);
const auto age_group_adults = mio::AgeGroup(1);
const auto age_group_seniors = mio::AgeGroup(2);

auto world1 = mio::abm::World(num_age_groups, 0);

//Set infection parameters
world1.parameters.get<mio::abm::IncubationPeriod>() = 4.;
world1.parameters.get<mio::abm::InfectedNoSymptomsToSymptoms>() = 2.;
world1.parameters.get<mio::abm::InfectedNoSymptomsToRecovered>() = 4.;
world1.parameters.get<mio::abm::InfectedSymptomsToRecovered>() = 5.;
world1.parameters.get<mio::abm::InfectedSymptomsToSevere>() = 6.;
world1.parameters.get<mio::abm::SevereToRecovered>() = 8.;
world1.parameters.get<mio::abm::SevereToCritical>() = 7.;
world1.parameters.get<mio::abm::CriticalToRecovered>() = 10.;
world1.parameters.get<mio::abm::CriticalToDead>() = 11.;

//Age group 0 goes to school and age group 1 goes to work
world1.parameters.get<mio::abm::AgeGroupGotoSchool>()[age_group_children] = true;
world1.parameters.get<mio::abm::AgeGroupGotoWork>()[age_group_adults] = true;

//Household members can be child, parent or senior
auto child = mio::abm::HouseholdMember(num_age_groups);
child.set_age_weight(age_group_children, 1);
auto parent = mio::abm::HouseholdMember(num_age_groups);
parent.set_age_weight(age_group_adults, 1);
auto adult = mio::abm::HouseholdMember(num_age_groups);
adult.set_age_weight(age_group_adults, 1);
adult.set_age_weight(age_group_seniors, 1);

//Single-Person households
auto single_hh = mio::abm::Household();
single_hh.add_members(adult, 1);

//Two-Adult household
auto two_adult_hh = mio::abm::Household();
two_adult_hh.add_members(adult, 2);

//Single-Parent household
auto single_parent_hh = mio::abm::Household();
single_parent_hh.add_members(child, 1);
single_parent_hh.add_members(parent, 1);

//Family household
auto family_hh = mio::abm::Household();
family_hh.add_members(child, 1);
family_hh.add_members(parent, 2);

// Vector holding all persons for the graph simulation. This vector is copied to all worlds at the end.
std::vector<mio::abm::Person> persons;

//Household groups for world 1
auto single_hh_group_w1 = mio::abm::HouseholdGroup();
single_hh_group_w1.add_households(single_hh, 5);
auto two_adult_hh_group_w1 = mio::abm::HouseholdGroup();
two_adult_hh_group_w1.add_households(two_adult_hh, 3);
auto single_parent_hh_group_w1 = mio::abm::HouseholdGroup();
single_parent_hh_group_w1.add_households(single_parent_hh, 5);
auto family_hh_group_w1 = mio::abm::HouseholdGroup();
family_hh_group_w1.add_households(family_hh, 10);
add_household_group_to_world(world1, single_hh_group_w1);
add_household_group_to_world(world1, two_adult_hh_group_w1);
add_household_group_to_world(world1, single_hh_group_w1);
add_household_group_to_world(world1, family_hh_group_w1);

//add persons from world 0 to vector
for (auto& person : world1.get_persons()) {
mio::abm::PersonId new_id{static_cast<uint32_t>(persons.size())};
persons.emplace_back(person, new_id);
}

auto world2 = mio::abm::World(num_age_groups, 1);

//Household groups for world 2
auto single_hh_group_w2 = mio::abm::HouseholdGroup();
single_hh_group_w2.add_households(single_hh, 6);
auto two_adult_hh_group_w2 = mio::abm::HouseholdGroup();
two_adult_hh_group_w2.add_households(two_adult_hh, 2);
auto single_parent_hh_group_w2 = mio::abm::HouseholdGroup();
single_parent_hh_group_w2.add_households(single_parent_hh, 10);
auto family_hh_group_w2 = mio::abm::HouseholdGroup();
family_hh_group_w2.add_households(family_hh, 11);
add_household_group_to_world(world2, single_hh_group_w2);
add_household_group_to_world(world2, two_adult_hh_group_w2);
add_household_group_to_world(world2, single_hh_group_w2);
add_household_group_to_world(world2, family_hh_group_w2);

//add persons from world 1 to vector
for (auto& person : world2.get_persons()) {
mio::abm::PersonId new_id{static_cast<uint32_t>(persons.size())};
persons.emplace_back(person, new_id);
}

//Create locations for both worlds
//world 0
auto event_w1 = world1.add_location(mio::abm::LocationType::SocialEvent);
world1.get_location(event_w1).get_infection_parameters().set<mio::abm::MaximumContacts>(10);
auto hospital_w1 = world1.add_location(mio::abm::LocationType::Hospital);
world1.get_location(hospital_w1).get_infection_parameters().set<mio::abm::MaximumContacts>(10);
auto icu_w1 = world1.add_location(mio::abm::LocationType::ICU);
world1.get_location(icu_w1).get_infection_parameters().set<mio::abm::MaximumContacts>(5);
auto shop_w1 = world1.add_location(mio::abm::LocationType::BasicsShop);
world1.get_location(shop_w1).get_infection_parameters().set<mio::abm::MaximumContacts>(20);
auto school_w1 = world1.add_location(mio::abm::LocationType::School);
world1.get_location(school_w1).get_infection_parameters().set<mio::abm::MaximumContacts>(20);
auto work_w1 = world1.add_location(mio::abm::LocationType::Work);
world1.get_location(work_w1).get_infection_parameters().set<mio::abm::MaximumContacts>(10);
//World 1
auto event_w2 = world2.add_location(mio::abm::LocationType::SocialEvent);
world2.get_location(event_w2).get_infection_parameters().set<mio::abm::MaximumContacts>(10);
auto hospital_w2 = world2.add_location(mio::abm::LocationType::Hospital);
world2.get_location(hospital_w2).get_infection_parameters().set<mio::abm::MaximumContacts>(10);
auto icu_w2 = world2.add_location(mio::abm::LocationType::ICU);
world2.get_location(icu_w2).get_infection_parameters().set<mio::abm::MaximumContacts>(5);
auto shop_w2 = world2.add_location(mio::abm::LocationType::BasicsShop);
world2.get_location(shop_w2).get_infection_parameters().set<mio::abm::MaximumContacts>(20);
auto school_w2 = world2.add_location(mio::abm::LocationType::School);
world2.get_location(school_w2).get_infection_parameters().set<mio::abm::MaximumContacts>(20);
auto work_w2 = world2.add_location(mio::abm::LocationType::Work);
world2.get_location(work_w2).get_infection_parameters().set<mio::abm::MaximumContacts>(10);

auto start_date = mio::abm::TimePoint(0);
auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30);
std::vector<uint32_t> params_e1;
std::vector<uint32_t> params_e2;

//Assign infection states and locations
std::vector<double> infection_distribution{0.5, 0.3, 0.05, 0.05, 0.05, 0.05, 0.0, 0.0};
for (auto& person : persons) {
mio::abm::InfectionState infection_state = mio::abm::InfectionState(
mio::DiscreteDistribution<size_t>::get_instance()(mio::thread_local_rng(), infection_distribution));
auto rng = mio::abm::PersonalRandomNumberGenerator(mio::thread_local_rng(), person);
if (infection_state != mio::abm::InfectionState::Susceptible) {
person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(),
world1.parameters, start_date, infection_state));
}
// Assign locations to persons from world 1
if (person.get_assigned_location_world_id(mio::abm::LocationType::Home) == world1.get_id()) {
person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w1, world1.get_id());
person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w1, world1.get_id());
person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w1, world1.get_id());
person.set_assigned_location(mio::abm::LocationType::ICU, icu_w1, world1.get_id());
if (person.get_age() == age_group_children) {
person.set_assigned_location(mio::abm::LocationType::School, school_w1, world1.get_id());
}
if (person.get_age() == age_group_adults) {
//10% of adults in world 0 work in world 1
size_t work_world = mio::DiscreteDistribution<size_t>::get_instance()(mio::thread_local_rng(),
std::vector<double>{0.9, 0.1});
if (work_world == 1) { //person works in other world
person.set_assigned_location(mio::abm::LocationType::Work, work_w2, world2.get_id());
//add person to edge parameters
params_e1.push_back(person.get_id().get());
}
else { //person works in same world
person.set_assigned_location(mio::abm::LocationType::Work, work_w1, world1.get_id());
}
}
}
else {
person.set_assigned_location(mio::abm::LocationType::SocialEvent, event_w2, world2.get_id());
person.set_assigned_location(mio::abm::LocationType::BasicsShop, shop_w2, world2.get_id());
person.set_assigned_location(mio::abm::LocationType::Hospital, hospital_w2, world2.get_id());
person.set_assigned_location(mio::abm::LocationType::ICU, icu_w2, world2.get_id());
if (person.get_age() == age_group_children) {
person.set_assigned_location(mio::abm::LocationType::School, school_w2, world2.get_id());
}
if (person.get_age() == age_group_adults) {
//20% of adults in world 1 work in world 0
size_t work_world = mio::DiscreteDistribution<size_t>::get_instance()(mio::thread_local_rng(),
std::vector<double>{0.2, 0.8});
if (work_world == 0) { //person works in other world
person.set_assigned_location(mio::abm::LocationType::Work, work_w1, world1.get_id());
//add person to edge parameters
params_e2.push_back(person.get_id().get());
}
else { //person works in same world
person.set_assigned_location(mio::abm::LocationType::Work, work_w2, world2.get_id());
}
}
}
}

//copy persons to both worlds
world1.set_persons(persons);
world2.set_persons(persons);

using HistoryType = mio::History<mio::DataWriterToMemory, Logger>;
mio::Graph<mio::ABMSimulationNode<HistoryType>, mio::ABMMobilityEdge<HistoryType>> graph;
graph.add_node(world1.get_id(), HistoryType{}, start_date, std::move(world1));
graph.add_node(world2.get_id(), HistoryType{}, start_date, std::move(world2));
graph.add_edge(0, 1, params_e1,
std::vector<mio::ABMMobilityEdge<HistoryType>::MobilityRuleType>{&mio::apply_commuting});
graph.add_edge(1, 0, params_e2,
std::vector<mio::ABMMobilityEdge<HistoryType>::MobilityRuleType>{&mio::apply_commuting});

auto sim = mio::make_abm_graph_sim<HistoryType>(start_date, mio::abm::hours(12), std::move(graph));
sim.advance(end_date);

auto& log_n1 = std::get<0>(sim.get_graph().nodes()[0].property.get_history()).get_log();
auto& log_n2 = std::get<0>(sim.get_graph().nodes()[1].property.get_history()).get_log();

return 0;
}
Loading