From b9345b1426084bc775288920bcec4a38db5d4b12 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Thu, 16 Mar 2023 14:56:38 +0100 Subject: [PATCH 001/105] openmp in abm --- cpp/CMakeLists.txt | 1 + cpp/memilio/CMakeLists.txt | 4 ++++ cpp/memilio/config_internal.h.in | 1 + cpp/models/abm/location.h | 2 ++ cpp/tests/CMakeLists.txt | 6 ++++++ cpp/thirdparty/CMakeLists.txt | 4 ++++ 6 files changed, 18 insertions(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1793d7b5a8..e60ef8c60d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -16,6 +16,7 @@ option(MEMILIO_SANITIZE_UNDEFINED "Enable undefined behavior sanitizer." OFF) option(MEMILIO_BUILD_SHARED_LIBS "Build memilio as a shared library." ON) option(MEMILIO_BUILD_STATIC_LIBS "Build memilio as a static library." ON) option(MEMILIO_ENABLE_MPI "Build memilio with MPI." OFF) +option(MEMILIO_ENABLE_OPENMP "Enable Multithreading with OpenMP." ON) mark_as_advanced(MEMILIO_USE_BUNDLED_SPDLOG MEMILIO_SANITIZE_ADDRESS MEMILIO_SANITIZE_UNDEFINED) diff --git a/cpp/memilio/CMakeLists.txt b/cpp/memilio/CMakeLists.txt index ba34a41d02..dd3a9ed841 100644 --- a/cpp/memilio/CMakeLists.txt +++ b/cpp/memilio/CMakeLists.txt @@ -111,3 +111,7 @@ endif() if(MEMILIO_ENABLE_MPI) target_link_libraries(memilio PUBLIC MPI::MPI_CXX) endif() + +if(MEMILIO_ENABLE_OPENMP) + target_link_libraries(memilio PUBLIC OpenMP::OpenMP_CXX) +endif() diff --git a/cpp/memilio/config_internal.h.in b/cpp/memilio/config_internal.h.in index 5430ab1833..7a86533273 100644 --- a/cpp/memilio/config_internal.h.in +++ b/cpp/memilio/config_internal.h.in @@ -28,5 +28,6 @@ #cmakedefine MEMILIO_HAS_HDF5 #cmakedefine MEMILIO_HAS_JSONCPP #cmakedefine MEMILIO_ENABLE_MPI +#cmakedefine MEMILIO_ENABLE_OPENMP #endif diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 86c57ff28b..d10d3f867c 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -33,6 +33,7 @@ #include "memilio/utils/memory.h" #include #include +#include namespace mio { @@ -330,6 +331,7 @@ class Location const TimeSeries& get_subpopulations() const; private: + std::mutex m_mut; ///< Mutex to protect the list of persons from concurrent modification. LocationId m_id; ///< Id of the Location including type and index. bool m_capacity_adapted_transmission_risk; /**< If true considers the LocationCapacity for the computation of the transmission risk.*/ diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index c9aa540f65..a78972d08e 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -78,6 +78,12 @@ test_save_results.cpp ) endif() +if(MEMILIO_ENABLE_OPENMP) + set(TESTSOURCES ${TESTSOURCES} + test_openmp.cpp + ) +endif() + add_executable(memilio-test ${TESTSOURCES}) target_include_directories(memilio-test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(memilio-test PRIVATE memilio ode_secir ode_seir ode_secirvvs ide_seir ide_secir abm gtest_main) diff --git a/cpp/thirdparty/CMakeLists.txt b/cpp/thirdparty/CMakeLists.txt index 79c1955ed1..c77d1f8a3d 100644 --- a/cpp/thirdparty/CMakeLists.txt +++ b/cpp/thirdparty/CMakeLists.txt @@ -112,3 +112,7 @@ endif() if (MEMILIO_ENABLE_MPI) find_package(MPI REQUIRED COMPONENTS CXX) endif() + +if (MEMILIO_ENABLE_OPENMP) + find_package(OpenMP COMPONENTS CXX REQUIRED) +endif() From a9e96959c983368ab566249edc5f52c2a1e4dce5 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Thu, 4 May 2023 14:55:41 +0200 Subject: [PATCH 002/105] skeleton for counter based rng --- cpp/memilio/compartments/parameter_studies.h | 48 ++++++++++++++++---- cpp/memilio/utils/random_number_generator.h | 35 ++++++++++++++ cpp/models/abm/person.h | 33 ++++++++++++++ cpp/models/abm/simulation.cpp | 2 +- cpp/models/abm/simulation.h | 1 + cpp/models/abm/world.cpp | 4 +- cpp/models/abm/world.h | 6 +-- 7 files changed, 113 insertions(+), 16 deletions(-) diff --git a/cpp/memilio/compartments/parameter_studies.h b/cpp/memilio/compartments/parameter_studies.h index 27f19f91d8..ff1edbe0ac 100644 --- a/cpp/memilio/compartments/parameter_studies.h +++ b/cpp/memilio/compartments/parameter_studies.h @@ -31,6 +31,7 @@ #include #include +#include #include namespace mio @@ -60,6 +61,36 @@ class ParameterStudy */ using SimulationGraph = mio::Graph, mio::MigrationEdge>; + struct RandomNumberGenerator + { + public: + RandomNumberGenerator(RNGKey key, size_t run_idx) + : m_key(key) + , m_counter(rng_subsequence_counter(run_idx, RNGCounter(0))) + {} + using result_type = uint64_t; + + static result_type min() { + return 0; + } + static result_type max() { + return std::numeric_limits::max(); + } + + result_type operator()() { + return rng_generate(m_key, m_counter); + } + + RNGCounter counter() const + { + return m_counter; + } + + private: + RNGKey m_key; + RNGCounter m_counter; + }; + /** * create study for graph of compartment models. * @param graph graph of parameters @@ -148,17 +179,12 @@ class ParameterStudy ensemble_result.reserve(m_num_runs); for (size_t run_idx = start_run_idx; run_idx < end_run_idx; run_idx++) { - //ensure reproducible results if the number of runs or MPI process changes. - //assumptions: - //- the sample_graph functor uses the RNG provided by our library - //- the RNG has been freshly seeded/initialized before this call - //- the seeds are identical on all MPI processes - //- the block size of the RNG is sufficiently big to cover one run - // (when in doubt, use a larger block size; fast-forwarding the RNG is cheap and the period length - // of the mt19937 RNG is huge) - mio::thread_local_rng().forward_to_block(run_idx); + auto rng = RandomNumberGenerator(m_rng_key, run_idx); + + log(LogLevel::info, "ParameterStudies: run {}", run_idx); + auto sim = create_sampled_simulation(sample_graph, rng); + log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", rng.counter().get()); - auto sim = create_sampled_simulation(sample_graph); sim.advance(m_tmax); ensemble_result.emplace_back(result_processing_function(std::move(sim).get_graph(), run_idx)); @@ -339,6 +365,8 @@ class ParameterStudy double m_dt_graph_sim; // adaptive time step of the integrator (will be corrected if too large/small) double m_dt_integration = 0.1; + // + RNGKey m_rng_key; }; } // namespace mio diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 3095f59bd1..3ffd8a651d 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -23,16 +23,51 @@ #include "memilio/utils/logging.h" #include "memilio/utils/miompi.h" #include "memilio/utils/span.h" +#include "memilio/utils/type_safe.h" #include #include #include #include #include +#include namespace mio { + template + struct RNGKey : TypeSafe>, OperatorComparison> + { + static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); + using TypeSafe>::TypeSafe; + }; + + template + struct RNGCounter : TypeSafe>, OperatorComparison>, OperatorAdditionSubtraction> + { + static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); + using TypeSafe>::TypeSafe; + }; + + template + uint64_t rng_generate(K key, C counter) + { + auto c = static_cast(counter.get()); + auto k = static_cast(key.get()); + return 0; //TODO: add rng + } + + template + RNGCounter rng_subsequence_counter(T subsequence_idx, C counter) + { + static_assert(sizeof(U) > sizeof(C), ""); + //TODO allow sizeof(C) = sizeof(U) and check actual values? + //TODO: assert value of subequence_idx + static const U bytes_shift = sizeof(U) - sizeof(C); + auto s = static_cast(subsequence_idx); + return RNGCounter{ (subsequence_idx << (bytes_shift * 8)) + counter }; + } + /** * Models a uniform_random_bit_generator. * Keeps track of its seeds so they can be logged or set. diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 3740509425..355afb433b 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -48,6 +48,38 @@ static constexpr uint32_t INVALID_PERSON_ID = std::numeric_limits::max class Person { public: + class RandomNumberGenerator + { + public: + using result_type = uint64_t; + + RandomNumberGenerator(RNGKey key, Person& person) + : m_key(key) + , m_counter(rng_subsequence_counter(person.get_person_id(), person.get_rng_counter())) + , m_person(person) + { + } + + result_type operator()() { + return rng_generate(m_key, m_counter); + } + + ~RandomNumberGenerator() + { + m_person.get_rng_counter() = m_counter; + } + + static constexpr result_type min() { return 0; } + static constexpr result_type max() { + return std::numeric_limits::max(); + } + + private: + RNGKey m_key; + RNGKey m_counter; + Person& m_person; + }; + /** * @brief Create a Person. * @param[in, out] location Initial location of the Person. @@ -388,6 +420,7 @@ class Person std::vector m_mask_compliance; ///< Vector of Mask compliance values for all #LocationType%s. uint32_t m_person_id; ///< Id of the Person. std::vector m_cells; ///< Vector with all Cell%s the Person visits at its current Location. + RNGCounter m_rng_counter; ///< counter for RandomNumberGenerator }; } // namespace abm diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index 58599390d6..ca37eba695 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -54,7 +54,7 @@ void Simulation::advance(TimePoint tmax) void Simulation::evolve_world(TimePoint tmax) { auto dt = std::min(m_dt, tmax - m_t); - m_world.evolve(m_t, dt); + m_world.evolve(m_t, dt, m_rng_key); m_t += m_dt; } diff --git a/cpp/models/abm/simulation.h b/cpp/models/abm/simulation.h index 902917c167..0268edf092 100644 --- a/cpp/models/abm/simulation.h +++ b/cpp/models/abm/simulation.h @@ -119,6 +119,7 @@ class Simulation TimeSeries m_result; ///< The result of the Simulation. TimePoint m_t; ///< The current TimePoint of the Simulation. TimeSpan m_dt; ///< The length of the time steps. + RNGKey m_rng_key; }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index c85e6f9bcd..d3de7b0486 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -49,10 +49,10 @@ Person& World::add_person(const LocationId id, AgeGroup age) return person; } -void World::evolve(TimePoint t, TimeSpan dt) +void World::evolve(TimePoint t, TimeSpan dt, RNGKey rng_key) { begin_step(t, dt); - interaction(t, dt); + interaction(t, dt, rng_key); m_testing_strategy.update_activity_status(t); migration(t, dt); end_step(t, dt); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index d237f49666..9aaaff1d56 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -87,7 +87,7 @@ class World * @param[in] t Current time. * @param[in] dt Length of the time step. */ - void evolve(TimePoint t, TimeSpan dt); + void evolve(TimePoint t, TimeSpan dt, RNGKey rng_key); /** * @brief Add a Location to the World. @@ -188,13 +188,13 @@ class World * @param[in] t The current TimePoint. * @param[in] dt The length of the time step of the Simulation. */ - void interaction(TimePoint t, TimeSpan dt); + void interaction(TimePoint t, TimeSpan dt, RNGKey rng_key); /** * @brief Person%s move in the World according to rules. * @param[in] t The current TimePoint. * @param[in] dt The length of the time step of the Simulation. */ - void migration(TimePoint t, TimeSpan dt); + void migration(TimePoint t, TimeSpan dt, RNGKey rng_key); std::vector> m_persons; ///< Vector with pointers to every Person. std::vector> m_locations; ///< Vector with pointers to every Location. From ceec6ea127ec11427773b711536e076cf18cb342 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 5 May 2023 19:00:59 +0200 Subject: [PATCH 003/105] include random123 library --- cpp/CMakeLists.txt | 2 +- cpp/memilio/CMakeLists.txt | 2 +- cpp/memilio/utils/random_number_generator.h | 101 ++++++++++++++------ cpp/models/abm/person.h | 54 ++++++++--- cpp/thirdparty/CMakeLists.txt | 19 ++++ 5 files changed, 135 insertions(+), 43 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e60ef8c60d..b571689a35 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -102,7 +102,7 @@ endif((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND(CMAKE_CXX_COMPILER_VERSION VERS # add flags to each target separately instead of globally so users have the choice to use their own flags if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS - "-Wno-unknown-warning;-Wno-pragmas;-Wall;-Wextra;-Werror;-Wshadow;--pedantic-errors;-Wno-deprecated-copy") + "-Wno-unknown-warning;-Wno-pragmas;-Wall;-Wextra;-Werror;-Wshadow;--pedantic-errors;-Wno-deprecated-copy;-Wno-expansion-to-defined") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS "-Wno-unknown-warning-option;-Wall;-Wextra;-Werror;-Wshadow;--pedantic-errors;-Wno-deprecated;-Wno-gnu-zero-variadic-macro-arguments") diff --git a/cpp/memilio/CMakeLists.txt b/cpp/memilio/CMakeLists.txt index dd3a9ed841..ca78439321 100644 --- a/cpp/memilio/CMakeLists.txt +++ b/cpp/memilio/CMakeLists.txt @@ -91,7 +91,7 @@ target_include_directories(memilio PUBLIC ) target_compile_features(memilio PUBLIC cxx_std_17) -target_link_libraries(memilio PUBLIC spdlog::spdlog Eigen3::Eigen Boost::boost Boost::filesystem Boost::disable_autolinking) +target_link_libraries(memilio PUBLIC spdlog::spdlog Eigen3::Eigen Boost::boost Boost::filesystem Boost::disable_autolinking Random123) target_compile_options(memilio PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 3ffd8a651d..fab75ccf87 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -20,12 +20,19 @@ #ifndef EPI_ABM_RANDOM_NUMBER_GENERATOR_H #define EPI_ABM_RANDOM_NUMBER_GENERATOR_H +#include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/logging.h" #include "memilio/utils/miompi.h" #include "memilio/utils/span.h" #include "memilio/utils/type_safe.h" +GCC_CLANG_DIAGNOSTIC(push) +GCC_CLANG_DIAGNOSTIC(ignored "-Wexpansion-to-defined") //Random123 handles the portability of this warning internally +#include "Random123/threefry.h" +GCC_CLANG_DIAGNOSTIC(pop) + #include +#include #include #include #include @@ -35,38 +42,74 @@ namespace mio { - template - struct RNGKey : TypeSafe>, OperatorComparison> - { - static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); - using TypeSafe>::TypeSafe; - }; +template +struct RNGKey : TypeSafe>, OperatorComparison> { + static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); + using TypeSafe>::TypeSafe; +}; - template - struct RNGCounter : TypeSafe>, OperatorComparison>, OperatorAdditionSubtraction> - { - static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); - using TypeSafe>::TypeSafe; - }; +template +struct RNGCounter : TypeSafe>, + OperatorComparison>, + OperatorAdditionSubtraction> { + static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); + using TypeSafe>::TypeSafe; +}; - template - uint64_t rng_generate(K key, C counter) - { - auto c = static_cast(counter.get()); - auto k = static_cast(key.get()); - return 0; //TODO: add rng - } +static uint64_t to_uint64(threefry2x32_ctr_t c) +{ + uint64_t i; + std::memcpy(&i, c.data(), sizeof(uint64_t)); + return i; +} - template - RNGCounter rng_subsequence_counter(T subsequence_idx, C counter) - { - static_assert(sizeof(U) > sizeof(C), ""); - //TODO allow sizeof(C) = sizeof(U) and check actual values? - //TODO: assert value of subequence_idx - static const U bytes_shift = sizeof(U) - sizeof(C); - auto s = static_cast(subsequence_idx); - return RNGCounter{ (subsequence_idx << (bytes_shift * 8)) + counter }; - } +static threefry2x32_ctr_t to_r123_ctr(uint64_t i) +{ + threefry2x32_ctr_t c; + std::memcpy(c.data(), &i, sizeof(uint64_t)); + return c; +} + +static threefry2x32_key_t to_r123_key(uint64_t i) +{ + threefry2x32_key_t c; + std::memcpy(c.data(), &i, sizeof(uint64_t)); + return c; +} + +template +uint64_t rng_generate(K key, C counter) +{ + auto c = static_cast(counter.get()); + auto k = static_cast(key.get()); + return to_uint64(threefry2x32(to_r123_key(k), to_r123_ctr(c))); +} + +template +RNGCounter rng_subsequence_counter(T subsequence_idx, C counter) +{ + static_assert(sizeof(U) > sizeof(C), ""); + //TODO allow sizeof(C) = sizeof(U) and check actual values? + //TODO: assert value of subequence_idx + + // + //high bits: subsequence idx + //low bits: subsequence counter + static const U bytes_shift = sizeof(U) - sizeof(C); + auto s = static_cast(subsequence_idx); + return RNGCounter{(s << (bytes_shift * 8)) + static_cast(counter.get())}; +} + +template +RNGCounter rng_subsequence_counter(C counter) +{ + static_assert(sizeof(U) < sizeof(C), ""); + //TODO allow sizeof(C) = sizeof(U) and check actual values? + //TODO: assert value of subequence_idx + + //truncate to get the subsequence counter + return RNGCounter(static_cast(counter.get())); +} /** * Models a uniform_random_bit_generator. diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 355afb433b..87650d495c 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -28,6 +28,7 @@ #include "abm/vaccine.h" #include "abm/mask_type.h" #include "abm/mask.h" +#include "memilio/utils/random_number_generator.h" #include "memilio/utils/memory.h" #include @@ -48,36 +49,60 @@ static constexpr uint32_t INVALID_PERSON_ID = std::numeric_limits::max class Person { public: + /** + * Random number generator of individual persons. + * Copies the rng counter from a person on construction and updates it on destruction. + * For each person, only one generator must be active at the same time. + * Satisifes the requirments of UniformRandomBitGenerator. + */ class RandomNumberGenerator { public: using result_type = uint64_t; - RandomNumberGenerator(RNGKey key, Person& person) - : m_key(key) - , m_counter(rng_subsequence_counter(person.get_person_id(), person.get_rng_counter())) - , m_person(person) + RandomNumberGenerator(RNGKey key, Person& person) + : m_person(person) + , m_key(key) + , m_counter(rng_subsequence_counter(person.get_person_id(), person.rng_counter())) +#ifndef NDEBUG + , m_initial_counter(m_counter) +#endif { } - result_type operator()() { - return rng_generate(m_key, m_counter); + ~RandomNumberGenerator() + { + //store the updated counter after the RNG is done + assert(m_person.rng_counter() == m_initial_counter && "Inconsistent RNG Counter. Make sure there is only one active per person."); + auto subcounter = rng_subsequence_counter(m_counter); + m_person.rng_counter() = subcounter; } - ~RandomNumberGenerator() + result_type operator()() { - m_person.get_rng_counter() = m_counter; + //generate sample and increment the counter + auto r = rng_generate(m_key, m_counter); + m_counter++; + return r; } - static constexpr result_type min() { return 0; } - static constexpr result_type max() { + static constexpr result_type min() + { + return 0; + } + + static constexpr result_type max() + { return std::numeric_limits::max(); } private: - RNGKey m_key; - RNGKey m_counter; Person& m_person; + RNGKey m_key; + RNGCounter m_counter; +#ifndef NDEBUG + RNGCounter m_initial_counter; //for sanity checks during debugging +#endif }; /** @@ -401,6 +426,11 @@ class Person return 1.; // put implementation in .cpp } + RNGCounter& rng_counter() + { + return m_rng_counter; + } + private: observer_ptr m_location; ///< Current Location of the Person. std::vector m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the diff --git a/cpp/thirdparty/CMakeLists.txt b/cpp/thirdparty/CMakeLists.txt index c77d1f8a3d..1dca677548 100644 --- a/cpp/thirdparty/CMakeLists.txt +++ b/cpp/thirdparty/CMakeLists.txt @@ -3,6 +3,7 @@ set(MEMILIO_EIGEN_VERSION "3.3.9") set(MEMILIO_SPDLOG_VERSION "1.11.0") set(MEMILIO_JSONCPP_VERSION "1.9.5") +set(MEMILIO_RANDOM123_VERSION "v1.14.0") # ## SPDLOG set(SPDLOG_INSTALL ON) @@ -116,3 +117,21 @@ endif() if (MEMILIO_ENABLE_OPENMP) find_package(OpenMP COMPONENTS CXX REQUIRED) endif() + +#Random123 library for random number generators +message(STATUS "Downloading Random123 library") + +include(FetchContent) +FetchContent_Declare(Random123 + GIT_REPOSITORY https://github.com/DEShawResearch/random123 + GIT_TAG ${MEMILIO_RANDOM123_VERSION} + CONFIGURE_COMMAND "" + BUILD_COMMAND "") +FetchContent_GetProperties(Random123) + +if(NOT Random123_POPULATED) + FetchContent_Populate(Random123) +endif() + +add_library(Random123 INTERFACE) +target_include_directories(Random123 INTERFACE ${random123_SOURCE_DIR}/include) From 51dba61c0405ddf5079a0e39a48c81f07448bc44 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 09:48:40 +0200 Subject: [PATCH 004/105] rng for parameter studies --- cpp/memilio/compartments/parameter_studies.h | 57 +++--- cpp/memilio/utils/random_number_generator.h | 165 ++++++++---------- .../2020_npis_sarscov2_wildtype_germany.cpp | 2 +- ...021_vaccination_sarscov2_delta_germany.cpp | 2 +- 4 files changed, 94 insertions(+), 132 deletions(-) diff --git a/cpp/memilio/compartments/parameter_studies.h b/cpp/memilio/compartments/parameter_studies.h index ff1edbe0ac..896156756f 100644 --- a/cpp/memilio/compartments/parameter_studies.h +++ b/cpp/memilio/compartments/parameter_studies.h @@ -61,36 +61,6 @@ class ParameterStudy */ using SimulationGraph = mio::Graph, mio::MigrationEdge>; - struct RandomNumberGenerator - { - public: - RandomNumberGenerator(RNGKey key, size_t run_idx) - : m_key(key) - , m_counter(rng_subsequence_counter(run_idx, RNGCounter(0))) - {} - using result_type = uint64_t; - - static result_type min() { - return 0; - } - static result_type max() { - return std::numeric_limits::max(); - } - - result_type operator()() { - return rng_generate(m_key, m_counter); - } - - RNGCounter counter() const - { - return m_counter; - } - - private: - RNGKey m_key; - RNGCounter m_counter; - }; - /** * create study for graph of compartment models. * @param graph graph of parameters @@ -169,7 +139,8 @@ class ParameterStudy #else num_procs = 1; rank = 0; -#endif +#endif + m_rng.synchronize(); auto run_distribution = distribute_runs(m_num_runs, num_procs); auto start_run_idx = std::accumulate(run_distribution.begin(), run_distribution.begin() + size_t(rank), size_t(0)); @@ -179,14 +150,24 @@ class ParameterStudy ensemble_result.reserve(m_num_runs); for (size_t run_idx = start_run_idx; run_idx < end_run_idx; run_idx++) { - auto rng = RandomNumberGenerator(m_rng_key, run_idx); - + //prepare rng for this run + //assume that sampling the graph uses the thread local rng and isn't multithreaded + auto initial_rng_counter = + rng_subsequence_counter(static_cast(run_idx), RNGCounter(0)); + m_rng.set_counter(initial_rng_counter); + thread_local_rng() = m_rng; + + //sample log(LogLevel::info, "ParameterStudies: run {}", run_idx); - auto sim = create_sampled_simulation(sample_graph, rng); - log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", rng.counter().get()); + auto sim = create_sampled_simulation(sample_graph); + m_rng = thread_local_rng(); + log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", (m_rng.get_counter() - initial_rng_counter).get()); + + //perform run sim.advance(m_tmax); + //handle result and store ensemble_result.emplace_back(result_processing_function(std::move(sim).get_graph(), run_idx)); } @@ -319,6 +300,10 @@ class ParameterStudy } /** @} */ + RandomNumberGenerator& get_rng() { + return m_rng; + } + private: //sample parameters and create simulation template @@ -366,7 +351,7 @@ class ParameterStudy // adaptive time step of the integrator (will be corrected if too large/small) double m_dt_integration = 0.1; // - RNGKey m_rng_key; + RandomNumberGenerator m_rng; }; } // namespace mio diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index fab75ccf87..8371136c1a 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -28,19 +28,36 @@ GCC_CLANG_DIAGNOSTIC(push) GCC_CLANG_DIAGNOSTIC(ignored "-Wexpansion-to-defined") //Random123 handles the portability of this warning internally +#include "Random123/array.h" #include "Random123/threefry.h" GCC_CLANG_DIAGNOSTIC(pop) #include #include #include +#include #include #include -#include #include namespace mio { +namespace details +{ +static uint64_t to_uint64(r123array2x32 tf_array) +{ + uint64_t i; + std::memcpy(&i, tf_array.data(), sizeof(uint64_t)); + return i; +} + +static r123array2x32 to_r123_array(uint64_t i) +{ + threefry2x32_ctr_t c; + std::memcpy(c.data(), &i, sizeof(uint64_t)); + return c; +} +} template struct RNGKey : TypeSafe>, OperatorComparison> { @@ -56,33 +73,12 @@ struct RNGCounter : TypeSafe>, using TypeSafe>::TypeSafe; }; -static uint64_t to_uint64(threefry2x32_ctr_t c) -{ - uint64_t i; - std::memcpy(&i, c.data(), sizeof(uint64_t)); - return i; -} - -static threefry2x32_ctr_t to_r123_ctr(uint64_t i) -{ - threefry2x32_ctr_t c; - std::memcpy(c.data(), &i, sizeof(uint64_t)); - return c; -} - -static threefry2x32_key_t to_r123_key(uint64_t i) -{ - threefry2x32_key_t c; - std::memcpy(c.data(), &i, sizeof(uint64_t)); - return c; -} - template uint64_t rng_generate(K key, C counter) { auto c = static_cast(counter.get()); auto k = static_cast(key.get()); - return to_uint64(threefry2x32(to_r123_key(k), to_r123_ctr(c))); + return details::to_uint64(threefry2x32(details::to_r123_array(k), details::to_r123_array(c))); } template @@ -111,68 +107,79 @@ RNGCounter rng_subsequence_counter(C counter) return RNGCounter(static_cast(counter.get())); } -/** - * Models a uniform_random_bit_generator. - * Keeps track of its seeds so they can be logged or set. - * The generated sequence can be segmented into blocks that can help to reproduce - * simulations involving random numbers by reliably generating a specific part - * of the sequence or to assign each thread/process a different sequence. - * @see thread_local_rng for a static instance. - */ -class RandomNumberGenerator +template +RNGKey seed_rng_key(SeedSeq& seed_seq) +{ + auto tf_key = threefry2x32_key_t::seed(seed_seq); + return RNGKey(details::to_uint64(tf_key)); +} + +template +class RandomNumberGeneratorBase { public: - using result_type = std::mt19937_64::result_type; + using result_type = uint64_t; static constexpr result_type min() { - return std::mt19937_64::min(); + return std::numeric_limits::min(); } static constexpr result_type max() { - return std::mt19937_64::max(); + return std::numeric_limits::max(); } result_type operator()() { - ++m_num_generated; - return m_rng(); + auto self = static_cast(this); + auto r = rng_generate(self->get_key(), self->get_counter()); + self->increment_counter(); + return r; } +}; - static std::vector generate_seeds() +class RandomNumberGenerator : RandomNumberGeneratorBase +{ +public: + RandomNumberGenerator() + : m_counter(0) { - std::random_device rd; - return {rd(), rd(), rd(), rd(), rd(), rd()}; + seed(generate_seeds()); } - RandomNumberGenerator() - : m_seeds(generate_seeds()) - { - std::seed_seq sseq(m_seeds.begin(), m_seeds.end()); - m_rng.seed(sseq); + RNGKey get_key() const { + return m_key; + } + RNGCounter get_counter() const { + return m_counter; + } + void set_counter(RNGCounter counter) { + m_counter = counter; } - std::vector get_seeds() const + void increment_counter() { + ++m_counter; + } + static std::vector generate_seeds() { - return m_seeds; + std::random_device rd; + return {rd(), rd(), rd(), rd(), rd(), rd()}; } - /** - * Seed this random number generator. - * Starts a new random sequence at block 0. - * @param seeds at least one seed, e.g., generated using std::random_device. More seeds increase quality. - */ - void seed(const std::vector& seeds) + void seed(const std::vector& seeds) { - assert(seeds.size() > 0); m_seeds = seeds; - std::seed_seq sseq(m_seeds.begin(), m_seeds.end()); - m_rng.seed(sseq); - m_num_generated = 0; + std::seed_seq seed_seq(m_seeds.begin(), m_seeds.end()); + m_key = seed_rng_key(seed_seq); + } + + const std::vector get_seeds() const + { + return m_seeds; } /** * Set the seeds in all MPI processes the same as in the root. */ - void synchronize_seeds() + void synchronize() { #ifdef MEMILIO_ENABLE_MPI int rank; @@ -186,46 +193,16 @@ class RandomNumberGenerator m_seeds.assign(num_seeds, 0); } MPI_Bcast(m_seeds.data(), num_seeds, MPI_UNSIGNED, 0, mpi::get_world()); + if (rank != 0) { + seed(m_seeds); + } #endif } - /** - * Get/Set the the size of blocks of the generated sequence. - * This affects the number of samples skipped by forward_to_block(). - * Skipping samples is an O(n) operation, where n is the number of skipped samples, so choose the block size with care. - * @{ - */ - void set_block_size(size_t block_size) - { - m_block_size = block_size; - } - size_t get_block_size() const - { - return m_block_size; - } - /**@}*/ - - /** - * Forward to block index i. - * Skips numbers in the generated sequence up to the beginning of the block. - * The next number generated will be element block_size * i of the random sequence. - * This operation is O(n), where n is the number of skipped samples, so choose the block size with care. - * @param i block index. May not be a block that is already passed or started. - */ - void forward_to_block(size_t block_idx) - { - assert(block_idx * m_block_size >= m_num_generated && - "Can't forward to a previous block or one that is started."); - auto num_remaining = m_block_size * block_idx - m_num_generated; - m_rng.discard(num_remaining); - m_num_generated += num_remaining; - } - private: - std::vector m_seeds; - std::mt19937_64 m_rng; - size_t m_block_size = 1'000'000; ///< number of samples in a block, will be skipped by forward_to_block - size_t m_num_generated = 0; ///< number of samples generated (or skipped) since the last seed + RNGKey m_key; + RNGCounter m_counter; + std::vector m_seeds; }; /** diff --git a/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp b/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp index 3c77f99a62..c90fecf8a0 100644 --- a/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp +++ b/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp @@ -654,7 +654,7 @@ int main(int argc, char** argv) // mio::thread_local_rng().seed( // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging - mio::thread_local_rng().synchronize_seeds(); + mio::thread_local_rng().synchronize(); if (mio::mpi::is_root()) { printf("Seeds: "); for (auto s : mio::thread_local_rng().get_seeds()) { diff --git a/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp b/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp index 68888019a7..30fd8a8e4b 100644 --- a/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp +++ b/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp @@ -812,7 +812,7 @@ int main(int argc, char** argv) //mio::thread_local_rng().seed( // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging - mio::thread_local_rng().synchronize_seeds(); + mio::thread_local_rng().synchronize(); if (mio::mpi::is_root()) { printf("Seeds: "); for (auto s : mio::thread_local_rng().get_seeds()) { From 989d4f8a1ca2a505ade34add027a70485d4e2fbe Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 09:50:52 +0200 Subject: [PATCH 005/105] ctr based rng and omp for ABM --- cpp/models/abm/location.cpp | 6 +++++- cpp/models/abm/location.h | 8 ++++---- cpp/models/abm/person.h | 35 ++++++++------------------------ cpp/models/abm/simulation.cpp | 3 +++ cpp/models/abm/simulation.h | 7 ++++++- cpp/models/abm/world.cpp | 38 +++++++++++++++++++++-------------- cpp/models/abm/world.h | 7 ++++--- 7 files changed, 53 insertions(+), 51 deletions(-) diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index ecfeee942e..ed38c86735 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -23,6 +23,7 @@ #include "memilio/utils/random_number_generator.h" #include "abm/random_events.h" +#include #include namespace mio @@ -112,6 +113,7 @@ void Location::cache_exposure_rates(TimePoint t, TimeSpan dt) void Location::add_person(Person& p, std::vector cells) { + std::lock_guard lk(m_mut); m_persons.push_back(&p); for (uint32_t cell_idx : cells) m_cells[cell_idx].m_persons.push_back(&p); @@ -119,13 +121,14 @@ void Location::add_person(Person& p, std::vector cells) void Location::remove_person(Person& p) { + std::lock_guard lk(m_mut); m_persons.erase(std::remove(m_persons.begin(), m_persons.end(), &p), m_persons.end()); for (auto&& cell : m_cells) { cell.m_persons.erase(std::remove(cell.m_persons.begin(), cell.m_persons.end(), &p), cell.m_persons.end()); } } -size_t Location::get_number_persons() +size_t Location::get_number_persons() const { return m_persons.size(); } @@ -178,6 +181,7 @@ void Location::initialize_subpopulations(const TimePoint t) } } } + const TimeSeries& Location::get_subpopulations() const { return m_subpopulations; diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index d10d3f867c..8a4398cf78 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -232,7 +232,7 @@ class Location * @param[in] cell_idx Cell index of interest. * @return Air exposure rate in the Cell. */ - CustomIndexArray get_cached_exposure_rate_contacts(uint32_t cell_idx) + CustomIndexArray get_cached_exposure_rate_contacts(uint32_t cell_idx) const { return m_cells[cell_idx].m_cached_exposure_rate_contacts; } @@ -242,7 +242,7 @@ class Location * @param[in] cell_idx Cell index of interest. * @return Contact exposure rate in the cell. */ - CustomIndexArray get_cached_exposure_rate_air(uint32_t cell_idx) + CustomIndexArray get_cached_exposure_rate_air(uint32_t cell_idx) const { return m_cells[cell_idx].m_cached_exposure_rate_air; } @@ -264,7 +264,7 @@ class Location * @param[in] cell_idx The index of the Cell. * @return The CellCapacity of the Cell. */ - CellCapacity get_capacity(uint32_t cell_idx = 0) + CellCapacity get_capacity(uint32_t cell_idx = 0) const { return m_cells[cell_idx].m_capacity; } @@ -302,7 +302,7 @@ class Location * @brief Get the total number of Person%s at the Location. * @return Number of Person%s. */ - size_t get_number_persons(); + size_t get_number_persons() const; /** * @brief Get the number of Person%s of a particular #InfectionState for all Cell%s. diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 87650d495c..1dcaf0ed6f 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -55,7 +55,7 @@ class Person * For each person, only one generator must be active at the same time. * Satisifes the requirments of UniformRandomBitGenerator. */ - class RandomNumberGenerator + class RandomNumberGenerator : RandomNumberGeneratorBase { public: using result_type = uint64_t; @@ -63,46 +63,27 @@ class Person RandomNumberGenerator(RNGKey key, Person& person) : m_person(person) , m_key(key) - , m_counter(rng_subsequence_counter(person.get_person_id(), person.rng_counter())) -#ifndef NDEBUG - , m_initial_counter(m_counter) -#endif { } - ~RandomNumberGenerator() + RNGKey get_key() const { - //store the updated counter after the RNG is done - assert(m_person.rng_counter() == m_initial_counter && "Inconsistent RNG Counter. Make sure there is only one active per person."); - auto subcounter = rng_subsequence_counter(m_counter); - m_person.rng_counter() = subcounter; + return m_key; } - result_type operator()() + RNGCounter get_counter() const { - //generate sample and increment the counter - auto r = rng_generate(m_key, m_counter); - m_counter++; - return r; + return rng_subsequence_counter(m_person.get_person_id(), m_person.get_rng_counter()); } - static constexpr result_type min() + void increment_counter() { - return 0; - } - - static constexpr result_type max() - { - return std::numeric_limits::max(); + ++m_person.get_rng_counter(); } private: Person& m_person; RNGKey m_key; - RNGCounter m_counter; -#ifndef NDEBUG - RNGCounter m_initial_counter; //for sanity checks during debugging -#endif }; /** @@ -426,7 +407,7 @@ class Person return 1.; // put implementation in .cpp } - RNGCounter& rng_counter() + RNGCounter& get_rng_counter() { return m_rng_counter; } diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index ca37eba695..35864f6d96 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -18,6 +18,8 @@ * limitations under the License. */ #include "abm/simulation.h" +#include "memilio/utils/random_number_generator.h" +#include namespace mio { @@ -29,6 +31,7 @@ Simulation::Simulation(TimePoint t, World&& world) , m_result(Eigen::Index(InfectionState::Count)) , m_t(t) , m_dt(hours(1)) + , m_rng() { initialize_locations(m_t); } diff --git a/cpp/models/abm/simulation.h b/cpp/models/abm/simulation.h index 0268edf092..db4c836226 100644 --- a/cpp/models/abm/simulation.h +++ b/cpp/models/abm/simulation.h @@ -22,6 +22,7 @@ #include "abm/world.h" #include "abm/time.h" +#include "memilio/utils/random_number_generator.h" #include "memilio/utils/time_series.h" #include "memilio/io/history.h" @@ -110,6 +111,10 @@ class Simulation return m_world; } + RandomNumberGenerator& get_rng() { + return m_rng; + } + private: void initialize_locations(TimePoint t); void store_result_at(TimePoint t); @@ -119,7 +124,7 @@ class Simulation TimeSeries m_result; ///< The result of the Simulation. TimePoint m_t; ///< The current TimePoint of the Simulation. TimeSpan m_dt; ///< The length of the time steps. - RNGKey m_rng_key; + RandomNumberGenerator m_rng; }; } // namespace abm diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index d3de7b0486..c23ae88177 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -49,23 +49,25 @@ Person& World::add_person(const LocationId id, AgeGroup age) return person; } -void World::evolve(TimePoint t, TimeSpan dt, RNGKey rng_key) +void World::evolve(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) { begin_step(t, dt); - interaction(t, dt, rng_key); + interaction(t, dt, rng); m_testing_strategy.update_activity_status(t); - migration(t, dt); + migration(t, dt, rng); end_step(t, dt); } -void World::interaction(TimePoint t, TimeSpan dt) +void World::interaction(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) { - for (auto&& person : m_persons) { - person->interact(t, dt, m_infection_parameters); + #pragma omp parallel for + for (auto i = size_t(0); i < m_persons.size(); ++i) { + auto&& person = m_persons[i]; + person->interact(t, dt, m_infection_parameters, rng); } } -void World::migration(TimePoint t, TimeSpan dt) +void World::migration(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) { std::vector>> @@ -85,12 +87,14 @@ void World::migration(TimePoint t, TimeSpan dt) m_enhanced_migration_rules.push_back(rule); } } - for (auto& person : m_persons) { - for (auto rule : m_enhanced_migration_rules) { + #pragma omp parallel for + for (auto i = size_t(0); i < m_persons.size(); ++i) { + auto&& person = m_persons[i]; + for (auto rule : m_migration_rules) { //check if transition rule can be applied - auto target_type = rule.first(*person, t, dt, m_migration_parameters); - auto& target_location = find_location(target_type, *person); - auto current_location = person->get_location(); + auto target_type = rule.first(*person, t, dt, m_migration_parameters); + auto& target_location = find_location(target_type, *person); + auto& current_location = person->get_location(); if (m_testing_strategy.run_strategy(*person, target_location, t)) { if (target_location != current_location && target_location.get_number_persons() < target_location.get_capacity().persons) { @@ -109,7 +113,7 @@ void World::migration(TimePoint t, TimeSpan dt) while (m_trip_list.get_current_index() < num_trips && m_trip_list.get_next_trip_time() < t + dt) { auto& trip = m_trip_list.get_next_trip(); auto& person = m_persons[trip.person_id]; - auto current_location = person->get_location(); + auto& current_location = person->get_location(); if (!person->is_in_quarantine() && person->get_infection_state(t) != InfectionState::Dead && current_location == get_individualized_location(trip.migration_origin)) { auto& target_location = get_individualized_location(trip.migration_destination); @@ -125,14 +129,18 @@ void World::migration(TimePoint t, TimeSpan dt) void World::begin_step(TimePoint t, TimeSpan dt) { - for (auto& location : m_locations) { + #pragma omp parallel for + for (auto i = size_t(0); i < m_locations.size(); ++i) { + auto&& location = m_locations[i]; location->cache_exposure_rates(t, dt); } } void World::end_step(TimePoint t, TimeSpan dt) { - for (auto& location : m_locations) { + #pragma omp parallel for + for (auto i = size_t(0); i < m_locations.size(); ++i) { + auto&& location = m_locations[i]; location->store_subpopulations(t + dt); } } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 9aaaff1d56..67d7a7c2bc 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -27,6 +27,7 @@ #include "abm/trip_list.h" #include "abm/testing_strategy.h" #include "memilio/utils/pointer_dereferencing_iterator.h" +#include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" #include @@ -87,7 +88,7 @@ class World * @param[in] t Current time. * @param[in] dt Length of the time step. */ - void evolve(TimePoint t, TimeSpan dt, RNGKey rng_key); + void evolve(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng); /** * @brief Add a Location to the World. @@ -188,13 +189,13 @@ class World * @param[in] t The current TimePoint. * @param[in] dt The length of the time step of the Simulation. */ - void interaction(TimePoint t, TimeSpan dt, RNGKey rng_key); + void interaction(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng); /** * @brief Person%s move in the World according to rules. * @param[in] t The current TimePoint. * @param[in] dt The length of the time step of the Simulation. */ - void migration(TimePoint t, TimeSpan dt, RNGKey rng_key); + void migration(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng); std::vector> m_persons; ///< Vector with pointers to every Person. std::vector> m_locations; ///< Vector with pointers to every Location. From 0f2f6f21da803d12c0d5e8f6a77476ed0e87b66e Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 10:32:04 +0200 Subject: [PATCH 006/105] global distribution adapter instances enable multithreaded tests --- cpp/memilio/utils/random_number_generator.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 8371136c1a..d1369aaad2 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -278,9 +278,11 @@ class DistributionAdapter */ using GeneratorFunction = std::function; +private: /** * the default generator function invokes an instance of the template parameter * with a static thread local RNG engine. + * Constructors are private, use get_instance to get the current version. */ DistributionAdapter() { @@ -288,7 +290,12 @@ class DistributionAdapter return DistT(params)(thread_local_rng()); }; } + DistributionAdapter(const DistributionAdapter&) = default; + DistributionAdapter& operator=(const DistributionAdapter&) = default; + DistributionAdapter(DistributionAdapter&&) = default; + DistributionAdapter& operator=(DistributionAdapter&&) = default; +public: /** * get a random sample from the distribution. * accepts the same arguments as the constructors of the template parameter type. @@ -324,7 +331,7 @@ class DistributionAdapter */ static DistributionAdapter& get_instance() { - static thread_local DistributionAdapter instance; + static DistributionAdapter instance; return instance; } From 8d5a88dea7619eba09a71c0df865a942d80c2ce8 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 15:38:25 +0200 Subject: [PATCH 007/105] fix warnings in examples --- cpp/examples/CMakeLists.txt | 21 +++++++++++++++++-- cpp/examples/adapt_rk_test.cpp | 8 +++---- cpp/examples/euler_test.cpp | 2 +- cpp/examples/graph.cpp | 2 +- cpp/examples/ode_secir_parameter_sampling.cpp | 3 --- cpp/examples/ode_secir_read_graph.cpp | 1 - cpp/examples/ode_secir_save_results.cpp | 3 +-- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index af05ef64cb..d661cc8bf6 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -4,68 +4,85 @@ configure_file(data_dir.h.in data_dir.h) add_executable(euler_example euler_test.cpp) target_link_libraries(euler_example PRIVATE memilio) +target_compile_options(euler_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ode_secir_parameter_sampling_example ode_secir_parameter_sampling.cpp) target_link_libraries(ode_secir_parameter_sampling_example PRIVATE memilio ode_secir) +target_compile_options(ode_secir_parameter_sampling_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(adapt_rk_example adapt_rk_test.cpp) target_link_libraries(adapt_rk_example PRIVATE memilio) +target_compile_options(adapt_rk_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ode_seir_example ode_seir.cpp) target_link_libraries(ode_seir_example PRIVATE memilio ode_seir) +target_compile_options(ode_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ode_secir_example ode_secir.cpp) target_link_libraries(ode_secir_example PRIVATE memilio ode_secir) +target_compile_options(ode_secir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ode_secirvvs_example ode_secirvvs.cpp) target_link_libraries(ode_secirvvs_example PRIVATE memilio ode_secirvvs) +target_compile_options(ode_secirvvs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ode_secir_ageres_example ode_secir_ageres.cpp) target_link_libraries(ode_secir_ageres_example PRIVATE memilio ode_secir) add_executable(graph_example graph.cpp) target_link_libraries(graph_example PRIVATE memilio ode_seir) +target_compile_options(graph_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(graph_stochastic_mobility_example graph_stochastic_mobility.cpp) target_link_libraries(graph_stochastic_mobility_example PRIVATE memilio ode_secir) +target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(abm_minimal_example abm_minimal.cpp) target_link_libraries(abm_minimal_example PRIVATE memilio abm) +target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(abm_history_example abm_history_object.cpp) target_link_libraries(abm_history_example PRIVATE memilio abm) +target_compile_options(abm_history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(twitter_migration_example twitter_migration.cpp) target_link_libraries(twitter_migration_example PRIVATE memilio ode_secir) add_executable(ide_seir_example ide_seir.cpp) target_link_libraries(ide_seir_example PRIVATE memilio ide_seir) +target_compile_options(twitter_migration_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ide_secir_example ide_secir.cpp) target_link_libraries(ide_secir_example PRIVATE memilio ide_secir) +target_compile_options(ide_secir_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) target_include_directories(ode_secir_read_graph_example PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # configured headers + target_compile_options(ode_secir_read_graph_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() if(MEMILIO_HAS_HDF5 AND MEMILIO_HAS_JSONCPP) add_executable(ode_secir_parameter_study_example ode_secir_parameter_study.cpp) target_link_libraries(ode_secir_parameter_study_example PRIVATE memilio ode_secir) + target_compile_options(ode_secir_parameter_study_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() if(MEMILIO_HAS_JSONCPP) add_executable(cli_example cli.cpp) target_link_libraries(cli_example PRIVATE memilio) + target_compile_options(cli_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() if(MEMILIO_HAS_JSONCPP) add_executable(serialize_example serialize.cpp) target_link_libraries(serialize_example PRIVATE memilio) + target_compile_options(serialize_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() if(MEMILIO_HAS_HDF5) - add_executable(ode_secir_save_results ode_secir_save_results.cpp) - target_link_libraries(ode_secir_save_results PRIVATE memilio ode_secir) + add_executable(ode_secir_save_results_example ode_secir_save_results.cpp) + target_link_libraries(ode_secir_save_results_example PRIVATE memilio ode_secir) + target_compile_options(ode_secir_save_results_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) endif() diff --git a/cpp/examples/adapt_rk_test.cpp b/cpp/examples/adapt_rk_test.cpp index d332ca6966..97c0195bd1 100644 --- a/cpp/examples/adapt_rk_test.cpp +++ b/cpp/examples/adapt_rk_test.cpp @@ -33,10 +33,10 @@ void init_vectors(std::vector& y, std::vector& } // Test for y'(t) = cos(t) -void integration_test(std::vector& y, std::vector& sol, size_t& n, double t, +void integration_test(std::vector& y, std::vector& sol, size_t& n, double t0, double dt, const double tmax, double& err) { - auto sine_deriv = [](auto&& y, auto&& t, auto&& dydt) { + auto sine_deriv = [](auto&& /*y*/, auto&& t, auto&& dydt) { dydt[0] = std::cos(t); }; @@ -50,7 +50,7 @@ void integration_test(std::vector& y, std::vector f = std::vector(1, 0); size_t i = 0; - double t_eval = t; + double t_eval = t0; // printf("\n t: %.8f\t sol %.8f\t rkf %.8f", t, sol[0][0], y[0][0]); while (t_eval - tmax < 1e-10) { @@ -60,8 +60,6 @@ void integration_test(std::vector& y, std::vector& y, std::vector -int main(int argc, char** argv) +int main() { const auto t0 = 0.; const auto tmax = 10.; diff --git a/cpp/examples/ode_secir_parameter_sampling.cpp b/cpp/examples/ode_secir_parameter_sampling.cpp index 6cfdcb0b02..9c70d05d7b 100644 --- a/cpp/examples/ode_secir_parameter_sampling.cpp +++ b/cpp/examples/ode_secir_parameter_sampling.cpp @@ -81,9 +81,6 @@ int main() mio::ContactMatrix(Eigen::MatrixXd::Constant((size_t)nb_groups, (size_t)nb_groups, 0.5))}; params.get() = cm_group; - double t0 = 0; - double tmax = 100; - params.get().get_dampings().push_back(mio::DampingSampling( mio::UncertainValue(0.5), mio::DampingLevel(0), mio::DampingType(0), mio::SimulationTime(30.), std::vector(1, size_t(0)), Eigen::VectorXd::Constant(Eigen::Index(nb_groups.get()), 1.0))); diff --git a/cpp/examples/ode_secir_read_graph.cpp b/cpp/examples/ode_secir_read_graph.cpp index 0fb162d6dc..0194fd382c 100644 --- a/cpp/examples/ode_secir_read_graph.cpp +++ b/cpp/examples/ode_secir_read_graph.cpp @@ -55,7 +55,6 @@ int main(int argc, char** argv) const auto t0 = 0.; const auto tmax = 10.; - const auto dt = 1.; //time step of migration, not integration double cont_freq = 10; // see Polymod study diff --git a/cpp/examples/ode_secir_save_results.cpp b/cpp/examples/ode_secir_save_results.cpp index d1e8fccabe..f6fdc8e1c6 100644 --- a/cpp/examples/ode_secir_save_results.cpp +++ b/cpp/examples/ode_secir_save_results.cpp @@ -22,7 +22,7 @@ #include -int main(int argc, char** argv) +int main() { const auto t0 = 0.; @@ -71,7 +71,6 @@ int main(int argc, char** argv) } params.apply_constraints(); - auto num_groups = (int)(size_t)params.get_num_groups(); mio::ContactMatrixGroup& contact_matrix = params.get(); contact_matrix[0] = From efb2c8e2fb0ae32a24735a5b4c772db4f9a2019f Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 15:41:12 +0200 Subject: [PATCH 008/105] change mocking of rng --- cpp/memilio/utils/random_number_generator.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index d1369aaad2..9f5c043dbf 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -137,7 +137,7 @@ class RandomNumberGeneratorBase } }; -class RandomNumberGenerator : RandomNumberGeneratorBase +class RandomNumberGenerator : public RandomNumberGeneratorBase { public: RandomNumberGenerator() @@ -284,12 +284,7 @@ class DistributionAdapter * with a static thread local RNG engine. * Constructors are private, use get_instance to get the current version. */ - DistributionAdapter() - { - m_generator = [](auto&& params) { - return DistT(params)(thread_local_rng()); - }; - } + DistributionAdapter() = default; DistributionAdapter(const DistributionAdapter&) = default; DistributionAdapter& operator=(const DistributionAdapter&) = default; DistributionAdapter(DistributionAdapter&&) = default; @@ -303,10 +298,15 @@ class DistributionAdapter * std::uniform_int_distribution is constructed from two integers, so * DistributionAdapter::operator() accepts two integers as well. */ - template - ResultType operator()(T&&... params) + template + ResultType operator()(RNG& rng, T&&... params) { - return m_generator(typename DistT::param_type{std::forward(params)...}); + if (m_generator) { + //unlikely outside of tests + return m_generator(typename DistT::param_type{std::forward(params)...}); + } else { + return DistT(std::forward(params)...)(rng); + } } /** From 252c6526e04b99a1b0540f3e7854558f08f995d8 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 16:10:31 +0200 Subject: [PATCH 009/105] full openmp and rng for ABM --- cpp/examples/abm_minimal.cpp | 3 +- cpp/models/abm/household.cpp | 10 +- cpp/models/abm/household.h | 2 +- cpp/models/abm/infection.cpp | 52 ++-- cpp/models/abm/infection.h | 18 +- cpp/models/abm/location.cpp | 11 +- cpp/models/abm/location.h | 4 +- cpp/models/abm/migration_rules.cpp | 36 +-- cpp/models/abm/migration_rules.h | 36 ++- cpp/models/abm/person.cpp | 34 ++- cpp/models/abm/person.h | 63 +++-- cpp/models/abm/random_events.h | 10 +- cpp/models/abm/simulation.cpp | 3 +- cpp/models/abm/simulation.h | 5 - cpp/models/abm/testing_strategy.cpp | 25 +- cpp/models/abm/testing_strategy.h | 7 +- cpp/models/abm/world.cpp | 31 ++- cpp/models/abm/world.h | 17 +- cpp/simulations/abm.cpp | 13 +- cpp/tests/CMakeLists.txt | 6 - cpp/tests/abm_helpers.cpp | 11 +- cpp/tests/test_abm_infection.cpp | 13 +- cpp/tests/test_abm_location.cpp | 133 +++++----- cpp/tests/test_abm_lockdown_rules.cpp | 78 ++++-- cpp/tests/test_abm_masks.cpp | 21 +- cpp/tests/test_abm_migration_rules.cpp | 281 +++++++++++++-------- cpp/tests/test_abm_person.cpp | 119 ++++++--- cpp/tests/test_abm_simulation.cpp | 22 +- cpp/tests/test_abm_testing_strategy.cpp | 30 ++- cpp/tests/test_abm_world.cpp | 12 +- cpp/tests/test_random_number_generator.cpp | 58 +++-- 31 files changed, 697 insertions(+), 467 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index a0fe88067e..1e97151bb9 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -129,8 +129,9 @@ int main() for (auto& person : persons) { mio::abm::InfectionState infection_state = (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(mio::abm::VirusVariant::Wildtype, person.get_age(), + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), world.get_global_infection_parameters(), start_date, infection_state)); } diff --git a/cpp/models/abm/household.cpp b/cpp/models/abm/household.cpp index 440d4e7c63..2cff309e95 100755 --- a/cpp/models/abm/household.cpp +++ b/cpp/models/abm/household.cpp @@ -19,7 +19,10 @@ */ #include "abm/household.h" +#include "abm/person.h" +#include "abm/location.h" #include "memilio/math/eigen.h" +#include "memilio/utils/random_number_generator.h" #include namespace mio @@ -34,10 +37,11 @@ namespace * @param[in] age_groups A CustomIndexArray with the weights. * @return The picked AgeGroup. */ -AgeGroup pick_age_group_from_age_distribution(const CustomIndexArray& age_groups) +AgeGroup pick_age_group_from_age_distribution(RandomNumberGenerator& rng, + const CustomIndexArray& age_groups) { auto age_group_weights = age_groups.array().cast().eval(); - size_t age_group = DiscreteDistribution::get_instance()(age_group_weights); + size_t age_group = DiscreteDistribution::get_instance()(rng, age_group_weights); return (AgeGroup)age_group; } } // namespace @@ -67,7 +71,7 @@ void add_household_to_world(World& world, const Household& household) HouseholdMember member; std::tie(member, count) = memberTouple; for (int j = 0; j < count; j++) { - auto age_group = pick_age_group_from_age_distribution(member.get_age_weights()); + auto age_group = pick_age_group_from_age_distribution(world.get_rng(), member.get_age_weights()); auto& person = world.add_person(home, age_group); person.set_assigned_location(home); } diff --git a/cpp/models/abm/household.h b/cpp/models/abm/household.h index 3b081160ad..6a951a7c5d 100644 --- a/cpp/models/abm/household.h +++ b/cpp/models/abm/household.h @@ -22,8 +22,8 @@ #ifndef EPI_ABM_HOUSEHOLD_H #define EPI_ABM_HOUSEHOLD_H -#include "abm/abm.h" #include "abm/age.h" +#include "abm/world.h" #include "memilio/utils/custom_index_array.h" #include #include diff --git a/cpp/models/abm/infection.cpp b/cpp/models/abm/infection.cpp index 504724a0eb..9073e9b4e4 100644 --- a/cpp/models/abm/infection.cpp +++ b/cpp/models/abm/infection.cpp @@ -26,19 +26,20 @@ namespace mio namespace abm { -Infection::Infection(VirusVariant virus, AgeGroup age, const GlobalInfectionParameters& params, TimePoint init_date, - InfectionState init_state, bool detected) +Infection::Infection(Person::RandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint init_date, InfectionState init_state, + bool detected) : m_virus_variant(virus) , m_detected(detected) { - m_viral_load.start_date = draw_infection_course(age, params, init_date, init_state); + m_viral_load.start_date = draw_infection_course(rng, age, params, init_date, init_state); auto vl_params = params.get()[{ virus, age, VaccinationState::Unvaccinated}]; // TODO: change vaccination state - m_viral_load.peak = vl_params.viral_load_peak.get_distribution_instance()(vl_params.viral_load_peak.params); + m_viral_load.peak = vl_params.viral_load_peak.get_distribution_instance()(rng, vl_params.viral_load_peak.params); m_viral_load.incline = - vl_params.viral_load_incline.get_distribution_instance()(vl_params.viral_load_incline.params); + vl_params.viral_load_incline.get_distribution_instance()(rng, vl_params.viral_load_incline.params); m_viral_load.decline = vl_params.viral_load_decline.get_distribution_instance()(vl_params.viral_load_decline.params); m_viral_load.end_date = @@ -46,8 +47,8 @@ Infection::Infection(VirusVariant virus, AgeGroup age, const GlobalInfectionPara days(int(m_viral_load.peak / m_viral_load.incline - m_viral_load.peak / m_viral_load.decline)); auto inf_params = params.get()[{virus, age}]; - m_log_norm_alpha = inf_params.infectivity_alpha.get_distribution_instance()(inf_params.infectivity_alpha.params); - m_log_norm_beta = inf_params.infectivity_beta.get_distribution_instance()(inf_params.infectivity_beta.params); + m_log_norm_alpha = inf_params.infectivity_alpha.get_distribution_instance()(rng, inf_params.infectivity_alpha.params); + m_log_norm_beta = inf_params.infectivity_beta.get_distribution_instance()(rng, inf_params.infectivity_beta.params); } ScalarType Infection::get_viral_load(TimePoint t) const @@ -100,22 +101,24 @@ bool Infection::is_detected() const return m_detected; } -TimePoint Infection::draw_infection_course(AgeGroup age, const GlobalInfectionParameters& params, TimePoint init_date, +TimePoint Infection::draw_infection_course(Person::RandomNumberGenerator& rng, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint init_date, InfectionState init_state) { - TimePoint start_date = draw_infection_course_backward(age, params, init_date, init_state); - draw_infection_course_forward(age, params, init_date, init_state); + TimePoint start_date = draw_infection_course_backward(rng, age, params, init_date, init_state); + draw_infection_course_forward(rng, age, params, init_date, init_state); return start_date; } -void Infection::draw_infection_course_forward(AgeGroup age, const GlobalInfectionParameters& params, - TimePoint init_date, InfectionState start_state) +void Infection::draw_infection_course_forward(Person::RandomNumberGenerator& rng, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint init_date, + InfectionState start_state) { auto t = init_date; TimeSpan time_period{}; // time period for current infection state InfectionState next_state{start_state}; // next state to enter m_infection_course.push_back(std::pair(t, next_state)); - auto uniform_dist = UniformDistribution::get_instance(); + auto& uniform_dist = UniformDistribution::get_instance(); ScalarType v; // random draws while ((next_state != InfectionState::Recovered && next_state != InfectionState::Dead)) { switch (next_state) { @@ -127,7 +130,7 @@ void Infection::draw_infection_course_forward(AgeGroup age, const GlobalInfectio break; case InfectionState::InfectedNoSymptoms: // roll out next infection step - v = uniform_dist(); + v = uniform_dist(rng); if (v < 0.5) { // TODO: subject to change time_period = days(params.get()[{ m_virus_variant, age, VaccinationState::Unvaccinated}]); // TODO: subject to change @@ -142,7 +145,7 @@ void Infection::draw_infection_course_forward(AgeGroup age, const GlobalInfectio break; case InfectionState::InfectedSymptoms: // roll out next infection step - v = uniform_dist(); + v = uniform_dist(rng); if (v < 0.5) { // TODO: subject to change time_period = days(params.get()[{ m_virus_variant, age, VaccinationState::Unvaccinated}]); // TODO: subject to change @@ -156,7 +159,7 @@ void Infection::draw_infection_course_forward(AgeGroup age, const GlobalInfectio break; case InfectionState::InfectedSevere: // roll out next infection step - v = uniform_dist(); + v = uniform_dist(rng); if (v < 0.5) { // TODO: subject to change time_period = days(params.get()[{ m_virus_variant, age, VaccinationState::Unvaccinated}]); // TODO: subject to change @@ -170,7 +173,7 @@ void Infection::draw_infection_course_forward(AgeGroup age, const GlobalInfectio break; case InfectionState::InfectedCritical: // roll out next infection step - v = uniform_dist(); + v = uniform_dist(rng); if (v < 0.5) { // TODO: subject to change time_period = days(params.get()[{ m_virus_variant, age, VaccinationState::Unvaccinated}]); // TODO: subject to change @@ -190,14 +193,15 @@ void Infection::draw_infection_course_forward(AgeGroup age, const GlobalInfectio } } -TimePoint Infection::draw_infection_course_backward(AgeGroup age, const GlobalInfectionParameters& params, - TimePoint init_date, InfectionState init_state) +TimePoint Infection::draw_infection_course_backward(Person::RandomNumberGenerator& rng, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint init_date, + InfectionState init_state) { auto start_date = init_date; TimeSpan time_period{}; // time period for current infection state InfectionState previous_state{init_state}; // next state to enter - auto uniform_dist = UniformDistribution::get_instance(); + auto& uniform_dist = UniformDistribution::get_instance(); ScalarType v; // random draws while ((previous_state != InfectionState::Exposed)) { switch (previous_state) { @@ -228,18 +232,18 @@ TimePoint Infection::draw_infection_course_backward(AgeGroup age, const GlobalIn case InfectionState::Recovered: // roll out next infection step - v = uniform_dist(); - if (v < 1 / 4) { + v = uniform_dist(rng); + if (v < 1. / 4) { time_period = days(params.get()[{ m_virus_variant, age, VaccinationState::Unvaccinated}]); // TODO: subject to change previous_state = InfectionState::InfectedNoSymptoms; } - if (v < 2 / 4) { // TODO: subject to change + if (v < 2. / 4) { // TODO: subject to change time_period = days(params.get()[{ m_virus_variant, age, VaccinationState::Unvaccinated}]); // TODO: subject to change previous_state = InfectionState::InfectedSymptoms; } - else if (v < 3 / 4) { + else if (v < 3. / 4) { time_period = days(params.get()[{ m_virus_variant, age, VaccinationState::Unvaccinated}]); // TODO: subject to change previous_state = InfectionState::InfectedSevere; diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index e99c113347..696366caa4 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -24,6 +24,7 @@ #include "abm/infection_state.h" #include "abm/virus_variant.h" #include "abm/parameters.h" +#include "abm/person.h" #include @@ -51,13 +52,16 @@ class Infection public: /** * @brief Create an Infection for a single Person. + * Draws a random infection course. + * @param[inout] rng Random number generator. * @param[in] virus Virus type of the Infection. * @param[in] age AgeGroup to determine the ViralLoad course. * @param[in] init_date Date of initializing the Infection. * @param[in] init_state [Default: InfectionState::Exposed] #InfectionState at time of initializing the Infection. * @param[in] detected [Default: false] If the Infection is detected. */ - Infection(VirusVariant virus, AgeGroup age, const GlobalInfectionParameters& params, TimePoint start_date, + Infection(Person::RandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint start_date, InfectionState start_state = InfectionState::Exposed, bool detected = false); /** @@ -107,34 +111,40 @@ class Infection * @brief Determine ViralLoad course and Infection course based on init_state. * Calls draw_infection_course_backward for all #InfectionState%s prior and draw_infection_course_forward for all * subsequent #InfectionState%s. + * @param[inout] rng Random number generator. * @param[in] age AgeGroup of the Person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. * @param[in] init_state #InfectionState at time of initializing the Infection. * @return The starting date of the Infection. */ - TimePoint draw_infection_course(AgeGroup age, const GlobalInfectionParameters& params, TimePoint init_date, + TimePoint draw_infection_course(Person::RandomNumberGenerator& rng, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint init_date, InfectionState init_state); /** * @brief Determine ViralLoad course and Infection course prior to the given start_state. + * @param[inout] rng Random number generator. * @param[in] age AgeGroup of the Person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. * @param[in] init_state #InfectionState at time of initializing the Infection. */ - void draw_infection_course_forward(AgeGroup age, const GlobalInfectionParameters& params, TimePoint init_date, + void draw_infection_course_forward(Person::RandomNumberGenerator& rng, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint init_date, InfectionState init_state); /** * @brief Determine ViralLoad course and Infection course subsequent to the given start_state. + * @param[inout] rng Random number generator. * @param[in] age AgeGroup of the person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. * @param[in] init_state InfectionState at time of initializing the Infection. * @return The starting date of the Infection. */ - TimePoint draw_infection_course_backward(AgeGroup age, const GlobalInfectionParameters& params, TimePoint init_date, + TimePoint draw_infection_course_backward(Person::RandomNumberGenerator& rng, AgeGroup age, + const GlobalInfectionParameters& params, TimePoint init_date, InfectionState init_state); std::vector> m_infection_course; ///< Start date of each #InfectionState. diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index ed38c86735..ec36d24c55 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -20,9 +20,9 @@ #include "abm/mask_type.h" #include "abm/mask.h" #include "abm/location.h" -#include "memilio/utils/random_number_generator.h" #include "abm/random_events.h" - +#include "abm/infection.h" +#include "memilio/utils/random_number_generator.h" #include #include @@ -57,7 +57,8 @@ ScalarType Location::transmission_air_per_day(uint32_t cell_index, VirusVariant m_parameters.get()[{virus}]; } -void Location::interact(Person& person, TimePoint t, TimeSpan dt, const GlobalInfectionParameters& global_params) const +void Location::interact(Person::RandomNumberGenerator& rng, Person& person, TimePoint t, TimeSpan dt, + const GlobalInfectionParameters& global_params) const { // TODO: we need to define what a cell is used for, as the loop may lead to incorrect results for multiple cells auto age_receiver = person.get_age(); @@ -77,11 +78,11 @@ void Location::interact(Person& person, TimePoint t, TimeSpan dt, const GlobalIn local_indiv_trans_prob[v] = std::make_pair(virus, local_indiv_trans_prob_v); } VirusVariant virus = - random_transition(VirusVariant::Count, dt, + random_transition(rng, VirusVariant::Count, dt, local_indiv_trans_prob); // use VirusVariant::Count for no virus submission if (virus != VirusVariant::Count) { person.add_new_infection( - Infection(virus, age_receiver, global_params, t + dt / 2)); // Starting time in first approximation + Infection(rng, virus, age_receiver, global_params, t + dt / 2)); // Starting time in first approximation } } } diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 8a4398cf78..e450c9ed35 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -160,11 +160,13 @@ class Location /** * @brief A Person interacts with the population at this Location and may become infected. + * @param[in, out] rng Random number generator for this person. * @param[in, out] person The Person that interacts with the population. * @param[in] dt Length of the current Simulation time step. * @param[in] global_params Global infection parameters. */ - void interact(Person& person, TimePoint t, TimeSpan dt, const GlobalInfectionParameters& global_params) const; + void interact(Person::RandomNumberGenerator& rng, Person& person, TimePoint t, TimeSpan dt, + const GlobalInfectionParameters& global_params) const; /** * @brief Add a Person to the population at this Location. diff --git a/cpp/models/abm/migration_rules.cpp b/cpp/models/abm/migration_rules.cpp index f03e1f3382..3c7b137460 100644 --- a/cpp/models/abm/migration_rules.cpp +++ b/cpp/models/abm/migration_rules.cpp @@ -32,14 +32,15 @@ namespace mio namespace abm { -LocationType random_migration(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params) +LocationType random_migration(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params) { auto current_loc = person.get_location().get_type(); auto make_transition = [current_loc](auto l) { return std::make_pair(l, l == current_loc ? 0. : 1.); }; if (t < params.get()) { - return random_transition(current_loc, dt, + return random_transition(rng, current_loc, dt, {make_transition(LocationType::Work), make_transition(LocationType::Home), make_transition(LocationType::School), make_transition(LocationType::SocialEvent), make_transition(LocationType::BasicsShop)}); @@ -47,7 +48,8 @@ LocationType random_migration(const Person& person, TimePoint t, TimeSpan dt, co return current_loc; } -LocationType go_to_school(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params) +LocationType go_to_school(Person::RandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params) { auto current_loc = person.get_location().get_type(); @@ -64,7 +66,8 @@ LocationType go_to_school(const Person& person, TimePoint t, TimeSpan dt, const return current_loc; } -LocationType go_to_work(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params) +LocationType go_to_work(Person::RandomNumberGenerator& /*rng*/, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params) { auto current_loc = person.get_location().get_type(); @@ -82,13 +85,14 @@ LocationType go_to_work(const Person& person, TimePoint t, TimeSpan dt, const Mi return current_loc; } -LocationType go_to_shop(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params) +LocationType go_to_shop(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params) { auto current_loc = person.get_location().get_type(); //leave if (t.day_of_week() < 6 && t.hour_of_day() > 7 && t.hour_of_day() < 22 && current_loc == LocationType::Home && !person.is_in_quarantine()) { - return random_transition(current_loc, dt, + return random_transition(rng, current_loc, dt, {{LocationType::BasicsShop, params.get()[person.get_age()]}}); } @@ -100,14 +104,15 @@ LocationType go_to_shop(const Person& person, TimePoint t, TimeSpan dt, const Mi return current_loc; } -LocationType go_to_event(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params) +LocationType go_to_event(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params) { auto current_loc = person.get_location().get_type(); //leave if (current_loc == LocationType::Home && t < params.get() && ((t.day_of_week() <= 4 && t.hour_of_day() >= 19) || (t.day_of_week() >= 5 && t.hour_of_day() >= 10)) && !person.is_in_quarantine()) { - return random_transition(current_loc, dt, + return random_transition(rng, current_loc, dt, {{LocationType::SocialEvent, params.get().get_matrix_at(t.days())[(size_t)person.get_age()]}}); } @@ -121,8 +126,8 @@ LocationType go_to_event(const Person& person, TimePoint t, TimeSpan dt, const M return current_loc; } -LocationType go_to_quarantine(const Person& person, TimePoint /*t*/, TimeSpan /*dt*/, - const MigrationParameters& /*params*/) +LocationType go_to_quarantine(Person::RandomNumberGenerator& /*rng*/, const Person& person, TimePoint /*t*/, + TimeSpan /*dt*/, const MigrationParameters& /*params*/) { auto current_loc = person.get_location().get_type(); if (person.is_in_quarantine() && current_loc != LocationType::Hospital && current_loc != LocationType::ICU) { @@ -131,8 +136,8 @@ LocationType go_to_quarantine(const Person& person, TimePoint /*t*/, TimeSpan /* return current_loc; } -LocationType go_to_hospital(const Person& person, const TimePoint t, TimeSpan /*dt*/, - const MigrationParameters& /*params*/) +LocationType go_to_hospital(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, + TimeSpan /*dt*/, const MigrationParameters& /*params*/) { auto current_loc = person.get_location().get_type(); if (person.get_infection_state(t) == InfectionState::InfectedSevere) { @@ -141,7 +146,8 @@ LocationType go_to_hospital(const Person& person, const TimePoint t, TimeSpan /* return current_loc; } -LocationType go_to_icu(const Person& person, const TimePoint t, TimeSpan /*dt*/, const MigrationParameters& /*params*/) +LocationType go_to_icu(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, TimeSpan /*dt*/, + const MigrationParameters& /*params*/) { auto current_loc = person.get_location().get_type(); if (person.get_infection_state(t) == InfectionState::InfectedCritical) { @@ -150,8 +156,8 @@ LocationType go_to_icu(const Person& person, const TimePoint t, TimeSpan /*dt*/, return current_loc; } -LocationType return_home_when_recovered(const Person& person, const TimePoint t, TimeSpan /*dt*/, - const MigrationParameters& /*params*/) +LocationType return_home_when_recovered(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, + TimeSpan /*dt*/, const MigrationParameters& /*params*/) { auto current_loc = person.get_location().get_type(); if ((current_loc == LocationType::Hospital || current_loc == LocationType::ICU) && diff --git a/cpp/models/abm/migration_rules.h b/cpp/models/abm/migration_rules.h index 16362d2f71..a3bfca69ee 100644 --- a/cpp/models/abm/migration_rules.h +++ b/cpp/models/abm/migration_rules.h @@ -24,16 +24,16 @@ #include "abm/location_type.h" #include "abm/parameters.h" #include "abm/time.h" +#include "abm/person.h" namespace mio { namespace abm { -class Person; - /** * @name Rules for migration between Location%s. + * @param[inout] rng Random number generator for the person. * @param[in] p Person the rule is applied to. * @param[in] t Current time. * @param[in] dt Length of the time step. @@ -46,54 +46,62 @@ class Person; /** * @brief Completely random migration to any other Location. */ -LocationType random_migration(const Person& p, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType random_migration(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /** * @brief School age children go to school in the morning and return later in the day. */ -LocationType go_to_school(const Person& p, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType go_to_school(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /** * @brief Adults may go shopping in their free time. */ -LocationType go_to_shop(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType go_to_shop(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /** * @brief Person%s might go to social events. */ -LocationType go_to_event(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType go_to_event(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /** * @brief Adults go to work in the morning and return later in the day. */ -LocationType go_to_work(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType go_to_work(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /** * @brief Person%s who are in quarantine should go home. */ -LocationType go_to_quarantine(const Person& person, TimePoint /*t*/, TimeSpan /*dt*/, - const MigrationParameters& /*params*/); +LocationType go_to_quarantine(Person::RandomNumberGenerator& rng, const Person& person, TimePoint /*t*/, + TimeSpan /*dt*/, const MigrationParameters& /*params*/); /** * @brief Infected Person%s may be hospitalized. */ -LocationType go_to_hospital(const Person& p, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType go_to_hospital(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /** * @brief Person%s in the hospital may be put in intensive care. */ -LocationType go_to_icu(const Person& p, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType go_to_icu(Person::RandomNumberGenerator& rng, const Person& p, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /** * @brief Person%s in the hospital/icu return home when they recover. */ -LocationType return_home_when_recovered(const Person& person, TimePoint t, TimeSpan dt, - const MigrationParameters& params); +LocationType return_home_when_recovered(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, + TimeSpan dt, const MigrationParameters& params); /** * @brief Person%s in the icu go to cemetery when they are dead. */ -LocationType get_buried(const Person& person, TimePoint t, TimeSpan dt, const MigrationParameters& params); +LocationType get_buried(Person::RandomNumberGenerator& rng, const Person& person, TimePoint t, TimeSpan dt, + const MigrationParameters& params); /**@}*/ } // namespace abm diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 9591854707..44cece7f63 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -31,7 +31,7 @@ namespace mio namespace abm { -Person::Person(Location& location, AgeGroup age, uint32_t person_id) +Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, uint32_t person_id) : m_location(&location) , m_assigned_locations((uint32_t)LocationType::Count, INVALID_LOCATION_INDEX) , m_quarantine(false) @@ -44,16 +44,16 @@ Person::Person(Location& location, AgeGroup age, uint32_t person_id) , m_person_id(person_id) , m_cells{0} { - m_random_workgroup = UniformDistribution::get_instance()(); - m_random_schoolgroup = UniformDistribution::get_instance()(); - m_random_goto_work_hour = UniformDistribution::get_instance()(); - m_random_goto_school_hour = UniformDistribution::get_instance()(); + m_random_workgroup = UniformDistribution::get_instance()(rng); + m_random_schoolgroup = UniformDistribution::get_instance()(rng); + m_random_goto_work_hour = UniformDistribution::get_instance()(rng); + m_random_goto_school_hour = UniformDistribution::get_instance()(rng); } -void Person::interact(TimePoint t, TimeSpan dt, const GlobalInfectionParameters& params) +void Person::interact(RandomNumberGenerator& rng, TimePoint t, TimeSpan dt, const GlobalInfectionParameters& params) { if (get_infection_state(t) == InfectionState::Susceptible) { // Susceptible - m_location->interact(*this, t, dt, params); + m_location->interact(rng, *this, t, dt, params); } m_time_at_location += dt; } @@ -107,6 +107,16 @@ const Location& Person::get_location() const return *m_location; } +const Infection& Person::get_infection() const +{ + return m_infections.back(); +} + +Infection& Person::get_infection() +{ + return m_infections.back(); +} + void Person::set_assigned_location(Location& location) { /* TODO: This is not safe if the location is not the same as added in the world, e.g. the index is wrong. We need to check this. @@ -167,9 +177,9 @@ void Person::remove_quarantine() m_quarantine = false; } -bool Person::get_tested(TimePoint t, const TestParameters& params) +bool Person::get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParameters& params) { - ScalarType random = UniformDistribution::get_instance()(); + ScalarType random = UniformDistribution::get_instance()(rng); if (is_infected(t)) { // true positive if (random < params.sensitivity) { @@ -223,13 +233,13 @@ ScalarType Person::get_mask_protective_factor(const GlobalInfectionParameters& p } } -bool Person::apply_mask_intervention(const Location& target) +bool Person::apply_mask_intervention(RandomNumberGenerator& rng, const Location& target) { if (target.get_npi_active() == false) { m_wears_mask = false; if (get_mask_compliance(target.get_type()) > 0.) { // draw if the person wears a mask even if not required - ScalarType wear_mask = UniformDistribution::get_instance()(); + ScalarType wear_mask = UniformDistribution::get_instance()(rng); if (wear_mask < get_mask_compliance(target.get_type())) { m_wears_mask = true; } @@ -239,7 +249,7 @@ bool Person::apply_mask_intervention(const Location& target) m_wears_mask = true; if (get_mask_compliance(target.get_type()) < 0.) { // draw if a person refuses to wear the required mask - ScalarType wear_mask = UniformDistribution::get_instance()(-1., 0.); + ScalarType wear_mask = UniformDistribution::get_instance()(rng, -1., 0.); if (wear_mask > get_mask_compliance(target.get_type())) { m_wears_mask = false; } diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 1dcaf0ed6f..d566f11cae 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -22,14 +22,13 @@ #include "abm/age.h" #include "abm/location_type.h" +#include "abm/infection_state.h" #include "abm/parameters.h" #include "abm/time.h" -#include "abm/infection.h" #include "abm/vaccine.h" #include "abm/mask_type.h" #include "abm/mask.h" #include "memilio/utils/random_number_generator.h" - #include "memilio/utils/memory.h" #include @@ -40,6 +39,7 @@ namespace abm struct LocationId; class Location; +class Infection; static constexpr uint32_t INVALID_PERSON_ID = std::numeric_limits::max(); @@ -51,31 +51,53 @@ class Person public: /** * Random number generator of individual persons. - * Copies the rng counter from a person on construction and updates it on destruction. - * For each person, only one generator must be active at the same time. + * Increments the rng counter of the person when used. * Satisifes the requirments of UniformRandomBitGenerator. */ - class RandomNumberGenerator : RandomNumberGeneratorBase + class RandomNumberGenerator : public RandomNumberGeneratorBase { - public: - using result_type = uint64_t; - + public: + /** + * Creates a random number generator for a person. + * @param key Key to be used by the generator. + * @param person Person who's counter will be used. + */ RandomNumberGenerator(RNGKey key, Person& person) : m_person(person) , m_key(key) { } + /** + * Creates a random number generator for a person. + * Uses the same key as another random number generator. + * @param rng Random number generator who's key will be used. + * @param person Person who's counter will be used. + */ + RandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) + : RandomNumberGenerator(rng.get_key(), person) + { + } + + /** + * @return Get the key. + */ RNGKey get_key() const { return m_key; } + /** + * @return Get the current counter. + */ RNGCounter get_counter() const { return rng_subsequence_counter(m_person.get_person_id(), m_person.get_rng_counter()); } + /** + * Increment the counter. + */ void increment_counter() { ++m_person.get_rng_counter(); @@ -88,11 +110,13 @@ class Person /** * @brief Create a Person. + * @param[in, out] rng Random number generator. * @param[in, out] location Initial location of the Person. * @param[in] age The AgeGroup of the Person. * @param[in] person_id Index of the Person. */ - explicit Person(Location& location, AgeGroup age, uint32_t person_id = INVALID_PERSON_ID); + explicit Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, + uint32_t person_id = INVALID_PERSON_ID); /** * @brief Compare two Person%s. @@ -109,7 +133,7 @@ class Person * @param[in] dt Length of the current Simulation TimeStep. * @param[in, out] global_infection_parameters Infection parameters that are the same in all Location%s. */ - void interact(TimePoint t, TimeSpan dt, const GlobalInfectionParameters& params); + void interact(RandomNumberGenerator& rng, TimePoint t, TimeSpan dt, const GlobalInfectionParameters& params); /** * @brief Migrate to a different Location. @@ -122,15 +146,8 @@ class Person * @brief Get the latest Infection of the Person. * @return The latest Infection of the Person. */ - Infection& get_infection() - { - return m_infections.back(); - } - - const Infection& get_infection() const - { - return m_infections.back(); - } + Infection& get_infection(); + const Infection& get_infection() const; /** * @brief Get all Vaccination%s of the Person. @@ -295,11 +312,12 @@ class Person * @brief Simulates a viral test and returns the test result of the Person. * If the test is positive, the Person has to quarantine. * If the test is negative, quarantine ends. + * @param[inout] rng Random number generator. * @param[in] t TimePoint of the test. * @param[in] params Sensitivity and specificity of the test method. * @return True if the test result of the Person is positive. */ - bool get_tested(TimePoint t, const TestParameters& params); + bool get_tested(RandomNumberGenerator& rng, TimePoint t, const TestParameters& params); /** * @brief Get the PersonID of the Person. @@ -362,10 +380,11 @@ class Person /** * @brief Checks whether the Person wears a Mask at the target Location. + * @param[inout] rng Random number generator. * @param[in] target The target Location. * @return Whether a Person wears a Mask at the Location. */ - bool apply_mask_intervention(const Location& target); + bool apply_mask_intervention(RandomNumberGenerator& rng, const Location& target); /** * @brief Decide if a Person is currently wearing a Mask. @@ -431,7 +450,7 @@ class Person std::vector m_mask_compliance; ///< Vector of Mask compliance values for all #LocationType%s. uint32_t m_person_id; ///< Id of the Person. std::vector m_cells; ///< Vector with all Cell%s the Person visits at its current Location. - RNGCounter m_rng_counter; ///< counter for RandomNumberGenerator + RNGCounter m_rng_counter{0}; ///< counter for RandomNumberGenerator }; } // namespace abm diff --git a/cpp/models/abm/random_events.h b/cpp/models/abm/random_events.h index c7566fa233..7163b1a785 100644 --- a/cpp/models/abm/random_events.h +++ b/cpp/models/abm/random_events.h @@ -37,15 +37,17 @@ namespace abm * S begin the sum of all rates. Which transition happens is determined by sampling from a discrete distribution * with the rates as weights. It's also possible that no transition happens in this time step. * In this case the current state is returned. + * @tparam RNG Type that satisfies the UniformRandomBitGenerator concept. * @tparam T Type that represents the states. * @tparam NumTransitions Number of possible transitions. + * @param[inout] rng Random number generator. * @param[in] current_state Current state before transition. * @param[in] dt Length of the time step. * @param[in] transitions Array of pairs of new states and their rates (probabilities). * @return New state from the list if transition happens, current_state otherwise. */ -template -T random_transition(T current_state, TimeSpan dt, const std::pair (&transitions)[NumTransitions]) +template +T random_transition(RNG& rng, T current_state, TimeSpan dt, const std::pair (&transitions)[NumTransitions]) { assert(std::all_of(std::begin(transitions), std::end(transitions), [](auto& p) { @@ -60,14 +62,14 @@ T random_transition(T current_state, TimeSpan dt, const std::pair (&t if (sum <= 0) { //no transitions or all transitions have rate zero return current_state; } - auto v = ExponentialDistribution::get_instance()(sum); + auto v = ExponentialDistribution::get_instance()(rng, sum); if (v < dt.days()) { //pick one of the possible transitions using discrete distribution std::array rates; std::transform(std::begin(transitions), std::end(transitions), rates.begin(), [](auto&& t) { return t.second; }); - auto random_idx = DiscreteDistribution::get_instance()(rates); + auto random_idx = DiscreteDistribution::get_instance()(rng, rates); return transitions[random_idx].first; } diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index 35864f6d96..c2781b560e 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -31,7 +31,6 @@ Simulation::Simulation(TimePoint t, World&& world) , m_result(Eigen::Index(InfectionState::Count)) , m_t(t) , m_dt(hours(1)) - , m_rng() { initialize_locations(m_t); } @@ -57,7 +56,7 @@ void Simulation::advance(TimePoint tmax) void Simulation::evolve_world(TimePoint tmax) { auto dt = std::min(m_dt, tmax - m_t); - m_world.evolve(m_t, dt, m_rng_key); + m_world.evolve(m_t, dt); m_t += m_dt; } diff --git a/cpp/models/abm/simulation.h b/cpp/models/abm/simulation.h index db4c836226..1de255d136 100644 --- a/cpp/models/abm/simulation.h +++ b/cpp/models/abm/simulation.h @@ -111,10 +111,6 @@ class Simulation return m_world; } - RandomNumberGenerator& get_rng() { - return m_rng; - } - private: void initialize_locations(TimePoint t); void store_result_at(TimePoint t); @@ -124,7 +120,6 @@ class Simulation TimeSeries m_result; ///< The result of the Simulation. TimePoint m_t; ///< The current TimePoint of the Simulation. TimeSpan m_dt; ///< The length of the time steps. - RandomNumberGenerator m_rng; }; } // namespace abm diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index e6360a7958..84114c5935 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -165,16 +165,17 @@ void TestingScheme::update_activity_status(TimePoint t) m_is_active = (m_start_date <= t && t <= m_end_date); } -bool TestingScheme::run_scheme(Person& person, const Location& location, TimePoint t) const +bool TestingScheme::run_scheme(Person::RandomNumberGenerator& rng, Person& person, const Location& location, + TimePoint t) const { if (person.get_time_since_negative_test() > m_minimal_time_since_last_test) { - double random = UniformDistribution::get_instance()(); + double random = UniformDistribution::get_instance()(rng); if (random < m_probability) { if (std::any_of(m_testing_criteria.begin(), m_testing_criteria.end(), - [person, location, t](TestingCriteria tr) { + [&person, &location, t](TestingCriteria tr) { return tr.evaluate(person, location, t); })) { - return !person.get_tested(t, m_test_type.get_default()); + return !person.get_tested(rng, t, m_test_type.get_default()); } } } @@ -206,18 +207,20 @@ void TestingStrategy::update_activity_status(TimePoint t) } } -bool TestingStrategy::run_strategy(Person& person, const Location& location, TimePoint t) const +bool TestingStrategy::run_strategy(Person::RandomNumberGenerator& rng, Person& person, const Location& location, + TimePoint t) 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, t](TestingScheme ts) { - if (ts.is_active()) { - return ts.run_scheme(person, location, t); - } - return true; - }); + return std::all_of(m_testing_schemes.begin(), m_testing_schemes.end(), + [&person, &location, &rng, t](TestingScheme ts) { + if (ts.is_active()) { + return ts.run_scheme(rng, person, location, t); + } + return true; + }); } } // namespace abm diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 6ebb67f4b0..a959fe83b1 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -25,6 +25,7 @@ #include "abm/person.h" #include "abm/location.h" #include "abm/time.h" +#include "memilio/utils/random_number_generator.h" namespace mio { @@ -176,12 +177,13 @@ class TestingScheme /** * @brief Runs the TestingScheme and potentially tests a Person. + * @param[inout] rng Random number generator for the person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the scheme. * @return If the person is allowed to enter the Location by the scheme. */ - bool run_scheme(Person& person, const Location& location, TimePoint t) const; + bool run_scheme(Person::RandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t) const; private: std::vector m_testing_criteria; ///< Vector with all TestingCriteria of the scheme. @@ -227,12 +229,13 @@ class TestingStrategy /** * @brief Runs the TestingStrategy and potentially tests a Person. + * @param[inout] rng Random number generator for the person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the strategy. * @return If the Person is allowed to enter the Location. */ - bool run_strategy(Person& person, const Location& location, TimePoint t) const; + bool run_strategy(Person::RandomNumberGenerator& rng, Person& person, const Location& location, TimePoint t) const; private: std::vector m_testing_schemes; ///< Set of schemes that are checked for testing. diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index c23ae88177..f012384c41 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -42,32 +42,33 @@ LocationId World::add_location(LocationType type, uint32_t num_cells) Person& World::add_person(const LocationId id, AgeGroup age) { uint32_t person_id = static_cast(m_persons.size()); - m_persons.push_back(std::make_unique(get_individualized_location(id), age, person_id)); + m_persons.push_back(std::make_unique(m_rng, get_individualized_location(id), age, person_id)); auto& person = *m_persons.back(); person.set_assigned_location(m_cemetery_id); get_individualized_location(id).add_person(person); return person; } -void World::evolve(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) +void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); - interaction(t, dt, rng); + interaction(t, dt); m_testing_strategy.update_activity_status(t); - migration(t, dt, rng); + migration(t, dt); end_step(t, dt); } -void World::interaction(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) +void World::interaction(TimePoint t, TimeSpan dt) { #pragma omp parallel for for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; - person->interact(t, dt, m_infection_parameters, rng); + auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); + person->interact(personal_rng, t, dt, m_infection_parameters); } } -void World::migration(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) +void World::migration(TimePoint t, TimeSpan dt) { std::vector>> @@ -90,15 +91,16 @@ void World::migration(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) #pragma omp parallel for for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; + auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); for (auto rule : m_migration_rules) { //check if transition rule can be applied - auto target_type = rule.first(*person, t, dt, m_migration_parameters); + auto target_type = rule.first(personal_rng, *person, t, dt, m_migration_parameters); auto& target_location = find_location(target_type, *person); auto& current_location = person->get_location(); - if (m_testing_strategy.run_strategy(*person, target_location, t)) { + if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { if (target_location != current_location && target_location.get_number_persons() < target_location.get_capacity().persons) { - bool wears_mask = person->apply_mask_intervention(target_location); + bool wears_mask = person->apply_mask_intervention(personal_rng, target_location); if (wears_mask) { person->migrate_to(target_location); } @@ -111,14 +113,15 @@ void World::migration(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng) size_t num_trips = m_trip_list.num_trips(); if (num_trips != 0) { while (m_trip_list.get_current_index() < num_trips && m_trip_list.get_next_trip_time() < t + dt) { - auto& trip = m_trip_list.get_next_trip(); - auto& person = m_persons[trip.person_id]; + auto& trip = m_trip_list.get_next_trip(); + auto& person = m_persons[trip.person_id]; + auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); auto& current_location = person->get_location(); if (!person->is_in_quarantine() && person->get_infection_state(t) != InfectionState::Dead && current_location == get_individualized_location(trip.migration_origin)) { auto& target_location = get_individualized_location(trip.migration_destination); - if (m_testing_strategy.run_strategy(*person, target_location, t)) { - person->apply_mask_intervention(target_location); + if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { + person->apply_mask_intervention(personal_rng, target_location); person->migrate_to(target_location); } } diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 67d7a7c2bc..6803f37e0d 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -88,7 +88,7 @@ class World * @param[in] t Current time. * @param[in] dt Length of the time step. */ - void evolve(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng); + void evolve(TimePoint t, TimeSpan dt); /** * @brief Add a Location to the World. @@ -183,19 +183,29 @@ class World const TestingStrategy& get_testing_strategy() const; + /** + * Get the random number generator used by this world for random events. + * Persons use their own generators with the same key as the global one. + * @return The random number generator. + */ + RandomNumberGenerator& get_rng() + { + return m_rng; + } + private: /** * @brief Person%s interact at their Location and may become infected. * @param[in] t The current TimePoint. * @param[in] dt The length of the time step of the Simulation. */ - void interaction(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng); + void interaction(TimePoint t, TimeSpan dt); /** * @brief Person%s move in the World according to rules. * @param[in] t The current TimePoint. * @param[in] dt The length of the time step of the Simulation. */ - void migration(TimePoint t, TimeSpan dt, RandomNumberGenerator& rng); + void migration(TimePoint t, TimeSpan dt); std::vector> m_persons; ///< Vector with pointers to every Person. std::vector> m_locations; ///< Vector with pointers to every Location. @@ -209,6 +219,7 @@ class World std::vector>> m_migration_rules; ///< Rules that govern the migration between Location%s. LocationId m_cemetery_id; // Central cemetery for all dead persons. + RandomNumberGenerator m_rng; ///< Global random number generator }; } // namespace abm diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index fee91e6616..b061013942 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/abm.h" +#include "abm/person.h" #include "memilio/io/result_io.h" #include "memilio/utils/uncertain_value.h" #include "boost/filesystem.hpp" @@ -42,8 +43,9 @@ void assign_uniform_distribution(mio::UncertainValue& p, ScalarType min, ScalarT * The infection states are chosen randomly. They are distributed according to the probabilites set in the example. * @return random infection state */ -mio::abm::InfectionState determine_infection_state(ScalarType exposed, ScalarType infected_no_symptoms, - ScalarType infected_symptoms, ScalarType recovered) +mio::abm::InfectionState determine_infection_state(mio::abm::Person::RandomNumberGenerator& rng, ScalarType exposed, + ScalarType infected_no_symptoms, ScalarType infected_symptoms, + ScalarType recovered) { ScalarType susceptible = 1 - exposed - infected_no_symptoms - infected_symptoms - recovered; std::vector weights = { @@ -52,7 +54,7 @@ mio::abm::InfectionState determine_infection_state(ScalarType exposed, ScalarTyp if (weights.size() != (size_t)mio::abm::InfectionState::Count - 1) { mio::log_error("Initialization in ABM wrong, please correct vector length."); } - auto state = mio::DiscreteDistribution::get_instance()(weights); + auto state = mio::DiscreteDistribution::get_instance()(rng, weights); return (mio::abm::InfectionState)state; } @@ -439,10 +441,11 @@ void assign_infection_state(mio::abm::World& world, mio::abm::TimePoint t, doubl { auto persons = world.get_persons(); for (auto& person : persons) { + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); auto infection_state = - determine_infection_state(exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); + determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(mio::abm::VirusVariant::Wildtype, person.get_age(), + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), world.get_global_infection_parameters(), t, infection_state)); } } diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index a78972d08e..c9aa540f65 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -78,12 +78,6 @@ test_save_results.cpp ) endif() -if(MEMILIO_ENABLE_OPENMP) - set(TESTSOURCES ${TESTSOURCES} - test_openmp.cpp - ) -endif() - add_executable(memilio-test ${TESTSOURCES}) target_include_directories(memilio-test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(memilio-test PRIVATE memilio ode_secir ode_seir ode_secirvvs ide_seir ide_secir abm gtest_main) diff --git a/cpp/tests/abm_helpers.cpp b/cpp/tests/abm_helpers.cpp index a1c2e1be30..fd8960c39e 100644 --- a/cpp/tests/abm_helpers.cpp +++ b/cpp/tests/abm_helpers.cpp @@ -18,15 +18,19 @@ * limitations under the License. */ #include "abm_helpers.h" +#include "abm/person.h" +#include "memilio/utils/random_number_generator.h" mio::abm::Person make_test_person(mio::abm::Location& location, mio::abm::AgeGroup age, mio::abm::InfectionState infection_state, mio::abm::TimePoint t, mio::abm::GlobalInfectionParameters params) { - mio::abm::Person p = mio::abm::Person(location, age); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Person p = mio::abm::Person(rng, location, age); if (infection_state != mio::abm::InfectionState::Susceptible) { + auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); p.add_new_infection( - mio::abm::Infection(static_cast(0), age, params, t, infection_state)); + mio::abm::Infection(rng_p, static_cast(0), age, params, t, infection_state)); } return p; } @@ -36,7 +40,8 @@ mio::abm::Person& add_test_person(mio::abm::World& world, mio::abm::LocationId l { mio::abm::Person& p = world.add_person(loc_id, age); if (infection_state != mio::abm::InfectionState::Susceptible) { - p.add_new_infection(mio::abm::Infection(static_cast(0), age, + auto rng_p = mio::abm::Person::RandomNumberGenerator(world.get_rng(), p); + p.add_new_infection(mio::abm::Infection(rng_p, static_cast(0), age, world.get_global_infection_parameters(), t, infection_state)); } return p; diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index 1f12bdb9c4..283a858c92 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -17,7 +17,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/age.h" +#include "abm/location_type.h" +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" TEST(TestInfection, init) { @@ -26,6 +30,13 @@ TEST(TestInfection, init) auto age_group_test = mio::abm::AgeGroup::Age15to34; auto vac_state_test = mio::abm::VaccinationState::Unvaccinated; + //set up a personal RNG for infections + //uses uniformdistribution but result doesn't matter, so init before the mock + mio::abm::Location loc(mio::abm::LocationType::Hospital, 0); + auto rng = mio::RandomNumberGenerator(); + auto person = mio::abm::Person(rng, loc, mio::abm::AgeGroup::Age15to34, 0); + auto prng = mio::abm::Person::RandomNumberGenerator(rng, person); + ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(7)) @@ -46,7 +57,7 @@ TEST(TestInfection, init) .infectivity_beta.params.a())) .WillRepeatedly(testing::Return(1.0)); - auto infection = mio::abm::Infection(mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, params, + auto infection = mio::abm::Infection(prng, mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, params, mio::abm::TimePoint(0), mio::abm::InfectionState::Exposed, true); EXPECT_EQ(infection.get_virus_variant(), mio::abm::VirusVariant::Wildtype); diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 7a31e3c7e1..6f72ed186e 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -18,12 +18,14 @@ * limitations under the License. */ #include "abm/infection.h" +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" #include TEST(TestLocation, init) { - auto location = mio::abm::Location(mio::abm::LocationType::School, 0); + mio::abm::Location location(mio::abm::LocationType::School, 0); for (mio::abm::InfectionState i = mio::abm::InfectionState(0); i < mio::abm::InfectionState::Count; i = mio::abm::InfectionState(size_t(i) + 1)) { ASSERT_EQ(location.get_subpopulation(mio::abm::TimePoint(0), i), 0); @@ -36,20 +38,20 @@ TEST(TestLocation, init) TEST(TestLocation, initCell) { - auto location = mio::abm::Location(mio::abm::LocationType::PublicTransport, 0, 2); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 2); ASSERT_EQ(location.get_cells().size(), 2); } TEST(TestLocation, getIndex) { - auto location = mio::abm::Location(mio::abm::LocationType::Home, 0); + mio::abm::Location location(mio::abm::LocationType::Home, 0); ASSERT_EQ((int)location.get_index(), 0); } TEST(TestLocation, addRemovePerson) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0, 1); - auto location = mio::abm::Location(mio::abm::LocationType::PublicTransport, 0, 3); + mio::abm::Location home(mio::abm::LocationType::Home, 0, 1); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); auto person1 = make_test_person(home, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms); auto person2 = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); @@ -85,52 +87,54 @@ TEST(TestLocation, CacheExposureRate) { using testing::Return; - { - mio::abm::AgeGroup age = - mio::abm::AgeGroup(mio::UniformIntDistribution()(0, int(mio::abm::AgeGroup::Count) - 1)); - mio::abm::VirusVariant variant = - mio::abm::VirusVariant(mio::UniformIntDistribution()(0, int(mio::abm::VirusVariant::Count) - 1)); - - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::seconds(10000); - - mio::abm::GlobalInfectionParameters params; - - // setup a location with some chance of exposure - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0, 1); - auto location = mio::abm::Location(mio::abm::LocationType::PublicTransport, 0, 3); - auto infected1 = mio::abm::Person(home, age); - infected1.add_new_infection( - mio::abm::Infection(variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - infected1.migrate_to(location, {0}); - auto infected2 = mio::abm::Person(home, age); - infected2.add_new_infection( - mio::abm::Infection(variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); - infected2.migrate_to(location, {0, 1}); - - //cache precomputed results - location.cache_exposure_rates(t, dt); - - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, - 1e-14); - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.015015859523894731, 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), 0.0075079297619473654, - 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.0075079297619473654, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); - - // should also work with capacities - location.set_capacity_adapted_transmission_risk_flag(true); - location.set_capacity(2, 22, 0); // Capacity for Cell 1 - location.set_capacity(2, 22, 1); // Capacity for Cell 2 - location.set_capacity(2, 22, 2); // Capacity for Cell 3 - location.cache_exposure_rates(t, dt); - - EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); - EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); - EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); - } + auto rng = mio::RandomNumberGenerator(); + + mio::abm::AgeGroup age = mio::abm::AgeGroup( + mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::AgeGroup::Count) - 1)); + mio::abm::VirusVariant variant = mio::abm::VirusVariant( + mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::VirusVariant::Count) - 1)); + + auto t = mio::abm::TimePoint(0); + auto dt = mio::abm::seconds(10000); + + mio::abm::GlobalInfectionParameters params; + + // setup a location with some chance of exposure + mio::abm::Location home(mio::abm::LocationType::Home, 0, 1); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); + auto infected1 = mio::abm::Person(rng, home, age); + auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); + infected1.add_new_infection( + mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); + infected1.migrate_to(location, {0}); + auto infected2 = mio::abm::Person(rng, home, age); + auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); + infected2.add_new_infection( + mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); + infected2.migrate_to(location, {0, 1}); + + //cache precomputed results + location.cache_exposure_rates(t, dt); + + EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, + 1e-14); + EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.015015859523894731, 1e-14); + EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_contacts[{variant, age}]), 0.0075079297619473654, + 1e-14); + EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.0075079297619473654, 1e-14); + EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_contacts[{variant, age}]), 0, 1e-14); + EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); + + // should also work with capacities + location.set_capacity_adapted_transmission_risk_flag(true); + location.set_capacity(2, 22, 0); // Capacity for Cell 1 + location.set_capacity(2, 22, 1); // Capacity for Cell 2 + location.set_capacity(2, 22, 2); // Capacity for Cell 3 + location.cache_exposure_rates(t, dt); + + EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); + EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); + EXPECT_NEAR((location.get_cells()[2].m_cached_exposure_rate_air[{variant}]), 0, 1e-14); } TEST(TestLocation, reachCapacity) @@ -195,7 +199,7 @@ TEST(TestLocation, computeSpacePerPersonRelative) { using testing::Return; - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0, 3); + mio::abm::Location home(mio::abm::LocationType::Home, 0, 3); home.set_capacity(4, 264, 0); // Capacity for Cell 1 home.set_capacity(2, 132, 1); // Capacity for Cell 2 home.set_capacity(0, 0, 2); // Capacity for Cell 3 @@ -210,11 +214,13 @@ TEST(TestLocation, interact) { using testing::Return; + auto rng = mio::RandomNumberGenerator(); + // 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::VirusVariant variant = - mio::abm::VirusVariant(mio::UniformIntDistribution()(0, int(mio::abm::VirusVariant::Count) - 1)); + mio::abm::AgeGroup age = mio::abm::AgeGroup( + mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::AgeGroup::Count) - 1)); + mio::abm::VirusVariant variant = mio::abm::VirusVariant( + mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::VirusVariant::Count) - 1)); auto t = mio::abm::TimePoint(0); auto dt = mio::abm::seconds(8640); //0.1 days @@ -230,7 +236,7 @@ TEST(TestLocation, interact) params.get()[{variant, age, mio::abm::VaccinationState::Unvaccinated}] = 2.; //setup location with some chance of exposure - auto location = mio::abm::Location(mio::abm::LocationType::Work, 0); + mio::abm::Location location(mio::abm::LocationType::Work, 0); auto infected1 = make_test_person(location, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms, t, params); auto infected2 = make_test_person(location, mio::abm::AgeGroup::Age80plus, @@ -251,18 +257,19 @@ TEST(TestLocation, interact) auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); - location.interact(susceptible, t, dt, params); + auto person_rng = mio::abm::Person::RandomNumberGenerator(rng, susceptible); + location.interact(person_rng, susceptible, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - location.interact(susceptible, t, dt, params); + location.interact(person_rng, susceptible, t, dt, params); EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); } TEST(TestLocation, setCapacity) { - auto location = mio::abm::Location(mio::abm::LocationType::Home, 0); + mio::abm::Location location(mio::abm::LocationType::Home, 0); location.set_capacity(4, 200); ASSERT_EQ(location.get_capacity().persons, (uint32_t)4); ASSERT_EQ(location.get_capacity().volume, (uint32_t)200); @@ -274,7 +281,7 @@ TEST(TestLocation, storeSubpopulations) auto dt = mio::abm::days(7); auto params = mio::abm::GlobalInfectionParameters{}; - auto location = mio::abm::Location(mio::abm::LocationType::PublicTransport, 0, 3); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); //setup: p1 goes from Infected to Recovered, p2 stays in Infected and p3 goes from Exposed to InfectedNoSymptoms to Recovered params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age5to14, @@ -361,7 +368,7 @@ TEST(TestLocation, storeSubpopulations) TEST(TestLocation, setRequiredMask) { - auto location = mio::abm::Location(mio::abm::LocationType::Home, 0); + mio::abm::Location location(mio::abm::LocationType::Home, 0); ASSERT_EQ(location.get_required_mask(), mio::abm::MaskType::Community); location.set_required_mask(mio::abm::MaskType::FFP2); @@ -370,7 +377,7 @@ TEST(TestLocation, setRequiredMask) TEST(TestLocation, setNPIActive) { - auto location = mio::abm::Location(mio::abm::LocationType::Home, 0); + mio::abm::Location location(mio::abm::LocationType::Home, 0); location.set_npi_active(false); ASSERT_FALSE(location.get_npi_active()); diff --git a/cpp/tests/test_abm_lockdown_rules.cpp b/cpp/tests/test_abm_lockdown_rules.cpp index 4440751796..51257e0d53 100644 --- a/cpp/tests/test_abm_lockdown_rules.cpp +++ b/cpp/tests/test_abm_lockdown_rules.cpp @@ -17,15 +17,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" TEST(TestLockdownRules, school_closure) { + auto rng = mio::RandomNumberGenerator(); auto t = mio::abm::TimePoint(0); auto dt = mio::abm::hours(1); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(6); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto school = mio::abm::Location(mio::abm::LocationType::School, 0); + + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location school(mio::abm::LocationType::School, 0); //setup rng mock so one person is home schooled and the other goes to school ScopedMockDistribution>>> mock_uniform_dist; @@ -41,28 +45,32 @@ TEST(TestLockdownRules, school_closure) .WillOnce(testing::Return(0.2)) .WillRepeatedly(testing::Return(1.0)); - auto p1 = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); + auto p1 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); p1.set_assigned_location(home); p1.set_assigned_location(school); - auto p2 = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); + auto p2 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); p2.set_assigned_location(home); p2.set_assigned_location(school); mio::abm::MigrationParameters params; mio::abm::set_school_closure(t, 0.7, params); - ASSERT_EQ(mio::abm::go_to_school(p1, t_morning, dt, params), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_school(p2, t_morning, dt, params), mio::abm::LocationType::School); + auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, p1); + ASSERT_EQ(mio::abm::go_to_school(p1_rng, p1, t_morning, dt, params), mio::abm::LocationType::Home); + auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, p2); + ASSERT_EQ(mio::abm::go_to_school(p2_rng, p2, t_morning, dt, params), mio::abm::LocationType::School); } TEST(TestLockdownRules, school_opening) { + auto rng = mio::RandomNumberGenerator(); auto t_closing = mio::abm::TimePoint(0); auto t_opening = mio::abm::TimePoint(0) + mio::abm::days(1); auto dt = mio::abm::hours(1); auto t_morning = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(7); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto school = mio::abm::Location(mio::abm::LocationType::School, 0); + + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location school(mio::abm::LocationType::School, 0); //setup rng mock so the person is homeschooled in case of lockdown ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -72,7 +80,7 @@ TEST(TestLockdownRules, school_opening) .WillOnce(testing::Return(0.6)) .WillOnce(testing::Return(0.6)) .WillRepeatedly(testing::Return(1.0)); - auto p = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); + auto p = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); p.set_assigned_location(home); p.set_assigned_location(school); mio::abm::MigrationParameters params; @@ -80,16 +88,19 @@ TEST(TestLockdownRules, school_opening) mio::abm::set_school_closure(t_closing, 1., params); mio::abm::set_school_closure(t_opening, 0., params); - ASSERT_EQ(mio::abm::go_to_school(p, t_morning, dt, params), mio::abm::LocationType::School); + auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + ASSERT_EQ(mio::abm::go_to_school(p_rng, p, t_morning, dt, params), mio::abm::LocationType::School); } TEST(TestLockdownRules, home_office) { + auto rng = mio::RandomNumberGenerator(); auto t = mio::abm::TimePoint(0); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); auto dt = mio::abm::hours(1); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); + + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location work(mio::abm::LocationType::Work, 0); mio::abm::MigrationParameters params; mio::abm::set_home_office(t, 0.4, params); @@ -104,25 +115,29 @@ TEST(TestLockdownRules, home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto person1 = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); - auto person2 = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); + auto person1 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); + auto person2 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); person1.set_assigned_location(home); person1.set_assigned_location(work); person2.set_assigned_location(home); person2.set_assigned_location(work); - ASSERT_EQ(mio::abm::go_to_work(person1, t_morning, dt, params), mio::abm::LocationType::Work); - ASSERT_EQ(mio::abm::go_to_work(person2, t_morning, dt, params), mio::abm::LocationType::Home); + auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, person1); + ASSERT_EQ(mio::abm::go_to_work(p1_rng, person1, t_morning, dt, params), mio::abm::LocationType::Work); + auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, person2); + ASSERT_EQ(mio::abm::go_to_work(p2_rng, person2, t_morning, dt, params), mio::abm::LocationType::Home); } TEST(TestLockdownRules, no_home_office) { + auto rng = mio::RandomNumberGenerator(); auto t_closing = mio::abm::TimePoint(0); auto t_opening = mio::abm::TimePoint(0) + mio::abm::days(1); auto dt = mio::abm::hours(1); auto t_morning = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(8); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); + + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location work(mio::abm::LocationType::Work, 0); //setup rng mock so the person works in home office ScopedMockDistribution>>> mock_uniform_dist; @@ -133,7 +148,7 @@ TEST(TestLockdownRules, no_home_office) .WillOnce(testing::Return(0.7)) .WillRepeatedly(testing::Return(1.0)); - auto p = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); + auto p = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); p.set_assigned_location(home); p.set_assigned_location(work); mio::abm::MigrationParameters params; @@ -141,35 +156,41 @@ TEST(TestLockdownRules, no_home_office) mio::abm::set_home_office(t_closing, 0.5, params); mio::abm::set_home_office(t_opening, 0., params); - ASSERT_EQ(mio::abm::go_to_work(p, t_morning, dt, params), mio::abm::LocationType::Work); + auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + ASSERT_EQ(mio::abm::go_to_work(p_rng, p, t_morning, dt, params), mio::abm::LocationType::Work); } TEST(TestLockdownRules, social_event_closure) { + auto rng = mio::RandomNumberGenerator(); auto t = mio::abm::TimePoint(0); auto dt = mio::abm::hours(1); auto t_evening = mio::abm::TimePoint(0) + mio::abm::hours(19); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto event = mio::abm::Location(mio::abm::LocationType::SocialEvent, 0); - auto p = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); + + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location event(mio::abm::LocationType::SocialEvent, 0); + auto p = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); p.set_assigned_location(home); p.set_assigned_location(event); mio::abm::MigrationParameters params; mio::abm::close_social_events(t, 1, params); - ASSERT_EQ(mio::abm::go_to_event(p, t_evening, dt, params), mio::abm::LocationType::Home); + auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + ASSERT_EQ(mio::abm::go_to_event(p_rng, p, t_evening, dt, params), mio::abm::LocationType::Home); } TEST(TestLockdownRules, social_events_opening) { + auto rng = mio::RandomNumberGenerator(); auto t_closing = mio::abm::TimePoint(0); auto t_opening = mio::abm::TimePoint(0) + mio::abm::days(1); auto dt = mio::abm::hours(1); auto t_evening = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(19); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto event = mio::abm::Location(mio::abm::LocationType::SocialEvent, 0); - auto p = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); + + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location event(mio::abm::LocationType::SocialEvent, 0); + auto p = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); p.set_assigned_location(event); p.set_assigned_location(home); mio::abm::MigrationParameters params; @@ -180,5 +201,6 @@ TEST(TestLockdownRules, social_events_opening) ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - ASSERT_EQ(mio::abm::go_to_event(p, t_evening, dt, params), mio::abm::LocationType::SocialEvent); + auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + ASSERT_EQ(mio::abm::go_to_event(p_rng, p, t_evening, dt, params), mio::abm::LocationType::SocialEvent); } diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index 573cb2ec8e..a4c6b36742 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -17,7 +17,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" TEST(TestMasks, init) { @@ -54,6 +56,7 @@ TEST(TestMasks, changeMask) TEST(TestMasks, maskProtection) { + auto rng = mio::RandomNumberGenerator(); mio::abm::GlobalInfectionParameters params; // set incubation period to two days so that the newly infected person is still exposed @@ -61,12 +64,12 @@ TEST(TestMasks, maskProtection) mio::abm::VaccinationState::Unvaccinated}] = 2.; //setup location with some chance of exposure - auto t = mio::abm::TimePoint(0); - auto infection_location = mio::abm::Location(mio::abm::Location(mio::abm::LocationType::School, 0)); - auto susc_person1 = mio::abm::Person(infection_location, mio::abm::AgeGroup::Age15to34); - auto susc_person2 = mio::abm::Person(infection_location, mio::abm::AgeGroup::Age15to34); - auto infected1 = make_test_person(infection_location, mio::abm::AgeGroup::Age15to34, - mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior + mio::abm::Location infection_location(mio::abm::LocationType::School, 0); + auto t = mio::abm::TimePoint(0); + auto susc_person1 = mio::abm::Person(rng, infection_location, mio::abm::AgeGroup::Age15to34); + auto susc_person2 = mio::abm::Person(rng, infection_location, mio::abm::AgeGroup::Age15to34); + auto infected1 = make_test_person(infection_location, mio::abm::AgeGroup::Age15to34, + mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior infection_location.add_person(infected1); @@ -82,9 +85,11 @@ TEST(TestMasks, maskProtection) ScopedMockDistribution>>> mock_exponential_dist; - infection_location.interact(susc_person1, t, dt, params); + auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person1); + infection_location.interact(p1_rng, susc_person1, t, dt, params); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); - infection_location.interact(susc_person2, t, dt, params); + auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person2); + infection_location.interact(p2_rng, susc_person2, t, dt, params); // The person susc_person1 should have full protection against an infection, susc_person2 not ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index 23edfd301a..fa8108876b 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -17,10 +17,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" TEST(TestMigrationRules, student_goes_to_school) { + auto rng = mio::RandomNumberGenerator(); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) @@ -30,21 +33,24 @@ TEST(TestMigrationRules, student_goes_to_school) .WillOnce(testing::Return(0.6)) .WillRepeatedly(testing::Return(1.0)); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto p_child = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); - auto p_adult = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + auto p_child = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); + auto p_adult = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(7); auto t_weekend = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(7); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_school(p_child, t_morning, dt, {}), mio::abm::LocationType::School); - ASSERT_EQ(mio::abm::go_to_school(p_adult, t_morning, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_school(p_child, t_weekend, dt, {}), mio::abm::LocationType::Home); + auto child_rng = mio::abm::Person::RandomNumberGenerator(rng, p_child); + auto adult_rng = mio::abm::Person::RandomNumberGenerator(rng, p_child); + ASSERT_EQ(mio::abm::go_to_school(child_rng, p_child, t_morning, dt, {}), mio::abm::LocationType::School); + ASSERT_EQ(mio::abm::go_to_school(adult_rng, p_adult, t_morning, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_school(child_rng, p_child, t_weekend, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, students_go_to_school_in_different_times) { + auto rng = mio::RandomNumberGenerator(); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) @@ -62,22 +68,29 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times) .WillOnce(testing::Return(0.8)) .WillRepeatedly(testing::Return(1.0)); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto p_child_goes_to_school_at_6 = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); - auto p_child_goes_to_school_at_8 = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); + auto rng_child_goes_to_school_at_6 = mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_6); + auto p_child_goes_to_school_at_8 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); + auto rng_child_goes_to_school_at_8 = mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); auto t_morning_8 = mio::abm::TimePoint(0) + mio::abm::hours(8); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_6, t_morning_6, dt, {}), mio::abm::LocationType::School); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_6, t_morning_8, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_8, t_morning_6, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_8, t_morning_8, dt, {}), mio::abm::LocationType::School); + ASSERT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_6, dt, {}), + mio::abm::LocationType::School); + ASSERT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_8, dt, {}), + mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_8, p_child_goes_to_school_at_8, t_morning_6, dt, {}), + mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_8, p_child_goes_to_school_at_8, t_morning_8, dt, {}), + mio::abm::LocationType::School); } TEST(TestMigrationRules, students_go_to_school_in_different_times_with_smaller_time_steps) { + auto rng = mio::RandomNumberGenerator(); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) @@ -98,37 +111,47 @@ TEST(TestMigrationRules, students_go_to_school_in_different_times_with_smaller_t .WillOnce(testing::Return(0.9)) .WillRepeatedly(testing::Return(1.0)); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto p_child_goes_to_school_at_6 = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); - auto p_child_goes_to_school_at_8_30 = mio::abm::Person(home, mio::abm::AgeGroup::Age5to14); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + auto p_child_goes_to_school_at_6 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); + auto rng_child_goes_to_school_at_6 = mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_6); + auto p_child_goes_to_school_at_8_30 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age5to14); + auto rng_child_goes_to_school_at_8_30 = + mio::abm::Person::RandomNumberGenerator(rng, p_child_goes_to_school_at_8_30); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); auto t_morning_8_30 = mio::abm::TimePoint(0) + mio::abm::hours(8) + mio::abm::seconds(1800); auto dt = mio::abm::seconds(1800); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_6, t_morning_6, dt, {}), mio::abm::LocationType::School); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_6, t_morning_8_30, dt, {}), - mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_8_30, t_morning_6, dt, {}), - mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_school(p_child_goes_to_school_at_8_30, t_morning_8_30, dt, {}), + ASSERT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_6, dt, {}), + mio::abm::LocationType::School); + ASSERT_EQ( + mio::abm::go_to_school(rng_child_goes_to_school_at_6, p_child_goes_to_school_at_6, t_morning_8_30, dt, {}), + mio::abm::LocationType::Home); + ASSERT_EQ( + mio::abm::go_to_school(rng_child_goes_to_school_at_8_30, p_child_goes_to_school_at_8_30, t_morning_6, dt, {}), + mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_school(rng_child_goes_to_school_at_8_30, p_child_goes_to_school_at_8_30, t_morning_8_30, + dt, {}), mio::abm::LocationType::School); } TEST(TestMigrationRules, school_return) { - auto school = mio::abm::Location(mio::abm::LocationType::School, 0); - auto p_child = mio::abm::Person(school, mio::abm::AgeGroup::Age5to14); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location school(mio::abm::LocationType::School, 0); + auto p_child = mio::abm::Person(rng, school, mio::abm::AgeGroup::Age5to14); + auto rng_child = mio::abm::Person::RandomNumberGenerator(rng, p_child); auto t = mio::abm::TimePoint(0) + mio::abm::hours(15); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_school(p_child, t, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_school(rng_child, p_child, t, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, worker_goes_to_work) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location home(mio::abm::LocationType::Home, 0); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) @@ -142,21 +165,24 @@ TEST(TestMigrationRules, worker_goes_to_work) .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = mio::abm::Person(home, mio::abm::AgeGroup::Age60to79); - auto p_adult = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); + auto p_retiree = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age60to79); + auto rng_retiree = mio::abm::Person::RandomNumberGenerator(rng, p_retiree); + auto p_adult = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); + auto rng_adult = mio::abm::Person::RandomNumberGenerator(rng, p_adult); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); auto t_night = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(4); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_work(p_retiree, t_morning, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(p_adult, t_morning, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(p_adult, t_night, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_night, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location home(mio::abm::LocationType::Home, 0); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) @@ -170,21 +196,24 @@ TEST(TestMigrationRules, worker_goes_to_work_with_non_dividable_timespan) .WillOnce(testing::Return(0.)) .WillRepeatedly(testing::Return(1.0)); - auto p_retiree = mio::abm::Person(home, mio::abm::AgeGroup::Age60to79); - auto p_adult = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); + auto p_retiree = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age60to79); + auto rng_retiree = mio::abm::Person::RandomNumberGenerator(rng, p_retiree); + auto p_adult = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); + auto rng_adult = mio::abm::Person::RandomNumberGenerator(rng, p_adult); auto t_morning = mio::abm::TimePoint(0) + mio::abm::hours(8); auto t_night = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(4); auto dt = mio::abm::minutes(53); - ASSERT_EQ(mio::abm::go_to_work(p_retiree, t_morning, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(p_adult, t_morning, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(p_adult, t_night, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_retiree, p_retiree, t_morning, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_morning, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t_night, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, workers_go_to_work_in_different_times) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location home(mio::abm::LocationType::Home, 0); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) .Times(testing::AtLeast(8)) @@ -199,73 +228,91 @@ TEST(TestMigrationRules, workers_go_to_work_in_different_times) .WillRepeatedly(testing::Return(1.0)); - auto p_adult_goes_to_work_at_6 = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); - auto p_adult_goes_to_work_at_8 = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); + auto p_adult_goes_to_work_at_6 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); + auto rng_adult_goes_to_work_at_6 = mio::abm::Person::RandomNumberGenerator(rng, p_adult_goes_to_work_at_6); + auto p_adult_goes_to_work_at_8 = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); + auto rng_adult_goes_to_work_at_8 = mio::abm::Person::RandomNumberGenerator(rng, p_adult_goes_to_work_at_8); auto t_morning_6 = mio::abm::TimePoint(0) + mio::abm::hours(6); auto t_morning_8 = mio::abm::TimePoint(0) + mio::abm::hours(8); auto t_night = mio::abm::TimePoint(0) + mio::abm::days(1) + mio::abm::hours(4); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_work(p_adult_goes_to_work_at_6, t_morning_6, dt, {}), mio::abm::LocationType::Work); - ASSERT_EQ(mio::abm::go_to_work(p_adult_goes_to_work_at_6, t_morning_8, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(p_adult_goes_to_work_at_6, t_night, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(p_adult_goes_to_work_at_8, t_morning_6, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_work(p_adult_goes_to_work_at_8, t_morning_8, dt, {}), mio::abm::LocationType::Work); - ASSERT_EQ(mio::abm::go_to_work(p_adult_goes_to_work_at_8, t_night, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_6, p_adult_goes_to_work_at_6, t_morning_6, dt, {}), + mio::abm::LocationType::Work); + ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_6, p_adult_goes_to_work_at_6, t_morning_8, dt, {}), + mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_6, p_adult_goes_to_work_at_6, t_night, dt, {}), + mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_8, p_adult_goes_to_work_at_8, t_morning_6, dt, {}), + mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_8, p_adult_goes_to_work_at_8, t_morning_8, dt, {}), + mio::abm::LocationType::Work); + ASSERT_EQ(mio::abm::go_to_work(rng_adult_goes_to_work_at_8, p_adult_goes_to_work_at_8, t_night, dt, {}), + mio::abm::LocationType::Home); } TEST(TestMigrationRules, work_return) { - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); - auto p_adult = mio::abm::Person(work, mio::abm::AgeGroup::Age35to59); - auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); - auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_work(p_adult, t, dt, {}), mio::abm::LocationType::Home); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location work(mio::abm::LocationType::Work, 0); + auto p_adult = mio::abm::Person(rng, work, mio::abm::AgeGroup::Age35to59); + auto rng_adult = mio::abm::Person::RandomNumberGenerator(rng, p_adult); + auto t = mio::abm::TimePoint(0) + mio::abm::hours(17); + auto dt = mio::abm::hours(1); + ASSERT_EQ(mio::abm::go_to_work(rng_adult, p_adult, t, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, quarantine) { - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); + auto rng = mio::RandomNumberGenerator(); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); - auto hospital = mio::abm::Location(mio::abm::LocationType::Hospital, 0); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location work(mio::abm::LocationType::Work, 0); + mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0); - auto p_inf1 = make_test_person(work, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, t); + auto p_inf1 = make_test_person(work, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, t); + auto rng_inf1 = mio::abm::Person::RandomNumberGenerator(rng, p_inf1); p_inf1.detect_infection(t); - ASSERT_EQ(mio::abm::go_to_quarantine(p_inf1, t, dt, {}), + ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf1, p_inf1, t, dt, {}), mio::abm::LocationType::Home); //detected infected person quarantines at home - auto p_inf2 = make_test_person(work, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, t); - ASSERT_EQ(mio::abm::go_to_quarantine(p_inf2, t, dt, {}), + auto p_inf2 = make_test_person(work, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, t); + auto rng_inf2 = mio::abm::Person::RandomNumberGenerator(rng, p_inf2); + ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf2, p_inf2, t, dt, {}), mio::abm::LocationType::Work); //undetected infected person does not quaratine auto p_inf3 = make_test_person(hospital, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSevere, t); + auto rng_inf3 = mio::abm::Person::RandomNumberGenerator(rng, p_inf3); p_inf3.detect_infection(t); - ASSERT_EQ(mio::abm::go_to_quarantine(p_inf3, t, dt, {}), + ASSERT_EQ(mio::abm::go_to_quarantine(rng_inf3, p_inf3, t, dt, {}), mio::abm::LocationType::Hospital); //detected infected person does not leave hospital to quarantine } TEST(TestMigrationRules, hospital) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); - auto p_inf = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSevere, t); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); + auto p_inf = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSevere, t); + auto rng_inf = mio::abm::Person::RandomNumberGenerator(rng, p_inf); - ASSERT_EQ(mio::abm::go_to_hospital(p_inf, t, dt, {}), mio::abm::LocationType::Hospital); + ASSERT_EQ(mio::abm::go_to_hospital(rng_inf, p_inf, t, dt, {}), mio::abm::LocationType::Hospital); - auto p_car = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); - ASSERT_EQ(mio::abm::go_to_hospital(p_car, t, dt, {}), mio::abm::LocationType::Home); + auto p_car = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); + auto rng_car = mio::abm::Person::RandomNumberGenerator(rng, p_car); + ASSERT_EQ(mio::abm::go_to_hospital(rng_car, p_car, t, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, go_shopping) { - auto hospital = mio::abm::Location(mio::abm::LocationType::Hospital, 0); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0); + mio::abm::Location home(mio::abm::LocationType::Home, 0); auto t_weekday = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(9); auto t_sunday = mio::abm::TimePoint(0) + mio::abm::days(6) + mio::abm::hours(9); @@ -274,109 +321,127 @@ TEST(TestMigrationRules, go_shopping) auto p_hosp = make_test_person(hospital, mio::abm::AgeGroup::Age0to4, mio::abm::InfectionState::InfectedSymptoms, t_weekday); - auto p_home = mio::abm::Person(home, mio::abm::AgeGroup::Age60to79); + auto rng_hosp = mio::abm::Person::RandomNumberGenerator(rng, p_hosp); + auto p_home = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age60to79); + auto rng_home = mio::abm::Person::RandomNumberGenerator(rng, p_home); - ASSERT_EQ(mio::abm::go_to_shop(p_hosp, t_weekday, dt, {}), mio::abm::LocationType::Hospital); - ASSERT_EQ(mio::abm::go_to_shop(p_home, t_sunday, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::go_to_shop(p_home, t_night, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_shop(rng_hosp, p_hosp, t_weekday, dt, {}), mio::abm::LocationType::Hospital); + ASSERT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_sunday, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_night, dt, {}), mio::abm::LocationType::Home); ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - ASSERT_EQ(mio::abm::go_to_shop(p_home, t_weekday, dt, {}), mio::abm::LocationType::BasicsShop); + ASSERT_EQ(mio::abm::go_to_shop(rng_home, p_home, t_weekday, dt, {}), mio::abm::LocationType::BasicsShop); } TEST(TestMigrationRules, shop_return) { + auto rng = mio::RandomNumberGenerator(); auto params = mio::abm::GlobalInfectionParameters{}; auto t = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(9); auto dt = mio::abm::hours(1); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto shop = mio::abm::Location(mio::abm::LocationType::BasicsShop, 0); - auto p = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms, t); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0); + auto p = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms, t); + auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); home.add_person(p); p.migrate_to(shop); - p.interact(t, dt, params); //person only returns home after some time passed + p.interact(rng_p, t, dt, params); //person only returns home after some time passed - ASSERT_EQ(mio::abm::go_to_shop(p, t, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_shop(rng_p, p, t, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, go_event) { - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); - auto p_work = mio::abm::Person(work, mio::abm::AgeGroup::Age35to59); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto p_home = mio::abm::Person(home, mio::abm::AgeGroup::Age60to79); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location work(mio::abm::LocationType::Work, 0); + auto p_work = mio::abm::Person(rng, work, mio::abm::AgeGroup::Age35to59); + auto rng_work = mio::abm::Person::RandomNumberGenerator(rng, p_work); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + auto p_home = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age60to79); + auto rng_home = mio::abm::Person::RandomNumberGenerator(rng, p_home); auto t_weekday = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(20); auto t_saturday = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(10); auto t_night = mio::abm::TimePoint(0) + mio::abm::days(5) + mio::abm::hours(1); auto dt = mio::abm::hours(1); - ASSERT_EQ(mio::abm::go_to_event(p_work, t_weekday, dt, {}), mio::abm::LocationType::Work); - ASSERT_EQ(mio::abm::go_to_event(p_home, t_night, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_event(rng_work, p_work, t_weekday, dt, {}), mio::abm::LocationType::Work); + ASSERT_EQ(mio::abm::go_to_event(rng_home, p_home, t_night, dt, {}), mio::abm::LocationType::Home); ScopedMockDistribution>>> mock_exponential_dist; EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - ASSERT_EQ(mio::abm::go_to_event(p_home, t_weekday, dt, {}), mio::abm::LocationType::SocialEvent); + ASSERT_EQ(mio::abm::go_to_event(rng_home, p_home, t_weekday, dt, {}), mio::abm::LocationType::SocialEvent); EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(testing::Return(0.01)); - ASSERT_EQ(mio::abm::go_to_event(p_home, t_saturday, dt, {}), mio::abm::LocationType::SocialEvent); + ASSERT_EQ(mio::abm::go_to_event(rng_home, p_home, t_saturday, dt, {}), mio::abm::LocationType::SocialEvent); } TEST(TestMigrationRules, event_return) { + auto rng = mio::RandomNumberGenerator(); auto params = mio::abm::GlobalInfectionParameters{}; auto t = mio::abm::TimePoint(0) + mio::abm::days(4) + mio::abm::hours(21); auto dt = mio::abm::hours(3); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto shop = mio::abm::Location(mio::abm::LocationType::SocialEvent, 0); - auto p = mio::abm::Person(home, mio::abm::AgeGroup::Age15to34); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location shop(mio::abm::LocationType::SocialEvent, 0); + auto p = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); + auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); home.add_person(p); p.migrate_to(shop); - p.interact(t, dt, params); + p.interact(rng_p, t, dt, params); - ASSERT_EQ(mio::abm::go_to_event(p, t, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, {}), mio::abm::LocationType::Home); } TEST(TestMigrationRules, icu) { - auto hospital = mio::abm::Location(mio::abm::LocationType::Hospital, 0); - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); auto p_hosp = make_test_person(hospital, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedCritical, t); + auto rng_hosp = mio::abm::Person::RandomNumberGenerator(rng, p_hosp); - ASSERT_EQ(mio::abm::go_to_icu(p_hosp, t, dt, {}), mio::abm::LocationType::ICU); + ASSERT_EQ(mio::abm::go_to_icu(rng_hosp, p_hosp, t, dt, {}), mio::abm::LocationType::ICU); - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); - auto p_work = make_test_person(work, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, t); - ASSERT_EQ(mio::abm::go_to_icu(p_work, t, dt, {}), mio::abm::LocationType::Work); + mio::abm::Location work(mio::abm::LocationType::Work, 0); + auto p_work = make_test_person(work, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, t); + auto rng_work = mio::abm::Person::RandomNumberGenerator(rng, p_work); + ASSERT_EQ(mio::abm::go_to_icu(rng_work, p_work, t, dt, {}), mio::abm::LocationType::Work); } TEST(TestMigrationRules, recover) { - auto hospital = mio::abm::Location(mio::abm::LocationType::Hospital, 0); - auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location hospital(mio::abm::LocationType::Hospital, 0); + auto t = mio::abm::TimePoint(12346); + auto dt = mio::abm::hours(1); auto p_rec = make_test_person(hospital, mio::abm::AgeGroup::Age60to79, mio::abm::InfectionState::Recovered, t); + auto rng_rec = mio::abm::Person::RandomNumberGenerator(rng, p_rec); auto p_inf = make_test_person(hospital, mio::abm::AgeGroup::Age60to79, mio::abm::InfectionState::InfectedSevere, t); + auto rng_inf = mio::abm::Person::RandomNumberGenerator(rng, p_inf); - ASSERT_EQ(mio::abm::return_home_when_recovered(p_rec, t, dt, {}), mio::abm::LocationType::Home); - ASSERT_EQ(mio::abm::return_home_when_recovered(p_inf, t, dt, {}), mio::abm::LocationType::Hospital); + ASSERT_EQ(mio::abm::return_home_when_recovered(rng_rec, p_rec, t, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::return_home_when_recovered(rng_inf, p_inf, t, dt, {}), mio::abm::LocationType::Hospital); } TEST(TestMigrationRules, dead) { - auto icu = mio::abm::Location(mio::abm::LocationType::ICU, 0); + auto rng = mio::RandomNumberGenerator(); + + mio::abm::Location icu(mio::abm::LocationType::ICU, 0); auto t = mio::abm::TimePoint(12346); - auto dt = mio::abm::hours(1); + auto dt = mio::abm::hours(1); auto p_dead = make_test_person(icu, mio::abm::AgeGroup::Age60to79, mio::abm::InfectionState::Dead, t); + auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p_dead); - ASSERT_EQ(mio::abm::get_buried(p_dead, t, dt, {}), mio::abm::LocationType::Cemetery); + ASSERT_EQ(mio::abm::get_buried(p_rng, p_dead, t, dt, {}), mio::abm::LocationType::Cemetery); } diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index d244bbf248..c7e325b377 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -17,13 +17,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/location_type.h" +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" +#include TEST(TestPerson, init) { - auto location = mio::abm::Location(mio::abm::LocationType::Work, 0); - auto t = mio::abm::TimePoint(0); - auto person = mio::abm::Person(location, mio::abm::AgeGroup::Age60to79); + auto rng = mio::RandomNumberGenerator(); + + mio::abm::Location location(mio::abm::LocationType::Work, 0); + auto t = mio::abm::TimePoint(0); + auto person = mio::abm::Person(rng, location, mio::abm::AgeGroup::Age60to79); ASSERT_EQ(person.get_infection_state(t), mio::abm::InfectionState::Susceptible); ASSERT_EQ(person.get_location(), location); @@ -32,11 +38,13 @@ TEST(TestPerson, init) TEST(TestPerson, migrate) { - auto t = mio::abm::TimePoint(0); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto loc1 = mio::abm::Location(mio::abm::LocationType::PublicTransport, 0, 1); - auto loc2 = mio::abm::Location(mio::abm::LocationType::School, 0); - auto loc3 = mio::abm::Location(mio::abm::LocationType::PublicTransport, 0, 2); + auto rng = mio::RandomNumberGenerator(); + + auto t = mio::abm::TimePoint(0); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 0, 1); + mio::abm::Location loc2(mio::abm::LocationType::School, 0); + mio::abm::Location loc3(mio::abm::LocationType::PublicTransport, 0, 2); auto person = make_test_person(home, mio::abm::AgeGroup::Age0to4, mio::abm::InfectionState::Recovered); person.migrate_to(loc1, {0}); @@ -63,8 +71,9 @@ TEST(TestPerson, migrate) TEST(TestPerson, setGetAssignedLocation) { - auto location = mio::abm::Location(mio::abm::LocationType::Work, 2); - auto person = mio::abm::Person(location, mio::abm::AgeGroup::Age35to59); + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location location(mio::abm::LocationType::Work, 2); + auto person = mio::abm::Person(rng, location, mio::abm::AgeGroup::Age35to59); person.set_assigned_location(location); ASSERT_EQ((int)person.get_assigned_location_index(mio::abm::LocationType::Work), 2); @@ -75,10 +84,11 @@ TEST(TestPerson, setGetAssignedLocation) TEST(TestPerson, quarantine) { using testing::Return; + auto rng = mio::RandomNumberGenerator(); auto infection_parameters = mio::abm::GlobalInfectionParameters(); - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location work(mio::abm::LocationType::Work, 0); //setup rng mock so the person has a state transition to Recovered ScopedMockDistribution>>> mock_uniform_dist; @@ -98,24 +108,28 @@ TEST(TestPerson, quarantine) auto person = make_test_person(home, mio::abm::AgeGroup::Age35to59, mio::abm::InfectionState::InfectedSymptoms, t_morning, infection_parameters); + auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); person.detect_infection(t_morning); ASSERT_EQ(person.get_infection_state(t_morning), mio::abm::InfectionState::InfectedSymptoms); - ASSERT_EQ(mio::abm::go_to_work(person, t_morning, dt, {}), mio::abm::LocationType::Home); + ASSERT_EQ(mio::abm::go_to_work(rng_person, person, t_morning, dt, {}), mio::abm::LocationType::Home); ASSERT_EQ(person.get_infection_state(t_morning + dt), mio::abm::InfectionState::Recovered); person.remove_quarantine(); - ASSERT_EQ(mio::abm::go_to_work(person, t_morning, dt, {}), mio::abm::LocationType::Work); + ASSERT_EQ(mio::abm::go_to_work(rng_person, person, t_morning, dt, {}), mio::abm::LocationType::Work); } TEST(TestPerson, get_tested) { using testing::Return; + auto rng = mio::RandomNumberGenerator(); mio::abm::TimePoint t(0); - auto loc = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto infected = make_test_person(loc, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); - auto susceptible = mio::abm::Person(loc, mio::abm::AgeGroup::Age15to34); + mio::abm::Location loc(mio::abm::LocationType::Home, 0); + auto infected = make_test_person(loc, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); + auto rng_infected = mio::abm::Person::RandomNumberGenerator(rng, infected); + auto susceptible = mio::abm::Person(rng, loc, mio::abm::AgeGroup::Age15to34); + auto rng_suscetible = mio::abm::Person::RandomNumberGenerator(rng, susceptible); auto pcr_test = mio::abm::PCRTest(); auto antigen_test = mio::abm::AntigenTest(); @@ -129,13 +143,13 @@ TEST(TestPerson, get_tested) .WillOnce(Return(0.95)) .WillOnce(Return(0.6)) .WillOnce(Return(0.999)); - ASSERT_EQ(infected.get_tested(t, pcr_test.get_default()), true); + ASSERT_EQ(infected.get_tested(rng_infected, t, pcr_test.get_default()), true); ASSERT_EQ(infected.is_in_quarantine(), true); - ASSERT_EQ(infected.get_tested(t, pcr_test.get_default()), false); + ASSERT_EQ(infected.get_tested(rng_infected, t, pcr_test.get_default()), false); ASSERT_EQ(infected.is_in_quarantine(), false); - ASSERT_EQ(susceptible.get_tested(t, pcr_test.get_default()), false); + ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, pcr_test.get_default()), false); ASSERT_EQ(susceptible.is_in_quarantine(), false); - ASSERT_EQ(susceptible.get_tested(t, pcr_test.get_default()), true); + ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, pcr_test.get_default()), true); ASSERT_EQ(susceptible.is_in_quarantine(), true); ASSERT_EQ(susceptible.get_time_since_negative_test(), mio::abm::days(0)); @@ -148,18 +162,18 @@ TEST(TestPerson, get_tested) .WillOnce(Return(0.95)) .WillOnce(Return(0.6)) .WillOnce(Return(0.999)); - ASSERT_EQ(infected.get_tested(t, antigen_test.get_default()), true); - ASSERT_EQ(infected.get_tested(t, antigen_test.get_default()), false); - ASSERT_EQ(susceptible.get_tested(t, antigen_test.get_default()), false); - ASSERT_EQ(susceptible.get_tested(t, antigen_test.get_default()), true); + ASSERT_EQ(infected.get_tested(rng_infected, t, antigen_test.get_default()), true); + ASSERT_EQ(infected.get_tested(rng_infected, t, antigen_test.get_default()), false); + ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, antigen_test.get_default()), false); + ASSERT_EQ(susceptible.get_tested(rng_suscetible, t, antigen_test.get_default()), true); ASSERT_EQ(susceptible.get_time_since_negative_test(), mio::abm::days(0)); } TEST(TestPerson, getCells) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0, 1); - auto location = mio::abm::Location(mio::abm::LocationType::PublicTransport, 0, 2); - auto person = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); + mio::abm::Location home(mio::abm::LocationType::Home, 0, 1); + mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 2); + auto person = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); home.add_person(person); person.migrate_to(location, {0, 1}); ASSERT_EQ(person.get_cells().size(), 2); @@ -167,31 +181,36 @@ TEST(TestPerson, getCells) TEST(TestPerson, interact) { - using testing::Return; + auto rng = mio::RandomNumberGenerator(); + // Location.interact is tested seperately in the location auto infection_parameters = mio::abm::GlobalInfectionParameters(); - auto loc = mio::abm::Location(mio::abm::LocationType::Home, 0); + mio::abm::Location loc(mio::abm::LocationType::Home, 0); mio::abm::TimePoint t(0); - auto person = mio::abm::Person(loc, mio::abm::AgeGroup::Age15to34); - auto dt = mio::abm::seconds(8640); //0.1 days - person.interact(t, dt, infection_parameters); + auto person = mio::abm::Person(rng, loc, mio::abm::AgeGroup::Age15to34); + auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); + auto dt = mio::abm::seconds(8640); //0.1 days + person.interact(rng_person, t, dt, infection_parameters); EXPECT_EQ(person.get_time_at_location(), dt); } TEST(TestPerson, applyMaskIntervention) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto target = mio::abm::Location(mio::abm::LocationType::Work, 0); + auto rng = mio::RandomNumberGenerator(); + + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location target(mio::abm::LocationType::Work, 0); auto person = make_test_person(home); person.get_mask().change_mask(mio::abm::MaskType::Community); + auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); target.set_npi_active(false); - person.apply_mask_intervention(target); + person.apply_mask_intervention(rng_person, target); ASSERT_FALSE(person.get_wear_mask()); auto preferences = std::vector((uint32_t)mio::abm::LocationType::Count, 1.); person.set_mask_preferences(preferences); - person.apply_mask_intervention(target); + person.apply_mask_intervention(rng_person, target); ASSERT_TRUE(person.get_wear_mask()); @@ -199,22 +218,22 @@ TEST(TestPerson, applyMaskIntervention) target.set_required_mask(mio::abm::MaskType::Surgical); preferences = std::vector((uint32_t)mio::abm::LocationType::Count, 0.); person.set_mask_preferences(preferences); - person.apply_mask_intervention(target); + person.apply_mask_intervention(rng_person, target); ASSERT_EQ(person.get_mask().get_type(), mio::abm::MaskType::Surgical); ASSERT_TRUE(person.get_wear_mask()); preferences = std::vector((uint32_t)mio::abm::LocationType::Count, -1.); person.set_mask_preferences(preferences); - person.apply_mask_intervention(target); + person.apply_mask_intervention(rng_person, target); ASSERT_FALSE(person.get_wear_mask()); } TEST(TestPerson, setWearMask) { - auto location = mio::abm::Location(mio::abm::LocationType::School, 0); - auto person = make_test_person(location); + mio::abm::Location location(mio::abm::LocationType::School, 0); + auto person = make_test_person(location); person.set_wear_mask(false); ASSERT_FALSE(person.get_wear_mask()); @@ -225,7 +244,7 @@ TEST(TestPerson, setWearMask) TEST(TestPerson, getProtectiveFactor) { - auto location = mio::abm::Location(mio::abm::LocationType::School, 0); + mio::abm::Location location(mio::abm::LocationType::School, 0); auto person_community = make_test_person(location); person_community.get_mask().change_mask(mio::abm::MaskType::Community); person_community.set_wear_mask(true); @@ -248,3 +267,19 @@ TEST(TestPerson, getProtectiveFactor) ASSERT_EQ(person_ffp2.get_mask_protective_factor(params), 0.9); ASSERT_EQ(person_without.get_mask_protective_factor(params), 0.); } + +TEST(Person, rng) +{ + auto rng = mio::RandomNumberGenerator(); + mio::abm::Location loc(mio::abm::LocationType::Home, 0); + auto p = mio::abm::Person(rng, loc, mio::abm::AgeGroup::Age35to59, 13); + + ASSERT_EQ(p.get_rng_counter(), mio::RNGCounter(0)); + + auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::RNGCounter{0})); + + p_rng(); + ASSERT_EQ(p.get_rng_counter(), mio::RNGCounter(1)); + ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::RNGCounter{1})); +} diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index c5ee8a292d..22cd009fc1 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -52,26 +52,6 @@ TEST(TestSimulation, advance_random) } } -TEST(TestDiscreteDistribution, generate) -{ - using namespace mio; - auto distribution = mio::DiscreteDistribution(); - - std::vector weights; - for (size_t i = 0; i < 50; i++) { - weights = {}; - ASSERT_EQ(distribution(weights), 0); - - weights = {0.5}; - ASSERT_EQ(distribution(weights), 0); - - weights = {0.5, 1.3, 0.1, 0.4, 0.3}; - auto d = distribution(weights); - ASSERT_GE(d, 0); - ASSERT_LE(d, 4); - } -} - TEST(TestSimulation, advance_subpopulation) { auto world = mio::abm::World(); @@ -103,7 +83,7 @@ TEST(TestSimulation, initializeSubpopulation) { auto world = mio::abm::World(); auto loc_id = world.add_location(mio::abm::LocationType::PublicTransport, 3); - auto loc = world.get_individualized_location(loc_id); + auto& loc = world.get_individualized_location(loc_id); ASSERT_EQ(loc.get_subpopulations().get_num_time_points(), 0); auto t = mio::abm::TimePoint(0); diff --git a/cpp/tests/test_abm_testing_strategy.cpp b/cpp/tests/test_abm_testing_strategy.cpp index 6a83fd1a37..d6ba2d3994 100644 --- a/cpp/tests/test_abm_testing_strategy.cpp +++ b/cpp/tests/test_abm_testing_strategy.cpp @@ -17,12 +17,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" TEST(TestTestingCriteria, addRemoveAndEvaluateTestCriteria) { - auto home = mio::abm::Location(mio::abm::LocationType::Home, 0); - auto work = mio::abm::Location(mio::abm::LocationType::Work, 0); + mio::abm::Location home(mio::abm::LocationType::Home, 0); + mio::abm::Location work(mio::abm::LocationType::Work, 0); auto person = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); mio::abm::TimePoint t{0}; @@ -60,10 +62,12 @@ TEST(TestTestingCriteria, addRemoveAndEvaluateTestCriteria) TEST(TestTestingScheme, runScheme) { + auto rng = mio::RandomNumberGenerator(); + std::vector test_infection_states1 = {mio::abm::InfectionState::InfectedSymptoms, mio::abm::InfectionState::InfectedNoSymptoms}; std::vector test_location_types1 = {mio::abm::LocationType::Home, - mio::abm::LocationType::Work}; + mio::abm::LocationType::Work}; auto testing_criteria1 = mio::abm::TestingCriteria({}, test_location_types1, test_infection_states1); std::vector testing_criterias = {testing_criteria1}; @@ -89,11 +93,13 @@ TEST(TestTestingScheme, runScheme) 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::Work, 0); - auto person1 = make_test_person(loc_home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); + mio::abm::Location loc_home(mio::abm::LocationType::Home, 0); + mio::abm::Location loc_work(mio::abm::LocationType::Work, 0); + auto person1 = make_test_person(loc_home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); + auto rng_person1 = mio::abm::Person::RandomNumberGenerator(rng, person1); auto person2 = make_test_person(loc_home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Recovered); + auto rng_person2 = mio::abm::Person::RandomNumberGenerator(rng, person2); ScopedMockDistribution>>> mock_uniform_dist; EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) @@ -103,13 +109,15 @@ TEST(TestTestingScheme, runScheme) .WillOnce(testing::Return(0.7)) .WillOnce(testing::Return(0.5)) .WillOnce(testing::Return(0.9)); - ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home, start_date), false); // Person tests and tests positive - ASSERT_EQ(testing_scheme.run_scheme(person2, loc_work, start_date), true); // Person tests and tests negative - ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home, start_date), + ASSERT_EQ(testing_scheme.run_scheme(rng_person1, person1, loc_home, start_date), + false); // Person tests and tests positive + ASSERT_EQ(testing_scheme.run_scheme(rng_person2, person2, loc_work, start_date), + true); // Person tests and tests negative + ASSERT_EQ(testing_scheme.run_scheme(rng_person1, person1, loc_home, start_date), true); // Person is in quarantine and wants to go home -> can do so - ASSERT_EQ(testing_scheme.run_scheme(person1, loc_work, start_date), true); // Person doesn't test + ASSERT_EQ(testing_scheme.run_scheme(rng_person1, person1, loc_work, start_date), true); // Person doesn't test testing_scheme.add_testing_criteria(testing_criteria1); testing_scheme.remove_testing_criteria(testing_criteria1); - ASSERT_EQ(testing_scheme.run_scheme(person1, loc_home, start_date), true); + ASSERT_EQ(testing_scheme.run_scheme(rng_person1, person1, loc_home, start_date), true); } diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index f0e00ed235..77fea9065d 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -17,7 +17,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "abm/person.h" #include "abm_helpers.h" +#include "memilio/utils/random_number_generator.h" TEST(TestWorld, init) { @@ -351,6 +353,8 @@ TEST(TestWorld, evolveMigration) TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) { + auto rng = mio::RandomNumberGenerator(); + mio::abm::GlobalInfectionParameters params; // make sure the infected person stay in Infected long enough params.get()[{mio::abm::VirusVariant(0), mio::abm::AgeGroup::Age15to34, @@ -366,6 +370,7 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) auto current_time = mio::abm::TimePoint(0); auto person = add_test_person(world, home_id, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, current_time); + auto rng_person = mio::abm::Person::RandomNumberGenerator(rng, person); person.set_assigned_location(home); person.set_assigned_location(work); @@ -385,7 +390,7 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) mio::abm::TestingScheme({testing_criteria}, testing_frequency, start_date, end_date, test_type, probability); world.get_testing_strategy().add_testing_scheme(testing_scheme); - ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work, current_time), + ASSERT_EQ(world.get_testing_strategy().run_strategy(rng_person, person, work, current_time), true); // no active testing scheme -> person can enter current_time = mio::abm::TimePoint(30); world.get_testing_strategy().update_activity_status(current_time); @@ -394,9 +399,10 @@ TEST(TestWorldTestingCriteria, testAddingAndUpdatingAndRunningTestingSchemes) .Times(testing::AtLeast(2)) .WillOnce(testing::Return(0.7)) .WillOnce(testing::Return(0.4)); - ASSERT_EQ(world.get_testing_strategy().run_strategy(person, work, current_time), false); + ASSERT_EQ(world.get_testing_strategy().run_strategy(rng_person, person, work, current_time), 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, current_time), true); // no more testing_schemes + ASSERT_EQ(world.get_testing_strategy().run_strategy(rng_person, person, work, current_time), + true); // no more testing_schemes } diff --git a/cpp/tests/test_random_number_generator.cpp b/cpp/tests/test_random_number_generator.cpp index 5a5719792e..87426a92af 100644 --- a/cpp/tests/test_random_number_generator.cpp +++ b/cpp/tests/test_random_number_generator.cpp @@ -4,35 +4,43 @@ #include #include -TEST(RandomNumberGenerator, blocks) +TEST(RandomNumberGenerator, set_counter) { - mio::RandomNumberGenerator rng1, rng2; - std::vector seeds = { 1, 2, 3, 4 }; - rng1.seed(seeds); - rng2.seed(seeds); - - std::vector samples1; - std::generate_n(std::back_inserter(samples1), 100, [&rng1] { return rng1(); }); + mio::RandomNumberGenerator rng; + uint64_t n = 10; + for (auto i = uint64_t(0); i < n; ++i) { + rng(); + } + ASSERT_EQ(rng.get_counter(), mio::RNGCounter(n)); + auto s1 = rng(); + for (auto i = uint64_t(0); i < n; ++i) { + rng(); + } + ASSERT_EQ(rng.get_counter(), mio::RNGCounter(2 * n + 1)); - rng2.set_block_size(5); - std::vector dump; - std::generate_n(std::back_inserter(dump), 23, [&rng2] { return rng2(); }); - rng2.forward_to_block(19); - std::vector samples2; - std::generate_n(std::back_inserter(samples2), 5, [&rng2] { return rng2(); }); + rng.set_counter(mio::RNGCounter{n}); + ASSERT_EQ(rng.get_counter(), mio::RNGCounter(n)); + auto s2 = rng(); - ASSERT_THAT(samples2, testing::ElementsAreArray(samples1.begin() + 95, samples1.end())); + ASSERT_EQ(s1, s2); } -#ifndef NDEBUG -TEST(RandomNumberGenerator, block_error) +TEST(TestDiscreteDistribution, generate) { - mio::RandomNumberGenerator rng; - rng.set_block_size(5); - std::vector dump; - std::generate_n(std::back_inserter(dump), 123, [&rng] { - return rng(); - }); - ASSERT_DEATH(rng.forward_to_block(19), ".*"); + auto distribution = mio::DiscreteDistributionInPlace(); + auto rng = mio::RandomNumberGenerator(); + + std::vector weights; + for (size_t i = 0; i < 50; i++) { + weights = {}; + ASSERT_EQ(distribution(rng, {weights}), 0); + + weights = {0.5}; + ASSERT_EQ(distribution(rng, {weights}), 0); + + weights = {0.5, 1.3, 0.1, 0.4, 0.3}; + auto d = distribution(rng, {weights}); + ASSERT_GE(d, 0); + ASSERT_LE(d, 4); + } } -#endif From cb2989154ca0e8eed6cc3cda87d3dae387b6d47b Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 17:16:48 +0200 Subject: [PATCH 010/105] clean up logging of rng seeds in tests and sims --- cpp/memilio/compartments/parameter_studies.h | 16 +++++++++++++- .../2020_npis_sarscov2_wildtype_germany.cpp | 22 +++++++++---------- ...021_vaccination_sarscov2_delta_germany.cpp | 22 +++++++++---------- cpp/simulations/abm.cpp | 15 +++++++------ cpp/tests/test_parameter_studies.cpp | 2 ++ cpp/tests/test_save_results.cpp | 18 ++------------- cpp/tests/testmain.cpp | 1 - 7 files changed, 49 insertions(+), 47 deletions(-) diff --git a/cpp/memilio/compartments/parameter_studies.h b/cpp/memilio/compartments/parameter_studies.h index 896156756f..81ff0f46e9 100644 --- a/cpp/memilio/compartments/parameter_studies.h +++ b/cpp/memilio/compartments/parameter_studies.h @@ -150,6 +150,8 @@ class ParameterStudy ensemble_result.reserve(m_num_runs); for (size_t run_idx = start_run_idx; run_idx < end_run_idx; run_idx++) { + log(LogLevel::info, "ParameterStudies: run {}", run_idx); + //prepare rng for this run //assume that sampling the graph uses the thread local rng and isn't multithreaded auto initial_rng_counter = @@ -158,7 +160,6 @@ class ParameterStudy thread_local_rng() = m_rng; //sample - log(LogLevel::info, "ParameterStudies: run {}", run_idx); auto sim = create_sampled_simulation(sample_graph); m_rng = thread_local_rng(); @@ -212,13 +213,26 @@ class ParameterStudy std::vector ensemble_result; ensemble_result.reserve(m_num_runs); + thread_local_rng() = m_rng; + for (size_t i = 0; i < m_num_runs; i++) { + log(LogLevel::info, "ParameterStudies: run {}", i); + + //prepare rng for this run + //assume that sampling the graph uses the thread local rng and isn't multithreaded + auto initial_rng_counter = + rng_subsequence_counter(static_cast(i), RNGCounter(0)); + thread_local_rng().set_counter(initial_rng_counter); auto sim = create_sampled_simulation(sample_graph); + log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", (m_rng.get_counter() - initial_rng_counter).get()); + sim.advance(m_tmax); ensemble_result.emplace_back(std::move(sim).get_graph()); } + m_rng = thread_local_rng(); + return ensemble_result; } diff --git a/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp b/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp index c90fecf8a0..c794067004 100644 --- a/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp +++ b/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp @@ -546,6 +546,17 @@ mio::IOResult run(RunMode mode, const fs::path& data_dir, const fs::path& //run parameter study auto parameter_study = mio::ParameterStudy>{params_graph, 0.0, num_days_sim, 0.5, size_t(num_runs)}; + + // parameter_study.get_rng().seed( + // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging + if (mio::mpi::is_root()) { + printf("Seeds: "); + for (auto s : parameter_study.get_rng().get_seeds()) { + printf("%u, ", s); + } + printf("\n"); + } + auto save_single_run_result = mio::IOResult(mio::success()); auto ensemble = parameter_study.run( [](auto&& graph) { @@ -652,17 +663,6 @@ int main(int argc, char** argv) printf("Saving results to \"%s\".\n", result_dir.c_str()); } - // mio::thread_local_rng().seed( - // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging - mio::thread_local_rng().synchronize(); - if (mio::mpi::is_root()) { - printf("Seeds: "); - for (auto s : mio::thread_local_rng().get_seeds()) { - printf("%u, ", s); - } - printf("\n"); - } - auto result = run(mode, data_dir, save_dir, result_dir, save_single_runs); if (!result) { printf("%s\n", result.error().formatted_message().c_str()); diff --git a/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp b/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp index 30fd8a8e4b..71d3e69d38 100644 --- a/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp +++ b/cpp/simulations/2021_vaccination_sarscov2_delta_germany.cpp @@ -632,6 +632,17 @@ mio::IOResult run(RunMode mode, const fs::path& data_dir, const fs::path& //run parameter study auto parameter_study = mio::ParameterStudy>{params_graph, 0.0, num_days_sim, 0.5, num_runs}; + + // parameter_study.get_rng().seed( + // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging + if (mio::mpi::is_root()) { + printf("Seeds: "); + for (auto s : parameter_study.get_rng().get_seeds()) { + printf("%u, ", s); + } + printf("\n"); + } + auto save_single_run_result = mio::IOResult(mio::success()); auto ensemble = parameter_study.run( [&](auto&& graph) { @@ -810,17 +821,6 @@ int main(int argc, char** argv) printf("Saving results to \"%s\".\n", result_dir.c_str()); } - //mio::thread_local_rng().seed( - // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging - mio::thread_local_rng().synchronize(); - if (mio::mpi::is_root()) { - printf("Seeds: "); - for (auto s : mio::thread_local_rng().get_seeds()) { - printf("%u, ", s); - } - printf("\n"); - } - auto result = run(mode, data_dir, save_dir, result_dir, save_single_runs, late, masks, test, high, long_time, future); if (!result) { diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index b061013942..bab0a98ce7 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -775,6 +775,14 @@ mio::abm::Simulation create_sampled_simulation(const mio::abm::TimePoint& t0) set_parameters(infection_params); auto world = mio::abm::World(infection_params); + // world.get_rng().seed( + // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging + printf("Seeds: "); + for (auto s : world.get_rng().get_seeds()) { + printf("%u, ", s); + } + printf("\n"); + // Create the world object from statistical data. create_world_from_statistical_data(world); @@ -872,13 +880,6 @@ int main(int argc, char** argv) return 0; } - // mio::thread_local_rng().seed({...}); //set seeds, e.g., for debugging - //printf("Seeds: "); - //for (auto s : mio::thread_local_rng().get_seeds()) { - // printf("%u, ", s); - //} - //printf("\n"); - auto result = run(result_dir, num_runs, save_single_runs); if (!result) { printf("%s\n", result.error().formatted_message().c_str()); diff --git a/cpp/tests/test_parameter_studies.cpp b/cpp/tests/test_parameter_studies.cpp index 62eb50fdc7..f2413e747b 100644 --- a/cpp/tests/test_parameter_studies.cpp +++ b/cpp/tests/test_parameter_studies.cpp @@ -154,6 +154,7 @@ TEST(ParameterStudies, sample_graph) graph.add_edge(0, 1, mio::MigrationParameters(Eigen::VectorXd::Constant(Eigen::Index(num_groups * 8), 1.0))); auto study = mio::ParameterStudy>(graph, 0.0, 0.0, 0.5, 1); + mio::log_rng_seeds(study.get_rng(), mio::LogLevel::warn); auto results = study.run([](auto&& g) { return draw_sample(g); }); @@ -306,6 +307,7 @@ TEST(ParameterStudies, check_ensemble_run_result) mio::osecir::set_params_distributions_normal(model, t0, tmax, 0.2); mio::ParameterStudy> parameter_study(model, t0, tmax, 1); + mio::log_rng_seeds(parameter_study.get_rng(), mio::LogLevel::warn); // Run parameter study parameter_study.set_num_runs(1); diff --git a/cpp/tests/test_save_results.cpp b/cpp/tests/test_save_results.cpp index e58cfb67ae..426d2ddb89 100644 --- a/cpp/tests/test_save_results.cpp +++ b/cpp/tests/test_save_results.cpp @@ -113,12 +113,6 @@ TEST(TestSaveResult, save_result) TEST(TestSaveResult, save_result_with_params) { - //rng needs to be reseeded right before using parallel parameterstudies - //to keep the rest of the tests independent, we install a temporary RNG for this test - auto rng = mio::thread_local_rng(); - mio::thread_local_rng() = mio::RandomNumberGenerator(); - mio::log_thread_local_rng_seeds(mio::LogLevel::warn); - // set up parameter study double t0 = 0; double tmax = 100; @@ -169,6 +163,7 @@ TEST(TestSaveResult, save_result_with_params) auto num_runs = 3; auto parameter_study = mio::ParameterStudy>(graph, 0.0, 2.0, 0.5, num_runs); + mio::log_rng_seeds(parameter_study.get_rng(), mio::LogLevel::warn); TempFileRegister tmp_file_register; std::string tmp_results_dir = tmp_file_register.get_unique_path(); @@ -223,18 +218,10 @@ TEST(TestSaveResult, save_result_with_params) .nodes()[0] .property.parameters.get()[mio::Index(1)], params.get()[mio::Index(1)]); - - mio::thread_local_rng() = rng; } TEST(TestSaveResult, save_percentiles_and_sums) { - //rng needs to be reseeded right before using parallel parameterstudies - //to keep the rest of the tests independent, we install a temporary RNG for this test - auto prev_rng = mio::thread_local_rng(); - mio::thread_local_rng() = mio::RandomNumberGenerator(); - mio::log_thread_local_rng_seeds(mio::LogLevel::warn); - // set up parameter study double t0 = 0; double tmax = 100; @@ -285,6 +272,7 @@ TEST(TestSaveResult, save_percentiles_and_sums) auto num_runs = 3; auto parameter_study = mio::ParameterStudy>(graph, 0.0, 2.0, 0.5, num_runs); + mio::log_rng_seeds(parameter_study.get_rng(), mio::LogLevel::warn); TempFileRegister tmp_file_register; std::string tmp_results_dir = tmp_file_register.get_unique_path(); @@ -343,6 +331,4 @@ TEST(TestSaveResult, save_percentiles_and_sums) ASSERT_TRUE(results_run2); auto results_run2_sum = mio::read_result(tmp_results_dir + "/results_run2_sum.h5"); ASSERT_TRUE(results_run2_sum); - - mio::thread_local_rng() = prev_rng; } diff --git a/cpp/tests/testmain.cpp b/cpp/tests/testmain.cpp index 375d2620fc..d969947a99 100644 --- a/cpp/tests/testmain.cpp +++ b/cpp/tests/testmain.cpp @@ -26,7 +26,6 @@ int main(int argc, char** argv) { mio::mpi::init(); mio::set_log_level(mio::LogLevel::warn); - mio::log_thread_local_rng_seeds(mio::LogLevel::warn); ::testing::InitGoogleTest(&argc, argv); int retval = RUN_ALL_TESTS(); From 4b4c9dbd92623d6362e165f1d1376303070c59fd Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 17:44:01 +0200 Subject: [PATCH 011/105] fix build msvc warning example build warnings disable openmp build by default --- cpp/CMakeLists.txt | 2 +- cpp/examples/CMakeLists.txt | 1 + cpp/memilio/utils/random_number_generator.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index b571689a35..2da730d298 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -16,7 +16,7 @@ option(MEMILIO_SANITIZE_UNDEFINED "Enable undefined behavior sanitizer." OFF) option(MEMILIO_BUILD_SHARED_LIBS "Build memilio as a shared library." ON) option(MEMILIO_BUILD_STATIC_LIBS "Build memilio as a static library." ON) option(MEMILIO_ENABLE_MPI "Build memilio with MPI." OFF) -option(MEMILIO_ENABLE_OPENMP "Enable Multithreading with OpenMP." ON) +option(MEMILIO_ENABLE_OPENMP "Enable Multithreading with OpenMP." OFF) mark_as_advanced(MEMILIO_USE_BUNDLED_SPDLOG MEMILIO_SANITIZE_ADDRESS MEMILIO_SANITIZE_UNDEFINED) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index d661cc8bf6..bf9a36bba6 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -28,6 +28,7 @@ target_compile_options(ode_secirvvs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_W add_executable(ode_secir_ageres_example ode_secir_ageres.cpp) target_link_libraries(ode_secir_ageres_example PRIVATE memilio ode_secir) +target_compile_options(ode_secir_ageres_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(graph_example graph.cpp) target_link_libraries(graph_example PRIVATE memilio ode_seir) diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 9f5c043dbf..ab98a8151d 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -26,11 +26,13 @@ #include "memilio/utils/span.h" #include "memilio/utils/type_safe.h" +MSVC_WARNING_DISABLE_PUSH(4127) GCC_CLANG_DIAGNOSTIC(push) GCC_CLANG_DIAGNOSTIC(ignored "-Wexpansion-to-defined") //Random123 handles the portability of this warning internally #include "Random123/array.h" #include "Random123/threefry.h" GCC_CLANG_DIAGNOSTIC(pop) +MSVC_WARNING_POP #include #include From 6e9ad90e89464af4ccb0ac479c62753d6404dd99 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 17:49:52 +0200 Subject: [PATCH 012/105] fix build inline functions --- cpp/memilio/utils/random_number_generator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index ab98a8151d..ed635e56db 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -46,14 +46,14 @@ namespace mio { namespace details { -static uint64_t to_uint64(r123array2x32 tf_array) +inline uint64_t to_uint64(r123array2x32 tf_array) { uint64_t i; std::memcpy(&i, tf_array.data(), sizeof(uint64_t)); return i; } -static r123array2x32 to_r123_array(uint64_t i) +inline r123array2x32 to_r123_array(uint64_t i) { threefry2x32_ctr_t c; std::memcpy(c.data(), &i, sizeof(uint64_t)); From 8edc60964884ebac29e757439bc1b30b29329c06 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 18:17:39 +0200 Subject: [PATCH 013/105] fix build msvc static assert hide omp pragma if no openmp more example warnings --- cpp/examples/CMakeLists.txt | 3 ++- cpp/memilio/CMakeLists.txt | 1 + cpp/memilio/utils/mioomp.h | 18 ++++++++++++++++++ cpp/memilio/utils/random_number_generator.h | 2 +- cpp/models/abm/world.cpp | 13 +++++++------ 5 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 cpp/memilio/utils/mioomp.h diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index bf9a36bba6..0184b90246 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -48,10 +48,11 @@ target_compile_options(abm_history_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WA add_executable(twitter_migration_example twitter_migration.cpp) target_link_libraries(twitter_migration_example PRIVATE memilio ode_secir) +target_compile_options(twitter_migration_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ide_seir_example ide_seir.cpp) target_link_libraries(ide_seir_example PRIVATE memilio ide_seir) -target_compile_options(twitter_migration_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_compile_options(ide_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ide_secir_example ide_secir.cpp) target_link_libraries(ide_secir_example PRIVATE memilio ide_secir) diff --git a/cpp/memilio/CMakeLists.txt b/cpp/memilio/CMakeLists.txt index ca78439321..9ffb60bfad 100644 --- a/cpp/memilio/CMakeLists.txt +++ b/cpp/memilio/CMakeLists.txt @@ -82,6 +82,7 @@ add_library(memilio utils/random_number_generator.cpp utils/miompi.h utils/miompi.cpp + utils/mioomp.h ) target_include_directories(memilio PUBLIC diff --git a/cpp/memilio/utils/mioomp.h b/cpp/memilio/utils/mioomp.h new file mode 100644 index 0000000000..6a11ea2798 --- /dev/null +++ b/cpp/memilio/utils/mioomp.h @@ -0,0 +1,18 @@ +#ifndef MIO_UTIL_OPENMP_H +#define MIO_UTIL_OPENMP_H + +#include "memilio/config.h" +#include "memilio/utils/compiler_diagnostics.h" + +#ifdef MEMILIO_ENABLE_OPENMP +#include "omp.h" + +#define PRAGMA_OMP(x) _Pragma(QUOTE(omp x)) + +#else + +#define PRAGMA_OMP(x) + +#endif + +#endif diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index ed635e56db..9950f6060c 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -86,7 +86,7 @@ uint64_t rng_generate(K key, C counter) template RNGCounter rng_subsequence_counter(T subsequence_idx, C counter) { - static_assert(sizeof(U) > sizeof(C), ""); + static_assert(sizeof(U) > sizeof(typename C::ValueType), "Subsequence counter must be smaller than requested total counter."); //TODO allow sizeof(C) = sizeof(U) and check actual values? //TODO: assert value of subequence_idx diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index f012384c41..7d5be9a11b 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -22,10 +22,11 @@ #include "abm/person.h" #include "abm/location.h" #include "abm/migration_rules.h" -#include "memilio/utils/random_number_generator.h" -#include "memilio/utils/stl_util.h" #include "abm/infection.h" #include "abm/vaccine.h" +#include "memilio/utils/mioomp.h" +#include "memilio/utils/random_number_generator.h" +#include "memilio/utils/stl_util.h" namespace mio { @@ -60,7 +61,7 @@ void World::evolve(TimePoint t, TimeSpan dt) void World::interaction(TimePoint t, TimeSpan dt) { - #pragma omp parallel for + PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); @@ -88,7 +89,7 @@ void World::migration(TimePoint t, TimeSpan dt) m_enhanced_migration_rules.push_back(rule); } } - #pragma omp parallel for + PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); @@ -132,7 +133,7 @@ void World::migration(TimePoint t, TimeSpan dt) void World::begin_step(TimePoint t, TimeSpan dt) { - #pragma omp parallel for + PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_locations.size(); ++i) { auto&& location = m_locations[i]; location->cache_exposure_rates(t, dt); @@ -141,7 +142,7 @@ void World::begin_step(TimePoint t, TimeSpan dt) void World::end_step(TimePoint t, TimeSpan dt) { - #pragma omp parallel for + PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_locations.size(); ++i) { auto&& location = m_locations[i]; location->store_subpopulations(t + dt); From e5036d9f2faa9138ab40be926f9cbe6816765e7a Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 19:03:51 +0200 Subject: [PATCH 014/105] documentation and naming of new RNG --- cpp/memilio/compartments/parameter_studies.h | 4 +- cpp/memilio/utils/random_number_generator.h | 115 ++++++++++++++----- cpp/models/abm/person.h | 18 +-- cpp/tests/test_abm_person.cpp | 8 +- cpp/tests/test_random_number_generator.cpp | 8 +- 5 files changed, 110 insertions(+), 43 deletions(-) diff --git a/cpp/memilio/compartments/parameter_studies.h b/cpp/memilio/compartments/parameter_studies.h index 81ff0f46e9..d482b2211d 100644 --- a/cpp/memilio/compartments/parameter_studies.h +++ b/cpp/memilio/compartments/parameter_studies.h @@ -155,7 +155,7 @@ class ParameterStudy //prepare rng for this run //assume that sampling the graph uses the thread local rng and isn't multithreaded auto initial_rng_counter = - rng_subsequence_counter(static_cast(run_idx), RNGCounter(0)); + rng_subsequence_counter(static_cast(run_idx), Counter(0)); m_rng.set_counter(initial_rng_counter); thread_local_rng() = m_rng; @@ -221,7 +221,7 @@ class ParameterStudy //prepare rng for this run //assume that sampling the graph uses the thread local rng and isn't multithreaded auto initial_rng_counter = - rng_subsequence_counter(static_cast(i), RNGCounter(0)); + rng_subsequence_counter(static_cast(i), Counter(0)); thread_local_rng().set_counter(initial_rng_counter); auto sim = create_sampled_simulation(sample_graph); log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", (m_rng.get_counter() - initial_rng_counter).get()); diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 9950f6060c..b0011653d0 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -46,6 +46,9 @@ namespace mio { namespace details { +/** +* Convert a Random123 array type (rng counters and keys) to uint64_t. +*/ inline uint64_t to_uint64(r123array2x32 tf_array) { uint64_t i; @@ -53,6 +56,9 @@ inline uint64_t to_uint64(r123array2x32 tf_array) return i; } +/** +* Convert a uint64_t to a Random123 array type (rng counters and keys). +*/ inline r123array2x32 to_r123_array(uint64_t i) { threefry2x32_ctr_t c; @@ -61,30 +67,75 @@ inline r123array2x32 to_r123_array(uint64_t i) } } +/** +* A key type for counter based random number generators. +* @tparam an unsigned integer type that determines the size of the key, i.e., the number of different sequences. +*/ template -struct RNGKey : TypeSafe>, OperatorComparison> { +struct Key : TypeSafe>, OperatorComparison> { static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); - using TypeSafe>::TypeSafe; + using TypeSafe>::TypeSafe; }; +/** +* A counter type for counter based random number generators. +* @tparam an unsigned integer type that determines the size of the counter, i.e., the length of the random sequence. +*/ template -struct RNGCounter : TypeSafe>, - OperatorComparison>, - OperatorAdditionSubtraction> { +struct Counter : TypeSafe>, + OperatorComparison>, + OperatorAdditionSubtraction> { static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); - using TypeSafe>::TypeSafe; + using TypeSafe>::TypeSafe; }; -template -uint64_t rng_generate(K key, C counter) +/** +* Generate a random 64-bit number. +* A counter based generator with no internal state, increment the counter +* to get the next value in the random sequence. +* Flexible counter size (as many bits as you need) and trivially parallelizable by either +* using subsequences of counters or different keys. +* @tparam C An instance of Counter template type with up to 64 bit. +* @param key Generator key. Can be seeded with initial randomness using mio::seed_rng_key +* @param counter Sequence counter. Increment to get the next value in the sequence. +* @return 64-bit random number. +*/ +template +uint64_t random(Key key, C counter) { auto c = static_cast(counter.get()); auto k = static_cast(key.get()); return details::to_uint64(threefry2x32(details::to_r123_array(k), details::to_r123_array(c))); } +/** +* Seed a counter based random number generator key. +* @tparam A type that satisfies standard SeedSeq. +* @param seed_seq A seed sequence, e.g. initialized using std::random_device. +* @return A seeded key. +*/ +template +Key seed_rng_key(SeedSeq& seed_seq) +{ + auto tf_key = threefry2x32_key_t::seed(seed_seq); + return Key(details::to_uint64(tf_key)); +} + +/** +* Get the counter in the total random sequence for a counter in a given subsequence. +* The total random sequence is split into contiguous subsequences whose length is determined by the size +* of the given subsequence counter. The total sequence has length 2^u, where u is the size in bits +* of the requested counter type. The subsequences have length 2^n, where n is the size in bits of the +* provided counter type. There are up to 2^(u - n) subsequences. +* @tparam U The desired counter type of the total sequence. +* @tparam T Any integer type. +* @tparam C A counter type. Must be smaller than U. +* @param subsequence_idx The index of the subsequence. +* @param counter The counter in the subsequence. +* @param return The counter in the total sequence. +*/ template -RNGCounter rng_subsequence_counter(T subsequence_idx, C counter) +Counter rng_subsequence_counter(T subsequence_idx, C counter) { static_assert(sizeof(U) > sizeof(typename C::ValueType), "Subsequence counter must be smaller than requested total counter."); //TODO allow sizeof(C) = sizeof(U) and check actual values? @@ -95,27 +146,32 @@ RNGCounter rng_subsequence_counter(T subsequence_idx, C counter) //low bits: subsequence counter static const U bytes_shift = sizeof(U) - sizeof(C); auto s = static_cast(subsequence_idx); - return RNGCounter{(s << (bytes_shift * 8)) + static_cast(counter.get())}; + return Counter{(s << (bytes_shift * 8)) + static_cast(counter.get())}; } +/** +* Get the counter in the given subsequence for the counter in the total sequence. +* @tparam U The desired counter type of the subsequence. Its size defines the length of the subsequence. +* @tparam C The counter type of the total sequence. Must be bigger than U. +* @param counter The counter in the total sequence. +* @return The counter in the subsequence. +*/ template -RNGCounter rng_subsequence_counter(C counter) +Counter rng_subsequence_counter(C counter) { - static_assert(sizeof(U) < sizeof(C), ""); + static_assert(sizeof(U) < sizeof(C::ValueType), ""); //TODO allow sizeof(C) = sizeof(U) and check actual values? //TODO: assert value of subequence_idx //truncate to get the subsequence counter - return RNGCounter(static_cast(counter.get())); -} - -template -RNGKey seed_rng_key(SeedSeq& seed_seq) -{ - auto tf_key = threefry2x32_key_t::seed(seed_seq); - return RNGKey(details::to_uint64(tf_key)); + return Counter(static_cast(counter.get())); } +/** +* Base class for counter based random number generator. +* Counter and key are supplied by the derived class. +* Satisfies the standard UniformRandomBitGenerator concept. +*/ template class RandomNumberGeneratorBase { @@ -126,19 +182,26 @@ class RandomNumberGeneratorBase { return std::numeric_limits::min(); } + static constexpr result_type max() { return std::numeric_limits::max(); } + result_type operator()() { auto self = static_cast(this); - auto r = rng_generate(self->get_key(), self->get_counter()); + auto r = random(self->get_key(), self->get_counter()); self->increment_counter(); return r; } }; +/** +* Counter based random number generator. +* Stores its own key and counter. +* @see RandomNumberGeneratorBase. +*/ class RandomNumberGenerator : public RandomNumberGeneratorBase { public: @@ -148,13 +211,13 @@ class RandomNumberGenerator : public RandomNumberGeneratorBase get_key() const { + Key get_key() const { return m_key; } - RNGCounter get_counter() const { + Counter get_counter() const { return m_counter; } - void set_counter(RNGCounter counter) { + void set_counter(Counter counter) { m_counter = counter; } void increment_counter() { @@ -202,8 +265,8 @@ class RandomNumberGenerator : public RandomNumberGeneratorBase m_key; - RNGCounter m_counter; + Key m_key; + Counter m_counter; std::vector m_seeds; }; diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index d566f11cae..02bf6e835c 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -52,7 +52,7 @@ class Person /** * Random number generator of individual persons. * Increments the rng counter of the person when used. - * Satisifes the requirments of UniformRandomBitGenerator. + * @see mio::RandomNumberGeneratorBase */ class RandomNumberGenerator : public RandomNumberGeneratorBase { @@ -62,7 +62,7 @@ class Person * @param key Key to be used by the generator. * @param person Person who's counter will be used. */ - RandomNumberGenerator(RNGKey key, Person& person) + RandomNumberGenerator(Key key, Person& person) : m_person(person) , m_key(key) { @@ -82,7 +82,7 @@ class Person /** * @return Get the key. */ - RNGKey get_key() const + Key get_key() const { return m_key; } @@ -90,7 +90,7 @@ class Person /** * @return Get the current counter. */ - RNGCounter get_counter() const + Counter get_counter() const { return rng_subsequence_counter(m_person.get_person_id(), m_person.get_rng_counter()); } @@ -105,7 +105,7 @@ class Person private: Person& m_person; - RNGKey m_key; + Key m_key; }; /** @@ -426,7 +426,11 @@ class Person return 1.; // put implementation in .cpp } - RNGCounter& get_rng_counter() + /** + * Get this persons random number generator counter. + * @see mio::abm::Person::RandomNumberGenerator. + */ + Counter& get_rng_counter() { return m_rng_counter; } @@ -450,7 +454,7 @@ class Person std::vector m_mask_compliance; ///< Vector of Mask compliance values for all #LocationType%s. uint32_t m_person_id; ///< Id of the Person. std::vector m_cells; ///< Vector with all Cell%s the Person visits at its current Location. - RNGCounter m_rng_counter{0}; ///< counter for RandomNumberGenerator + Counter m_rng_counter{0}; ///< counter for RandomNumberGenerator }; } // namespace abm diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index c7e325b377..c608fd2f59 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -274,12 +274,12 @@ TEST(Person, rng) mio::abm::Location loc(mio::abm::LocationType::Home, 0); auto p = mio::abm::Person(rng, loc, mio::abm::AgeGroup::Age35to59, 13); - ASSERT_EQ(p.get_rng_counter(), mio::RNGCounter(0)); + ASSERT_EQ(p.get_rng_counter(), mio::Counter(0)); auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); - ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::RNGCounter{0})); + ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::Counter{0})); p_rng(); - ASSERT_EQ(p.get_rng_counter(), mio::RNGCounter(1)); - ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::RNGCounter{1})); + ASSERT_EQ(p.get_rng_counter(), mio::Counter(1)); + ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::Counter{1})); } diff --git a/cpp/tests/test_random_number_generator.cpp b/cpp/tests/test_random_number_generator.cpp index 87426a92af..20f4a2a96c 100644 --- a/cpp/tests/test_random_number_generator.cpp +++ b/cpp/tests/test_random_number_generator.cpp @@ -11,15 +11,15 @@ TEST(RandomNumberGenerator, set_counter) for (auto i = uint64_t(0); i < n; ++i) { rng(); } - ASSERT_EQ(rng.get_counter(), mio::RNGCounter(n)); + ASSERT_EQ(rng.get_counter(), mio::Counter(n)); auto s1 = rng(); for (auto i = uint64_t(0); i < n; ++i) { rng(); } - ASSERT_EQ(rng.get_counter(), mio::RNGCounter(2 * n + 1)); + ASSERT_EQ(rng.get_counter(), mio::Counter(2 * n + 1)); - rng.set_counter(mio::RNGCounter{n}); - ASSERT_EQ(rng.get_counter(), mio::RNGCounter(n)); + rng.set_counter(mio::Counter{n}); + ASSERT_EQ(rng.get_counter(), mio::Counter(n)); auto s2 = rng(); ASSERT_EQ(s1, s2); From 47ffc4a7be09125bb79658a0367b60e0d6a75045 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 20:00:55 +0200 Subject: [PATCH 015/105] fix build msvc and clang warnings in examples --- cpp/examples/abm_minimal.cpp | 6 +----- cpp/examples/serialize.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 1e97151bb9..31d60928b2 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -20,9 +20,6 @@ #include "abm/abm.h" #include "abm/household.h" #include -#include "abm/world.h" -#include "memilio/io/io.h" -#include "abm/location_type.h" #include #include #include @@ -33,7 +30,7 @@ void write_results_to_file(const mio::abm::Simulation& sim) // The first row is t = time, the others correspond to the number of people with a certain infection state at this time: // S = Susceptible, E = Exposed, I_NS = InfectedNoSymptoms, I_Sy = InfectedSymptoms, I_Sev = InfectedSevere, // I_Crit = InfectedCritical, R = Recovered, D = Dead - std::ofstream myfile("abm_minimal.txt"); + std::ofstream myfile("abm_minimal.txt", std::ios::out); myfile << "# t S E I_NS I_Sy I_Sev I_Crit R D\n"; for (auto i = 0; i < sim.get_result().get_num_time_points(); ++i) { myfile << sim.get_result().get_time(i); @@ -48,7 +45,6 @@ void write_results_to_file(const mio::abm::Simulation& sim) myfile << "\n"; } } - myfile.close(); std::cout << "Results written to abm_minimal.txt" << std::endl; } diff --git a/cpp/examples/serialize.cpp b/cpp/examples/serialize.cpp index c50b212d41..5e50bac33f 100644 --- a/cpp/examples/serialize.cpp +++ b/cpp/examples/serialize.cpp @@ -59,8 +59,8 @@ struct Foo { //and create the object if all lookups were succesful. return mio::apply( io, - [](auto&& s) { - return Foo{s}; + [](auto&& s_) { + return Foo{s_}; }, s_rslt); } @@ -97,10 +97,10 @@ struct Bar { //e.g. it can validate values and return an error return mio::apply( io, - [](auto&& i, auto&& foos) -> mio::IOResult { + [](auto&& i_, auto&& foos_) -> mio::IOResult { //use mio::success or mio::failure to return an IOResult - if (i >= 0) { - return mio::success(Bar{i, std::vector{foos.begin(), foos.end()}}); + if (i_ >= 0) { + return mio::success(Bar{i_, std::vector{foos_.begin(), foos_.end()}}); } return mio::failure(mio::StatusCode::OutOfRange, "i must be non-negative."); }, From c04ebb4aaf26361bda3fcbdd1a9ad7958570f532 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 12 May 2023 20:27:01 +0200 Subject: [PATCH 016/105] fix build msvc warning in example --- cpp/examples/ode_secir_parameter_study.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/ode_secir_parameter_study.cpp b/cpp/examples/ode_secir_parameter_study.cpp index 7e26dd347e..6dd37c13d3 100644 --- a/cpp/examples/ode_secir_parameter_study.cpp +++ b/cpp/examples/ode_secir_parameter_study.cpp @@ -31,7 +31,7 @@ * @param tmax end point of simulation */ mio::IOResult -write_single_run_result(const int run, +write_single_run_result(const size_t run, const mio::Graph>, mio::MigrationEdge>& graph) { std::string abs_path; From ec87579cdee3f43176fa0d84ad57e9b410b92df6 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 19 May 2023 13:00:26 +0200 Subject: [PATCH 017/105] fix race conditions in dampings --- cpp/memilio/epidemiology/damping_sampling.h | 1 + cpp/models/abm/lockdown_rules.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/cpp/memilio/epidemiology/damping_sampling.h b/cpp/memilio/epidemiology/damping_sampling.h index c252031036..a9ce7aad14 100644 --- a/cpp/memilio/epidemiology/damping_sampling.h +++ b/cpp/memilio/epidemiology/damping_sampling.h @@ -261,6 +261,7 @@ void apply_dampings(DampingExpression& damping_expression, const DampingSampling damping_expression[i].add_damping(m, d.get_level(), d.get_type(), d.get_time()); } } + damping_expression.finalize(); } /** diff --git a/cpp/models/abm/lockdown_rules.cpp b/cpp/models/abm/lockdown_rules.cpp index 5a509537ba..0efe83e752 100644 --- a/cpp/models/abm/lockdown_rules.cpp +++ b/cpp/models/abm/lockdown_rules.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/lockdown_rules.h" +#include "abm/parameters.h" #include "abm/person.h" #include "abm/time.h" @@ -30,18 +31,21 @@ void set_home_office(TimePoint t_begin, double p, MigrationParameters& params) { auto damping1 = Eigen::VectorXd::Constant(1, p); params.get().add_damping(damping1, SimulationTime(t_begin.days())); + params.get().finalize(); } void set_school_closure(TimePoint t_begin, double p, MigrationParameters& params) { auto damping1 = Eigen::VectorXd::Constant(1, p); params.get().add_damping(damping1, SimulationTime(t_begin.days())); + params.get().finalize(); } void close_social_events(TimePoint t_begin, double p, MigrationParameters& params) { auto damping1 = Eigen::VectorXd::Constant((size_t)AgeGroup::Count, p); params.get().add_damping(damping1, SimulationTime(t_begin.days())); + params.get().finalize(); } } // namespace abm From 3d4bcc589ecdc5c9c0674804cc271e726445b064 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 26 May 2023 13:55:22 +0200 Subject: [PATCH 018/105] avoid copy of testingstrategy --- cpp/models/abm/testing_strategy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/models/abm/testing_strategy.cpp b/cpp/models/abm/testing_strategy.cpp index 84114c5935..3a44d8cd99 100644 --- a/cpp/models/abm/testing_strategy.cpp +++ b/cpp/models/abm/testing_strategy.cpp @@ -215,7 +215,7 @@ bool TestingStrategy::run_strategy(Person::RandomNumberGenerator& rng, Person& p return true; } return std::all_of(m_testing_schemes.begin(), m_testing_schemes.end(), - [&person, &location, &rng, t](TestingScheme ts) { + [&person, &location, &rng, t](const TestingScheme& ts) { if (ts.is_active()) { return ts.run_scheme(rng, person, location, t); } From bbe472ab988344d2bd742a4b4b7a51a4e3a27a41 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 26 May 2023 13:55:59 +0200 Subject: [PATCH 019/105] parallel store_result_at --- cpp/models/abm/simulation.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index c2781b560e..c395f39f3b 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/simulation.h" +#include "memilio/utils/logging.h" #include "memilio/utils/random_number_generator.h" #include @@ -64,8 +65,18 @@ void Simulation::store_result_at(TimePoint t) { m_result.add_time_point(t.days()); m_result.get_last_value().setZero(); - for (auto& location : m_world.get_locations()) { - m_result.get_last_value() += location.get_subpopulations().get_last_value().cast(); + #pragma omp parallel + { + Eigen::VectorXd sum = Eigen::VectorXd::Zero(m_result.get_num_elements()); + #pragma omp for + for (auto i = size_t(0); i < m_world.get_locations().size(); ++i) { + auto&& location = m_world.get_locations()[i]; + sum += location.get_subpopulations().get_last_value().cast(); + } + #pragma omp critical + { + m_result.get_last_value() += sum; + } } } From fa14ce5a0fc6d8a963fe0fe908e23e62dad73ad8 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 26 May 2023 13:56:36 +0200 Subject: [PATCH 020/105] avoid copy of infection --- cpp/models/abm/location.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index ec36d24c55..a3e81a978a 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -96,9 +96,9 @@ void Location::cache_exposure_rates(TimePoint t, TimeSpan dt) cell.m_cached_exposure_rate_air = {{VirusVariant::Count}, 0.}; for (auto&& p : cell.m_persons) { if (p->is_infected(t)) { - auto inf = p->get_infection(); - auto virus = inf.get_virus_variant(); - auto age = p->get_age(); + auto& inf = p->get_infection(); + auto& virus = inf.get_virus_variant(); + auto age = p->get_age(); /* average infectivity over the time step * to second order accuracy using midpoint rule */ From 18a245d12adc341c344558d8d6fa077a0df7cf7f Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 26 May 2023 13:58:44 +0200 Subject: [PATCH 021/105] move update testingstrategy to begin_step --- cpp/models/abm/world.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 7d5be9a11b..d7c3d53726 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -24,6 +24,7 @@ #include "abm/migration_rules.h" #include "abm/infection.h" #include "abm/vaccine.h" +#include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" @@ -53,8 +54,9 @@ Person& World::add_person(const LocationId id, AgeGroup age) void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); + log_info("ABM World interaction."); interaction(t, dt); - m_testing_strategy.update_activity_status(t); + log_info("ABM World migration."); migration(t, dt); end_step(t, dt); } @@ -133,6 +135,7 @@ void World::migration(TimePoint t, TimeSpan dt) void World::begin_step(TimePoint t, TimeSpan dt) { + m_testing_strategy.update_activity_status(t); PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_locations.size(); ++i) { auto&& location = m_locations[i]; From 2b92902cecbfab9b963e9eeef507290d8771a760 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 9 Jun 2023 16:45:04 +0200 Subject: [PATCH 022/105] log abm and world seeds in abm simulation --- cpp/simulations/abm.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index bab0a98ce7..352c4c02fd 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -20,6 +20,7 @@ #include "abm/abm.h" #include "abm/person.h" #include "memilio/io/result_io.h" +#include "memilio/utils/random_number_generator.h" #include "memilio/utils/uncertain_value.h" #include "boost/filesystem.hpp" @@ -766,6 +767,14 @@ void set_parameters(mio::abm::GlobalInfectionParameters infection_params) */ mio::abm::Simulation create_sampled_simulation(const mio::abm::TimePoint& t0) { + // mio::thread_local_rng().seed( + // {123144124, 835345345, 123123123, 99123}); //set seeds, e.g., for debugging + printf("Parameter Sample Seeds: "); + for (auto s : mio::thread_local_rng().get_seeds()) { + printf("%u, ", s); + } + printf("\n"); + // Assumed percentage of infection state at the beginning of the simulation. ScalarType exposed_prob = 0.005, infected_no_symptoms_prob = 0.001, infected_symptoms_prob = 0.001, recovered_prob = 0.0; @@ -776,8 +785,8 @@ mio::abm::Simulation create_sampled_simulation(const mio::abm::TimePoint& t0) auto world = mio::abm::World(infection_params); // world.get_rng().seed( - // {114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); //set seeds, e.g., for debugging - printf("Seeds: "); + // {23144124, 1835345345, 9343763, 9123}); //set seeds, e.g., for debugging + printf("ABM Simulation Seeds: "); for (auto s : world.get_rng().get_seeds()) { printf("%u, ", s); } From 6726e13d83ce13191e68985ba48885f263c032b1 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:04:40 +0200 Subject: [PATCH 023/105] improve documentation of counter based rngs --- cpp/memilio/compartments/parameter_studies.h | 4 +- cpp/memilio/utils/random_number_generator.h | 287 ++++++++++++------- cpp/models/abm/infection.h | 8 +- cpp/models/abm/location.h | 2 +- cpp/models/abm/migration_rules.h | 2 +- cpp/models/abm/person.h | 20 +- cpp/models/abm/random_events.h | 2 +- cpp/models/abm/testing_strategy.h | 4 +- cpp/models/abm/world.h | 2 +- cpp/tests/test_abm_person.cpp | 4 +- 10 files changed, 212 insertions(+), 123 deletions(-) diff --git a/cpp/memilio/compartments/parameter_studies.h b/cpp/memilio/compartments/parameter_studies.h index d482b2211d..97b527ceaf 100644 --- a/cpp/memilio/compartments/parameter_studies.h +++ b/cpp/memilio/compartments/parameter_studies.h @@ -155,7 +155,7 @@ class ParameterStudy //prepare rng for this run //assume that sampling the graph uses the thread local rng and isn't multithreaded auto initial_rng_counter = - rng_subsequence_counter(static_cast(run_idx), Counter(0)); + rng_totalsequence_counter(static_cast(run_idx), Counter(0)); m_rng.set_counter(initial_rng_counter); thread_local_rng() = m_rng; @@ -221,7 +221,7 @@ class ParameterStudy //prepare rng for this run //assume that sampling the graph uses the thread local rng and isn't multithreaded auto initial_rng_counter = - rng_subsequence_counter(static_cast(i), Counter(0)); + rng_totalsequence_counter(static_cast(i), Counter(0)); thread_local_rng().set_counter(initial_rng_counter); auto sim = create_sampled_simulation(sample_graph); log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", (m_rng.get_counter() - initial_rng_counter).get()); diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index b0011653d0..6d7440115e 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -17,6 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef EPI_ABM_RANDOM_NUMBER_GENERATOR_H #define EPI_ABM_RANDOM_NUMBER_GENERATOR_H @@ -44,6 +45,98 @@ MSVC_WARNING_POP namespace mio { + +/** +* Base class for counter based random number generator. +* +* All (pseudo) random number generators (RNG) consist of some state, +* a function `state = advance(state)`, and a function `sample = generate(state)`. +* They produce a sequence of random samples by advancing the state and from the new state +* generate the actual sample. +* +* example in pseudo code: +* state = initial_state(seed) +* sample1 = generate(state) +* state = advance(state) +* sample2 = generate(state) +* ... +* +* In most normal RNGs like mersenne twister the advance function needs to +* be complicated in order to perform the necessary mixing of bits. The state +* needs to be relatively large to contain sufficient numbers of random bits. The generate function +* on the other hand is very simple, often just returning the state (whole or in parts) without further +* computation. +* +* In counter based generators (cRNG), the state and advance function are very simple and +* all the mixing of bits is in the generate function. The state is split into a key and a counter. +* The generate function is an encryption or hash function. Like a hash function, it produces a +* pseudo-random value from the input. The key is used by the generate function the same way as an encryption key. +* The key is randomly seeded in the beginning so that a different sequence of samples is generated on each run. +* Then the key doesn't change anymore. The advance function only increments the counter. The generate function +* produces completely different output even for sequential numbers. +* +* Because their state is simple, cRNG are well suited for parallel applications. To create n independent subsequences from +* a total sequence of N samples you only need to create n counters, where counter i starts at i * (N / n). +* The counter is of minimal size, it only needs to be big enough to fit the number of samples generated. +* Often the subsequence index i is already available in some other form, e.g., the thread index or the agent index in an +* agent based model, so only the lower bits of the subsequence counters need to be stored. +* The key is shared between all subsequences. Modern CPU architectures also are very efficient at executing the hash and +* encryption functions that are used as the generate function, increasing performance of the generator. +* +* The length of the total sequence and the subsequences and the number of subsequences can be adjusted as needed +* by assigning different numbers of bits to the key, the subsequence index and the subsequence counter. +* The counter only needs to store the number of samples generated. A counter of c bits supports a +* sequence of 2^c samples or 2^n subsequences of 2^(c - n) samples, in which case you can split the +* counter into a subsequence index of n bits and a subsequence counter of (c - n) bits where each +* of the subsequence counters starts at 0. +* Generating a samples of k bits requires a key of at least k bits for sufficient randomness. +* Example: +* A 64 bit counter (uint64_t) and a 64 bit key produce 2^64 samples each with 64 bits. +* A subsequence index of 32 bits (uint32_t), 2^32 subsequence counters with 32 bits each and a 64 bit key +* produce 2^32 subsequences of 2^32 samples each with 64 bits. +* +* @see https://github.com/DEShawResearch/random123 for more information. +* +* Classes deriving from this base class need to supply the key and counter by implementing +* the functions +* * result_type get_key() const +* * result_type get_counter() const +* * void increment_counter() +* +* This class satisfies the standard UniformRandomBitGenerator concept. +*/ +template +class RandomNumberGeneratorBase +{ +public: + using result_type = uint64_t; + + /** + * Minimum value generated by this generator. + * Counterbased generators allow the whole range supported by the result_type. + */ + static constexpr result_type min() + { + return std::numeric_limits::min(); + } + + /** + * Maximum value generated by this generator. + * Counterbased generators allow the whole range supported by the result_type. + */ + static constexpr result_type max() + { + return std::numeric_limits::max(); + } + + /** + * Generate the next value in the random sequence. + * Key and counter are supplied by the Derived class by implementing + * the functions get_key(), get_counter(), and increment_counter(). + */ + result_type operator()(); +}; + namespace details { /** @@ -65,7 +158,7 @@ inline r123array2x32 to_r123_array(uint64_t i) std::memcpy(c.data(), &i, sizeof(uint64_t)); return c; } -} +} // namespace details /** * A key type for counter based random number generators. @@ -82,30 +175,22 @@ struct Key : TypeSafe>, OperatorComparison> { * @tparam an unsigned integer type that determines the size of the counter, i.e., the length of the random sequence. */ template -struct Counter : TypeSafe>, - OperatorComparison>, - OperatorAdditionSubtraction> { +struct Counter : TypeSafe>, OperatorComparison>, OperatorAdditionSubtraction> { static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); using TypeSafe>::TypeSafe; }; -/** -* Generate a random 64-bit number. -* A counter based generator with no internal state, increment the counter -* to get the next value in the random sequence. -* Flexible counter size (as many bits as you need) and trivially parallelizable by either -* using subsequences of counters or different keys. -* @tparam C An instance of Counter template type with up to 64 bit. -* @param key Generator key. Can be seeded with initial randomness using mio::seed_rng_key -* @param counter Sequence counter. Increment to get the next value in the sequence. -* @return 64-bit random number. -*/ -template -uint64_t random(Key key, C counter) +template +auto RandomNumberGeneratorBase::operator()() -> result_type { - auto c = static_cast(counter.get()); - auto k = static_cast(key.get()); - return details::to_uint64(threefry2x32(details::to_r123_array(k), details::to_r123_array(c))); + //generate a random sample using the Random123 library. + //Use another threefryNxR algorithm if larger or more random samples are needed than 64 bit. + auto self = static_cast(this); + auto c = static_cast(self->get_counter().get()); + auto k = static_cast(self->get_key().get()); + auto r = details::to_uint64(threefry2x32(details::to_r123_array(k), details::to_r123_array(c))); + self->increment_counter(); + return r; } /** @@ -114,7 +199,7 @@ uint64_t random(Key key, C counter) * @param seed_seq A seed sequence, e.g. initialized using std::random_device. * @return A seeded key. */ -template +template Key seed_rng_key(SeedSeq& seed_seq) { auto tf_key = threefry2x32_key_t::seed(seed_seq); @@ -122,83 +207,72 @@ Key seed_rng_key(SeedSeq& seed_seq) } /** -* Get the counter in the total random sequence for a counter in a given subsequence. -* The total random sequence is split into contiguous subsequences whose length is determined by the size -* of the given subsequence counter. The total sequence has length 2^u, where u is the size in bits -* of the requested counter type. The subsequences have length 2^n, where n is the size in bits of the -* provided counter type. There are up to 2^(u - n) subsequences. -* @tparam U The desired counter type of the total sequence. -* @tparam T Any integer type. -* @tparam C A counter type. Must be smaller than U. -* @param subsequence_idx The index of the subsequence. +* Get the counter in the total sequence for a counter in a given subsequence. +* A total sequence counter of C bits supports 2^N subsequences of 2^(C - N) samples. Then the counter +* can be split into a subsequence index of N bits and a subsequence counter of S = (C - N) bits. +* The length of the subsequences is determined by the type of the subsequence counter and the +* requested total sequence counter. +* The subsequence index does not need to be exactly N bits, it can be larger. E.g., a +* subsequence counter of 16 bits and a subsequence index of 64 bits can be used for +* a combined counter of 64 bits since there is not 48 bit type. +* @tparam UIntC The counter type of the total sequence with C bits. +* @tparam UIntN An unsigned integer type with at least N bits for the subsequence index. +* @tparam CounterS A counter type with S = (C - N) bits for the subsequence counter. +* @param subsequence_idx The index of the subsequence. Must be less than 2^N. * @param counter The counter in the subsequence. * @param return The counter in the total sequence. */ -template -Counter rng_subsequence_counter(T subsequence_idx, C counter) +template +Counter rng_totalsequence_counter(UIntN subsequence_idx, CounterS counter) { - static_assert(sizeof(U) > sizeof(typename C::ValueType), "Subsequence counter must be smaller than requested total counter."); - //TODO allow sizeof(C) = sizeof(U) and check actual values? - //TODO: assert value of subequence_idx - - // - //high bits: subsequence idx - //low bits: subsequence counter - static const U bytes_shift = sizeof(U) - sizeof(C); - auto s = static_cast(subsequence_idx); - return Counter{(s << (bytes_shift * 8)) + static_cast(counter.get())}; + //use UIntC for variables because it's the biggest integer type in this function + static const UIntC BITS_PER_BYTE = 8; + static const UIntC C_BITS = sizeof(UIntC) * BITS_PER_BYTE; + static const UIntC S_BITS = sizeof(CounterS) * BITS_PER_BYTE; + static const UIntC N_BITS = C_BITS - S_BITS; + + static_assert(S_BITS < C_BITS, "Subsequence counter must be smaller than total sequence counter."); + static_assert(N_BITS <= C_BITS, "Subsequence index must not be bigger than total sequence counter."); + static_assert(N_BITS <= sizeof(UIntN) * BITS_PER_BYTE, "Subsequence index must be at least N bits"); + + assert(subsequence_idx <= (1 << N_BITS) && "Subsequence index is too large."); //(1 << N) is the same as (2^N) + + //N high bits: subsequence idx + //S low bits: subsequence counter + //=> C = N + S bits: total sequence counter + const auto i = static_cast(subsequence_idx); + const auto s = static_cast(counter.get()); + const auto c = (i << S_BITS) + s; //shift subsequence index to the high bits, add subsequence counter into low bits + return Counter{c}; } /** -* Get the counter in the given subsequence for the counter in the total sequence. -* @tparam U The desired counter type of the subsequence. Its size defines the length of the subsequence. -* @tparam C The counter type of the total sequence. Must be bigger than U. -* @param counter The counter in the total sequence. +* Get the subsequence counter from the total sequence counter. +* A total sequence counter of C bits supports 2^N subsequences of 2^(C - N) samples. Then the counter +* can be split into a subsequence index of N bits and a subsequence counter of S = (C - N) bits. +* The length of the subsequences is determined by the type of the subsequence counter. +* @tparam UIntS An unsigned integer type of S bits for the subsequence counter. +* @tparam CounterC A counter type of C bits, where C > S. +* @param counter The total sequence counter. * @return The counter in the subsequence. */ -template -Counter rng_subsequence_counter(C counter) -{ - static_assert(sizeof(U) < sizeof(C::ValueType), ""); - //TODO allow sizeof(C) = sizeof(U) and check actual values? - //TODO: assert value of subequence_idx - - //truncate to get the subsequence counter - return Counter(static_cast(counter.get())); -} - -/** -* Base class for counter based random number generator. -* Counter and key are supplied by the derived class. -* Satisfies the standard UniformRandomBitGenerator concept. -*/ -template -class RandomNumberGeneratorBase +template +Counter rng_subsequence_counter(CounterC counter) { -public: - using result_type = uint64_t; + using UIntC = typename CounterC::ValueType; + static const UIntC C_BYTES = sizeof(UIntC); + static const UIntC S_BYTES = sizeof(UIntS); - static constexpr result_type min() - { - return std::numeric_limits::min(); - } + static_assert(S_BYTES < C_BYTES, "Subsequence counter must be smaller than total sequence counter."); - static constexpr result_type max() - { - return std::numeric_limits::max(); - } - - result_type operator()() - { - auto self = static_cast(this); - auto r = random(self->get_key(), self->get_counter()); - self->increment_counter(); - return r; - } -}; + //the subsequence counter is in the lower bits of total sequence counter + //see rng_totalsequence_counter above + //so we just need to truncate the counter to get the subsequence counter + return Counter(static_cast(counter.get())); +} /** -* Counter based random number generator. +* General purpose counter based random number generator. * Stores its own key and counter. * @see RandomNumberGeneratorBase. */ @@ -211,16 +285,20 @@ class RandomNumberGenerator : public RandomNumberGeneratorBase get_key() const { + Key get_key() const + { return m_key; } - Counter get_counter() const { + Counter get_counter() const + { return m_counter; } - void set_counter(Counter counter) { + void set_counter(Counter counter) + { m_counter = counter; } - void increment_counter() { + void increment_counter() + { ++m_counter; } static std::vector generate_seeds() @@ -229,14 +307,14 @@ class RandomNumberGenerator : public RandomNumberGeneratorBase& seeds) + void seed(const std::vector& seeds) { m_seeds = seeds; std::seed_seq seed_seq(m_seeds.begin(), m_seeds.end()); m_key = seed_rng_key(seed_seq); } - const std::vector get_seeds() const + const std::vector get_seeds() const { return m_seeds; } @@ -272,10 +350,14 @@ class RandomNumberGenerator : public RandomNumberGeneratorBase ResultType operator()(RNG& rng, T&&... params) { - if (m_generator) { + if (m_generator) { //unlikely outside of tests return m_generator(typename DistT::param_type{std::forward(params)...}); - } else { + } + else { return DistT(std::forward(params)...)(rng); } } @@ -391,8 +477,10 @@ class DistributionAdapter } /** - * get a static thread local instance of this class. + * get a static instance of this class. * Instance is default constructed on the first call. + * The generator function of this instance can be replaced + * for mocking during tests. */ static DistributionAdapter& get_instance() { @@ -407,7 +495,8 @@ class DistributionAdapter /** * select a random integer in [0, n) with weights [w_0, ..., w_(n-1)] * the probability to pick i is w_i/S where S is the sum of all weights. - * similar to std::discrete_distribution but does not allocate. + * Similar to std::discrete_distribution but does not allocate, instead + * expects a Span of weights. * Models the standard RandomNumberDistribution concept. */ template diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index 696366caa4..b337a50a9c 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -53,7 +53,7 @@ class Infection /** * @brief Create an Infection for a single Person. * Draws a random infection course. - * @param[inout] rng Random number generator. + * @param[inout] rng RandomNumberGenerator for the person. * @param[in] virus Virus type of the Infection. * @param[in] age AgeGroup to determine the ViralLoad course. * @param[in] init_date Date of initializing the Infection. @@ -111,7 +111,7 @@ class Infection * @brief Determine ViralLoad course and Infection course based on init_state. * Calls draw_infection_course_backward for all #InfectionState%s prior and draw_infection_course_forward for all * subsequent #InfectionState%s. - * @param[inout] rng Random number generator. + * @param[inout] rng RandomNumberGenerator of the person. * @param[in] age AgeGroup of the Person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. @@ -124,7 +124,7 @@ class Infection /** * @brief Determine ViralLoad course and Infection course prior to the given start_state. - * @param[inout] rng Random number generator. + * @param[inout] rng RandomNumberGenerator of the person. * @param[in] age AgeGroup of the Person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. @@ -136,7 +136,7 @@ class Infection /** * @brief Determine ViralLoad course and Infection course subsequent to the given start_state. - * @param[inout] rng Random number generator. + * @param[inout] rng RandomNumberGenerator of the person. * @param[in] age AgeGroup of the person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index e450c9ed35..7d54d9f804 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -160,7 +160,7 @@ class Location /** * @brief A Person interacts with the population at this Location and may become infected. - * @param[in, out] rng Random number generator for this person. + * @param[in, out] rng RandomNumberGenerator for this person. * @param[in, out] person The Person that interacts with the population. * @param[in] dt Length of the current Simulation time step. * @param[in] global_params Global infection parameters. diff --git a/cpp/models/abm/migration_rules.h b/cpp/models/abm/migration_rules.h index a3bfca69ee..5052cfe530 100644 --- a/cpp/models/abm/migration_rules.h +++ b/cpp/models/abm/migration_rules.h @@ -33,7 +33,7 @@ namespace abm /** * @name Rules for migration between Location%s. - * @param[inout] rng Random number generator for the person. + * @param[inout] rng RandomNumberGenerator for the person. * @param[in] p Person the rule is applied to. * @param[in] t Current time. * @param[in] dt Length of the time step. diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 02bf6e835c..0f4ccdc0cd 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -51,14 +51,14 @@ class Person public: /** * Random number generator of individual persons. - * Increments the rng counter of the person when used. + * Increments the random number generator counter of the person when used. * @see mio::RandomNumberGeneratorBase */ class RandomNumberGenerator : public RandomNumberGeneratorBase { public: /** - * Creates a random number generator for a person. + * Creates a RandomNumberGenerator for a person. * @param key Key to be used by the generator. * @param person Person who's counter will be used. */ @@ -69,9 +69,9 @@ class Person } /** - * Creates a random number generator for a person. - * Uses the same key as another random number generator. - * @param rng Random number generator who's key will be used. + * Creates a RandomNumberGenerator for a person. + * Uses the same key as another RandomNumberGenerator. + * @param rng RandomNumberGenerator who's key will be used. * @param person Person who's counter will be used. */ RandomNumberGenerator(const mio::RandomNumberGenerator& rng, Person& person) @@ -92,7 +92,7 @@ class Person */ Counter get_counter() const { - return rng_subsequence_counter(m_person.get_person_id(), m_person.get_rng_counter()); + return rng_totalsequence_counter(m_person.get_person_id(), m_person.get_rng_counter()); } /** @@ -110,7 +110,7 @@ class Person /** * @brief Create a Person. - * @param[in, out] rng Random number generator. + * @param[in, out] rng RandomNumberGenerator. * @param[in, out] location Initial location of the Person. * @param[in] age The AgeGroup of the Person. * @param[in] person_id Index of the Person. @@ -312,7 +312,7 @@ class Person * @brief Simulates a viral test and returns the test result of the Person. * If the test is positive, the Person has to quarantine. * If the test is negative, quarantine ends. - * @param[inout] rng Random number generator. + * @param[inout] rng RandomNumberGenerator of the person. * @param[in] t TimePoint of the test. * @param[in] params Sensitivity and specificity of the test method. * @return True if the test result of the Person is positive. @@ -380,7 +380,7 @@ class Person /** * @brief Checks whether the Person wears a Mask at the target Location. - * @param[inout] rng Random number generator. + * @param[inout] rng RandomNumberGenerator of the person. * @param[in] target The target Location. * @return Whether a Person wears a Mask at the Location. */ @@ -427,7 +427,7 @@ class Person } /** - * Get this persons random number generator counter. + * Get this persons RandomNumberGenerator counter. * @see mio::abm::Person::RandomNumberGenerator. */ Counter& get_rng_counter() diff --git a/cpp/models/abm/random_events.h b/cpp/models/abm/random_events.h index 7163b1a785..b709cd9e9e 100644 --- a/cpp/models/abm/random_events.h +++ b/cpp/models/abm/random_events.h @@ -40,7 +40,7 @@ namespace abm * @tparam RNG Type that satisfies the UniformRandomBitGenerator concept. * @tparam T Type that represents the states. * @tparam NumTransitions Number of possible transitions. - * @param[inout] rng Random number generator. + * @param[inout] rng RandomNumberGenerator. * @param[in] current_state Current state before transition. * @param[in] dt Length of the time step. * @param[in] transitions Array of pairs of new states and their rates (probabilities). diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index a959fe83b1..7340d1c3b3 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -177,7 +177,7 @@ class TestingScheme /** * @brief Runs the TestingScheme and potentially tests a Person. - * @param[inout] rng Random number generator for the person being tested. + * @param[inout] rng RandomNumberGenerator for the person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the scheme. @@ -229,7 +229,7 @@ class TestingStrategy /** * @brief Runs the TestingStrategy and potentially tests a Person. - * @param[inout] rng Random number generator for the person being tested. + * @param[inout] rng RandomNumberGenerator for the person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the strategy. diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 6803f37e0d..90019fcc94 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -184,7 +184,7 @@ class World const TestingStrategy& get_testing_strategy() const; /** - * Get the random number generator used by this world for random events. + * Get the RandomNumberGenerator used by this world for random events. * Persons use their own generators with the same key as the global one. * @return The random number generator. */ diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index c608fd2f59..d6689dd1ac 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -277,9 +277,9 @@ TEST(Person, rng) ASSERT_EQ(p.get_rng_counter(), mio::Counter(0)); auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); - ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::Counter{0})); + ASSERT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{0})); p_rng(); ASSERT_EQ(p.get_rng_counter(), mio::Counter(1)); - ASSERT_EQ(p_rng.get_counter(), mio::rng_subsequence_counter(13, mio::Counter{1})); + ASSERT_EQ(p_rng.get_counter(), mio::rng_totalsequence_counter(13, mio::Counter{1})); } From f18bff9265550045505f2ad6758017b4430bdca7 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:04:52 +0200 Subject: [PATCH 024/105] extend unittests for counter based rngs --- cpp/tests/test_random_number_generator.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/cpp/tests/test_random_number_generator.cpp b/cpp/tests/test_random_number_generator.cpp index 20f4a2a96c..be7bbcc345 100644 --- a/cpp/tests/test_random_number_generator.cpp +++ b/cpp/tests/test_random_number_generator.cpp @@ -2,29 +2,43 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include +#include #include -TEST(RandomNumberGenerator, set_counter) +TEST(RandomNumberGenerator, counter) { mio::RandomNumberGenerator rng; uint64_t n = 10; for (auto i = uint64_t(0); i < n; ++i) { rng(); } - ASSERT_EQ(rng.get_counter(), mio::Counter(n)); + ASSERT_EQ(rng.get_counter(), mio::Counter(n)); //counter was incremented + auto s1 = rng(); for (auto i = uint64_t(0); i < n; ++i) { rng(); } - ASSERT_EQ(rng.get_counter(), mio::Counter(2 * n + 1)); + ASSERT_EQ(rng.get_counter(), mio::Counter(2 * n + 1)); //counter was further incremented rng.set_counter(mio::Counter{n}); - ASSERT_EQ(rng.get_counter(), mio::Counter(n)); + ASSERT_EQ(rng.get_counter(), mio::Counter(n)); //counter was reverted auto s2 = rng(); ASSERT_EQ(s1, s2); } +TEST(RandomNumberGenerator, subsequences) +{ + uint32_t subsequence_index{5}; + mio::Counter subsequence_counter{12}; + + auto counter = mio::rng_totalsequence_counter(subsequence_index, subsequence_counter); + ASSERT_EQ(counter, mio::Counter{5 * 4'294'967'296ll + 12}); + + auto subsequence_counter2 = mio::rng_subsequence_counter(counter); + ASSERT_EQ(subsequence_counter2, subsequence_counter); +} + TEST(TestDiscreteDistribution, generate) { auto distribution = mio::DiscreteDistributionInPlace(); From 516f31fe6eec67850c0c76d7e2c4216b105d70b5 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:09:24 +0200 Subject: [PATCH 025/105] references to Person::RandomNUmberGenerator --- cpp/models/abm/infection.h | 8 ++++---- cpp/models/abm/location.h | 2 +- cpp/models/abm/migration_rules.h | 2 +- cpp/models/abm/testing_strategy.h | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/models/abm/infection.h b/cpp/models/abm/infection.h index b337a50a9c..57a240b43d 100644 --- a/cpp/models/abm/infection.h +++ b/cpp/models/abm/infection.h @@ -53,7 +53,7 @@ class Infection /** * @brief Create an Infection for a single Person. * Draws a random infection course. - * @param[inout] rng RandomNumberGenerator for the person. + * @param[inout] rng Person::RandomNumberGenerator for the person. * @param[in] virus Virus type of the Infection. * @param[in] age AgeGroup to determine the ViralLoad course. * @param[in] init_date Date of initializing the Infection. @@ -111,7 +111,7 @@ class Infection * @brief Determine ViralLoad course and Infection course based on init_state. * Calls draw_infection_course_backward for all #InfectionState%s prior and draw_infection_course_forward for all * subsequent #InfectionState%s. - * @param[inout] rng RandomNumberGenerator of the person. + * @param[inout] rng Person::RandomNumberGenerator of the Person. * @param[in] age AgeGroup of the Person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. @@ -124,7 +124,7 @@ class Infection /** * @brief Determine ViralLoad course and Infection course prior to the given start_state. - * @param[inout] rng RandomNumberGenerator of the person. + * @param[inout] rng Person::RandomNumberGenerator of the Person. * @param[in] age AgeGroup of the Person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. @@ -136,7 +136,7 @@ class Infection /** * @brief Determine ViralLoad course and Infection course subsequent to the given start_state. - * @param[inout] rng RandomNumberGenerator of the person. + * @param[inout] rng Person::RandomNumberGenerator of the person. * @param[in] age AgeGroup of the person. * @param[in] params GlobalInfectionParameters. * @param[in] init_date Date of initializing the Infection. diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 7d54d9f804..17952281f8 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -160,7 +160,7 @@ class Location /** * @brief A Person interacts with the population at this Location and may become infected. - * @param[in, out] rng RandomNumberGenerator for this person. + * @param[in, out] rng Person::RandomNumberGenerator for this Person. * @param[in, out] person The Person that interacts with the population. * @param[in] dt Length of the current Simulation time step. * @param[in] global_params Global infection parameters. diff --git a/cpp/models/abm/migration_rules.h b/cpp/models/abm/migration_rules.h index 5052cfe530..47f01c2e80 100644 --- a/cpp/models/abm/migration_rules.h +++ b/cpp/models/abm/migration_rules.h @@ -33,7 +33,7 @@ namespace abm /** * @name Rules for migration between Location%s. - * @param[inout] rng RandomNumberGenerator for the person. + * @param[inout] rng Person::RandomNumberGenerator for the person. * @param[in] p Person the rule is applied to. * @param[in] t Current time. * @param[in] dt Length of the time step. diff --git a/cpp/models/abm/testing_strategy.h b/cpp/models/abm/testing_strategy.h index 7340d1c3b3..63d06dbb95 100644 --- a/cpp/models/abm/testing_strategy.h +++ b/cpp/models/abm/testing_strategy.h @@ -177,7 +177,7 @@ class TestingScheme /** * @brief Runs the TestingScheme and potentially tests a Person. - * @param[inout] rng RandomNumberGenerator for the person being tested. + * @param[inout] rng Person::RandomNumberGenerator for the Person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the scheme. @@ -229,7 +229,7 @@ class TestingStrategy /** * @brief Runs the TestingStrategy and potentially tests a Person. - * @param[inout] rng RandomNumberGenerator for the person being tested. + * @param[inout] rng Person::RandomNumberGenerator for the Person being tested. * @param[in] person Person to check. * @param[in] location Location to check. * @param[in] t TimePoint when to run the strategy. From ab6b1f5388a53ef141306c82808aaf6b437e27d8 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:10:41 +0200 Subject: [PATCH 026/105] review: consistent cmake args --- cpp/thirdparty/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/thirdparty/CMakeLists.txt b/cpp/thirdparty/CMakeLists.txt index 1dca677548..6319e52a16 100644 --- a/cpp/thirdparty/CMakeLists.txt +++ b/cpp/thirdparty/CMakeLists.txt @@ -115,7 +115,7 @@ if (MEMILIO_ENABLE_MPI) endif() if (MEMILIO_ENABLE_OPENMP) - find_package(OpenMP COMPONENTS CXX REQUIRED) + find_package(OpenMP REQUIRED COMPONENTS CXX) endif() #Random123 library for random number generators From 98b71d833d106ba129e774eb97e1bbba61060903 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:15:04 +0200 Subject: [PATCH 027/105] review: fix unwrapped pragma omp --- cpp/models/abm/simulation.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index c395f39f3b..b4a5f9c517 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -19,6 +19,7 @@ */ #include "abm/simulation.h" #include "memilio/utils/logging.h" +#include "memilio/utils/mioomp.h" #include "memilio/utils/random_number_generator.h" #include @@ -65,15 +66,15 @@ void Simulation::store_result_at(TimePoint t) { m_result.add_time_point(t.days()); m_result.get_last_value().setZero(); - #pragma omp parallel + PRAGMA_OMP(parallel) { Eigen::VectorXd sum = Eigen::VectorXd::Zero(m_result.get_num_elements()); - #pragma omp for + PRAGMA_OMP(for) for (auto i = size_t(0); i < m_world.get_locations().size(); ++i) { auto&& location = m_world.get_locations()[i]; sum += location.get_subpopulations().get_last_value().cast(); } - #pragma omp critical + PRAGMA_OMP(critical) { m_result.get_last_value() += sum; } From 4342ad7ec3c05425d504cbfe5ddd6446ef0364dc Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:20:23 +0200 Subject: [PATCH 028/105] review: comment for manual openmp reduction --- cpp/models/abm/simulation.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index b4a5f9c517..698ed6943f 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -66,14 +66,19 @@ void Simulation::store_result_at(TimePoint t) { m_result.add_time_point(t.days()); m_result.get_last_value().setZero(); + + //Use a manual parallel reduction to sum up the subpopulations + //The reduction clause of `omp parallel for` doesn't work well for `Eigen::VectorXd` PRAGMA_OMP(parallel) { + //thread local sum of subpopulations, computed in parallel Eigen::VectorXd sum = Eigen::VectorXd::Zero(m_result.get_num_elements()); PRAGMA_OMP(for) for (auto i = size_t(0); i < m_world.get_locations().size(); ++i) { auto&& location = m_world.get_locations()[i]; sum += location.get_subpopulations().get_last_value().cast(); } + //synchronized total sum PRAGMA_OMP(critical) { m_result.get_last_value() += sum; From 40795fe045d2ded37f48497cea1ce9ecf1a39603 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:26:45 +0200 Subject: [PATCH 029/105] review: document openmp pragma macro --- cpp/memilio/utils/mioomp.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cpp/memilio/utils/mioomp.h b/cpp/memilio/utils/mioomp.h index 6a11ea2798..7f26c76e50 100644 --- a/cpp/memilio/utils/mioomp.h +++ b/cpp/memilio/utils/mioomp.h @@ -7,10 +7,21 @@ #ifdef MEMILIO_ENABLE_OPENMP #include "omp.h" +/** +* Macro for OpenMP directives. +* OpenMP is enabled. +* `PRAGMA_OMP(parallel for)` evaluates to `#pragma omp parallel for` +*/ #define PRAGMA_OMP(x) _Pragma(QUOTE(omp x)) #else +/** +* Macro for OpenMP directives. +* Evaluates to nothing because OpenMP is disabled. +* Unknown pragmas are ignored by the compiler, but the compiler emits warnings, so +* we remove the #pragma completely. +*/ #define PRAGMA_OMP(x) #endif From 17d5e734426be44a1cc90ee21f45f2d21a462955 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:45:15 +0200 Subject: [PATCH 030/105] review: further improved doc of cRNG --- cpp/memilio/utils/random_number_generator.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 6d7440115e..b09434a844 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -77,9 +77,10 @@ namespace mio * * Because their state is simple, cRNG are well suited for parallel applications. To create n independent subsequences from * a total sequence of N samples you only need to create n counters, where counter i starts at i * (N / n). +* Normal RNGs need special algorithms to efficiently generate subsequences, if it is possible at all. * The counter is of minimal size, it only needs to be big enough to fit the number of samples generated. * Often the subsequence index i is already available in some other form, e.g., the thread index or the agent index in an -* agent based model, so only the lower bits of the subsequence counters need to be stored. +* agent based model, so only a small amount of extra storage is needed for the subsequence counter. * The key is shared between all subsequences. Modern CPU architectures also are very efficient at executing the hash and * encryption functions that are used as the generate function, increasing performance of the generator. * @@ -91,11 +92,16 @@ namespace mio * of the subsequence counters starts at 0. * Generating a samples of k bits requires a key of at least k bits for sufficient randomness. * Example: -* A 64 bit counter (uint64_t) and a 64 bit key produce 2^64 samples each with 64 bits. -* A subsequence index of 32 bits (uint32_t), 2^32 subsequence counters with 32 bits each and a 64 bit key -* produce 2^32 subsequences of 2^32 samples each with 64 bits. +* * A 64 bit counter (uint64_t) and a 64 bit key produce 2^64 samples each with 64 bits. You need to store +* one counter and one key. +* * A subsequence index of 32 bits (uint32_t), subsequence counter with 32 bits and a 64 bit key +* produce 2^32 subsequences of 2^32 samples each with 64 bits per sample. You need to store 2^32 subsequence indices, +* 2^32 subsequence counters and one key, but the counters are completely independent and thread safe. The subsequence index +* and corresponding subsequence counter can also be stored together in one 64bit counter, e.g., the subsequence index is the +* high bits and subsequence counter is the low bits, see `rng_totalsequence_index()`. * -* @see https://github.com/DEShawResearch/random123 for more information. +* Also see https://github.com/DEShawResearch/random123 for more information on cRNGs and the specific cRNG +* we use. * * Classes deriving from this base class need to supply the key and counter by implementing * the functions @@ -240,6 +246,10 @@ Counter rng_totalsequence_counter(UIntN subsequence_idx, CounterS counter //N high bits: subsequence idx //S low bits: subsequence counter //=> C = N + S bits: total sequence counter + //example: + //subsequence index uint32_t(181) = 0x000000B5 + //subsequence counter uint32_t(41309) = 0x0000A15D + //total sequence counter = 0x000000B50000A15D const auto i = static_cast(subsequence_idx); const auto s = static_cast(counter.get()); const auto c = (i << S_BITS) + s; //shift subsequence index to the high bits, add subsequence counter into low bits From 41ea10505086fc6f524f0d3dfb97500ddd2cb79d Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:49:00 +0200 Subject: [PATCH 031/105] review: doc of Person::RandomNumberGenerator --- cpp/models/abm/person.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 0f4ccdc0cd..5e2a2985d3 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -52,6 +52,15 @@ class Person /** * Random number generator of individual persons. * Increments the random number generator counter of the person when used. + * Does not store its own key or counter. + * Instead the key needs to be provided from the outside, so that the RNG + * for all persons share the same key. + * The counter is taken from the person. + * Person::RandomNumberGenerator is cheap to construct and transparent + * for the compiler to optimize, so we don't store the RNG persistently, only the + * counter, so we don't need to store the key in each person. This increases + * consistency (if the key is changed after the person is created) and + * reduces the memory required per person. * @see mio::RandomNumberGeneratorBase */ class RandomNumberGenerator : public RandomNumberGeneratorBase From 660d1fdb088e541243848337896f40e1226d6487 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:50:34 +0200 Subject: [PATCH 032/105] review: consistent order of arguments --- cpp/models/abm/person.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 5e2a2985d3..141b0a181f 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -72,8 +72,8 @@ class Person * @param person Person who's counter will be used. */ RandomNumberGenerator(Key key, Person& person) - : m_person(person) - , m_key(key) + : m_key(key) + , m_person(person) { } @@ -113,8 +113,8 @@ class Person } private: - Person& m_person; Key m_key; + Person& m_person; }; /** From 28840c5754d0f537669019bd7dcda3dfabf288ac Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 17:52:00 +0200 Subject: [PATCH 033/105] review: rename variable in unitest --- cpp/tests/test_abm_migration_rules.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index fa8108876b..e08ad6921a 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -388,11 +388,11 @@ TEST(TestMigrationRules, event_return) auto dt = mio::abm::hours(3); mio::abm::Location home(mio::abm::LocationType::Home, 0); - mio::abm::Location shop(mio::abm::LocationType::SocialEvent, 0); + mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 0); auto p = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); home.add_person(p); - p.migrate_to(shop); + p.migrate_to(social_event); p.interact(rng_p, t, dt, params); ASSERT_EQ(mio::abm::go_to_event(rng_p, p, t, dt, {}), mio::abm::LocationType::Home); From 6063600d3809a2b31b78e80818ee595d94da37a8 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 18:24:25 +0200 Subject: [PATCH 034/105] review: comment for rng handling in parameterstudies --- cpp/memilio/compartments/parameter_studies.h | 46 +++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/cpp/memilio/compartments/parameter_studies.h b/cpp/memilio/compartments/parameter_studies.h index 97b527ceaf..1971cd39c1 100644 --- a/cpp/memilio/compartments/parameter_studies.h +++ b/cpp/memilio/compartments/parameter_studies.h @@ -30,6 +30,7 @@ #include "memilio/compartments/simulation.h" #include +#include #include #include #include @@ -140,7 +141,12 @@ class ParameterStudy num_procs = 1; rank = 0; #endif + + //The ParameterDistributions used for sampling parameters use thread_local_rng() + //So we set our own RNG to be used. + //Assume that sampling uses the thread_local_rng() and isn't multithreaded m_rng.synchronize(); + thread_local_rng() = m_rng; auto run_distribution = distribute_runs(m_num_runs, num_procs); auto start_run_idx = std::accumulate(run_distribution.begin(), run_distribution.begin() + size_t(rank), size_t(0)); @@ -152,18 +158,16 @@ class ParameterStudy for (size_t run_idx = start_run_idx; run_idx < end_run_idx; run_idx++) { log(LogLevel::info, "ParameterStudies: run {}", run_idx); - //prepare rng for this run - //assume that sampling the graph uses the thread local rng and isn't multithreaded - auto initial_rng_counter = - rng_totalsequence_counter(static_cast(run_idx), Counter(0)); - m_rng.set_counter(initial_rng_counter); - thread_local_rng() = m_rng; + //prepare rng for this run by setting the counter to the right offset + //Add the old counter so that this call of run() produces different results + //from the previous call + auto run_rng_counter = m_rng.get_counter() + rng_totalsequence_counter( + static_cast(run_idx), Counter(0)); + thread_local_rng().set_counter(run_rng_counter); //sample auto sim = create_sampled_simulation(sample_graph); - - m_rng = thread_local_rng(); - log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", (m_rng.get_counter() - initial_rng_counter).get()); + log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", (thread_local_rng().get_counter() - run_rng_counter).get()); //perform run sim.advance(m_tmax); @@ -172,6 +176,9 @@ class ParameterStudy ensemble_result.emplace_back(result_processing_function(std::move(sim).get_graph(), run_idx)); } + //Set the counter of our RNG so that future calls of run() produce different parameters. + m_rng.set_counter(m_rng.get_counter() + rng_totalsequence_counter(m_num_runs, Counter(0))); + #ifdef MEMILIO_ENABLE_MPI //gather results if (rank == 0) { @@ -213,25 +220,32 @@ class ParameterStudy std::vector ensemble_result; ensemble_result.reserve(m_num_runs); + //The ParameterDistributions used for sampling parameters use thread_local_rng() + //So we set our own RNG to be used. + //Assume that sampling uses the thread_local_rng() and isn't multithreaded thread_local_rng() = m_rng; for (size_t i = 0; i < m_num_runs; i++) { log(LogLevel::info, "ParameterStudies: run {}", i); - //prepare rng for this run - //assume that sampling the graph uses the thread local rng and isn't multithreaded - auto initial_rng_counter = - rng_totalsequence_counter(static_cast(i), Counter(0)); - thread_local_rng().set_counter(initial_rng_counter); + //prepare rng for this run by setting the counter to the right offset + //Add the old counter so that this call of run() produces different results + //from the previous call + auto run_rng_counter = m_rng.get_counter() + + rng_totalsequence_counter(static_cast(i), Counter(0)); + thread_local_rng().set_counter(run_rng_counter); + auto sim = create_sampled_simulation(sample_graph); - log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", (m_rng.get_counter() - initial_rng_counter).get()); + log(LogLevel::info, "ParameterStudies: Generated {} random numbers.", + (thread_local_rng().get_counter() - run_rng_counter).get()); sim.advance(m_tmax); ensemble_result.emplace_back(std::move(sim).get_graph()); } - m_rng = thread_local_rng(); + //Set the counter of our RNG so that future calls of run() produce different parameters. + m_rng.set_counter(m_rng.get_counter() + rng_totalsequence_counter(m_num_runs, Counter(0))); return ensemble_result; } From 4fc460806474d77f8ab937435ee4b8ab039d2da9 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 4 Aug 2023 23:04:23 +0200 Subject: [PATCH 035/105] fix build and test issues due to rebase --- cpp/examples/abm_history_object.cpp | 11 ++++++----- cpp/examples/graph_stochastic_mobility.cpp | 2 +- cpp/memilio/mobility/graph_simulation.h | 12 +++++++++--- cpp/memilio/utils/random_number_generator.h | 6 +++--- cpp/models/abm/infection.cpp | 2 +- cpp/models/abm/location.cpp | 6 +++--- cpp/models/abm/migration_rules.cpp | 4 ++-- cpp/models/abm/world.cpp | 8 ++++---- cpp/models/abm/world.h | 3 ++- cpp/tests/test_abm_infection.cpp | 9 +++++++-- cpp/tests/test_graph_simulation.cpp | 10 +++++----- 11 files changed, 43 insertions(+), 30 deletions(-) diff --git a/cpp/examples/abm_history_object.cpp b/cpp/examples/abm_history_object.cpp index eebb701b62..5b8d35204a 100644 --- a/cpp/examples/abm_history_object.cpp +++ b/cpp/examples/abm_history_object.cpp @@ -44,13 +44,13 @@ void write_log_to_file(const T& history) std::string input; std::ofstream myfile("test_output.txt"); myfile << "Locations as numbers:\n"; - for (auto loc_id_index = 0; loc_id_index < loc_id[0].size(); ++loc_id_index) { - myfile << convert_loc_id_to_string(loc_id[0][loc_id_index]) << "\n"; + for (auto&& id : loc_id[0]) { + myfile << convert_loc_id_to_string(id) << "\n"; } myfile << "Timepoints:\n"; - for (int t = 0; t < time_points.size(); ++t) { - input += std::to_string(time_points[t]) + " "; + for (auto&& t : time_points) { + input += std::to_string(t) + " "; } myfile << input << "\n"; @@ -132,10 +132,11 @@ int main() // The infection states are chosen randomly. auto persons = world.get_persons(); for (auto& person : persons) { + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); mio::abm::InfectionState infection_state = (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(mio::abm::VirusVariant::Wildtype, person.get_age(), + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), world.get_global_infection_parameters(), start_date, infection_state)); } diff --git a/cpp/examples/graph_stochastic_mobility.cpp b/cpp/examples/graph_stochastic_mobility.cpp index 5da882c4bf..f89766b0a5 100644 --- a/cpp/examples/graph_stochastic_mobility.cpp +++ b/cpp/examples/graph_stochastic_mobility.cpp @@ -27,7 +27,7 @@ #include -int main(int argc, char** argv) +int main(int /*argc*/, char** /*argv*/) { const auto t0 = 0.; const auto tmax = 10.; diff --git a/cpp/memilio/mobility/graph_simulation.h b/cpp/memilio/mobility/graph_simulation.h index b15bf7f08d..4956af81e5 100644 --- a/cpp/memilio/mobility/graph_simulation.h +++ b/cpp/memilio/mobility/graph_simulation.h @@ -145,7 +145,7 @@ class GraphSimulationStochastic void advance(double t_max) { //draw normalized waiting time - ScalarType normalized_waiting_time = ExponentialDistribution::get_instance()(1.0); + ScalarType normalized_waiting_time = ExponentialDistribution::get_instance()(m_rng, 1.0); std::vector dt_cand(Base::m_graph.nodes().size()); ScalarType cumulative_rate = 0; //cumulative transition rate size_t parameters_per_edge = @@ -161,7 +161,7 @@ class GraphSimulationStochastic //evaluate rates get_rates(m_rates); //draw transition event - size_t event = mio::DiscreteDistribution::get_instance()(m_rates); + size_t event = mio::DiscreteDistribution::get_instance()(m_rng, m_rates); //edge that performs transition event auto& event_edge = Base::m_graph.edges()[event / parameters_per_edge]; //index for compartment and age group migrating @@ -188,7 +188,7 @@ class GraphSimulationStochastic cumulative_rate = get_cumulative_transition_rate(); //draw new normalized waiting time - normalized_waiting_time = ExponentialDistribution::get_instance()(1.0); + normalized_waiting_time = ExponentialDistribution::get_instance()(m_rng, 1.0); } while (cumulative_rate * Base::m_dt > normalized_waiting_time); } @@ -211,6 +211,11 @@ class GraphSimulationStochastic } } + RandomNumberGenerator& get_rng() + { + return m_rng; + } + private: ScalarType get_cumulative_transition_rate() { @@ -239,6 +244,7 @@ class GraphSimulationStochastic } std::vector m_rates; + RandomNumberGenerator m_rng; }; template diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index b09434a844..4af8144578 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -27,13 +27,13 @@ #include "memilio/utils/span.h" #include "memilio/utils/type_safe.h" -MSVC_WARNING_DISABLE_PUSH(4127) +MSVC_WARNING_DISABLE_PUSH(4127) //conditional expression is constant GCC_CLANG_DIAGNOSTIC(push) GCC_CLANG_DIAGNOSTIC(ignored "-Wexpansion-to-defined") //Random123 handles the portability of this warning internally #include "Random123/array.h" #include "Random123/threefry.h" GCC_CLANG_DIAGNOSTIC(pop) -MSVC_WARNING_POP +MSVC_WARNING_POP() #include #include @@ -241,7 +241,7 @@ Counter rng_totalsequence_counter(UIntN subsequence_idx, CounterS counter static_assert(N_BITS <= C_BITS, "Subsequence index must not be bigger than total sequence counter."); static_assert(N_BITS <= sizeof(UIntN) * BITS_PER_BYTE, "Subsequence index must be at least N bits"); - assert(subsequence_idx <= (1 << N_BITS) && "Subsequence index is too large."); //(1 << N) is the same as (2^N) + assert(UIntC(subsequence_idx) <= (UIntC(1) << N_BITS) && "Subsequence index is too large."); //(1 << N) is the same as (2^N) //N high bits: subsequence idx //S low bits: subsequence counter diff --git a/cpp/models/abm/infection.cpp b/cpp/models/abm/infection.cpp index 9073e9b4e4..dab44908bc 100644 --- a/cpp/models/abm/infection.cpp +++ b/cpp/models/abm/infection.cpp @@ -41,7 +41,7 @@ Infection::Infection(Person::RandomNumberGenerator& rng, VirusVariant virus, Age m_viral_load.incline = vl_params.viral_load_incline.get_distribution_instance()(rng, vl_params.viral_load_incline.params); m_viral_load.decline = - vl_params.viral_load_decline.get_distribution_instance()(vl_params.viral_load_decline.params); + vl_params.viral_load_decline.get_distribution_instance()(rng, vl_params.viral_load_decline.params); m_viral_load.end_date = m_viral_load.start_date + days(int(m_viral_load.peak / m_viral_load.incline - m_viral_load.peak / m_viral_load.decline)); diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index a3e81a978a..63e2227295 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -96,9 +96,9 @@ void Location::cache_exposure_rates(TimePoint t, TimeSpan dt) cell.m_cached_exposure_rate_air = {{VirusVariant::Count}, 0.}; for (auto&& p : cell.m_persons) { if (p->is_infected(t)) { - auto& inf = p->get_infection(); - auto& virus = inf.get_virus_variant(); - auto age = p->get_age(); + auto& inf = p->get_infection(); + auto virus = inf.get_virus_variant(); + auto age = p->get_age(); /* average infectivity over the time step * to second order accuracy using midpoint rule */ diff --git a/cpp/models/abm/migration_rules.cpp b/cpp/models/abm/migration_rules.cpp index 3c7b137460..20d1b75031 100644 --- a/cpp/models/abm/migration_rules.cpp +++ b/cpp/models/abm/migration_rules.cpp @@ -167,8 +167,8 @@ LocationType return_home_when_recovered(Person::RandomNumberGenerator& /*rng*/, return current_loc; } -LocationType get_buried(const Person& person, const TimePoint t, TimeSpan /*dt*/, - const MigrationParameters& /*params*/) +LocationType get_buried(Person::RandomNumberGenerator& /*rng*/, const Person& person, const TimePoint t, + TimeSpan /*dt*/, const MigrationParameters& /*params*/) { auto current_loc = person.get_location().get_type(); if (person.get_infection_state(t) == InfectionState::Dead) { diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index d7c3d53726..0e0ffb760f 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -73,9 +73,9 @@ void World::interaction(TimePoint t, TimeSpan dt) void World::migration(TimePoint t, TimeSpan dt) { - std::vector>> - m_enhanced_migration_rules; + enhanced_migration_rules; for (auto rule : m_migration_rules) { //check if transition rule can be applied bool nonempty = false; @@ -88,14 +88,14 @@ void World::migration(TimePoint t, TimeSpan dt) } if (nonempty) { - m_enhanced_migration_rules.push_back(rule); + enhanced_migration_rules.push_back(rule); } } PRAGMA_OMP(parallel for) for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); - for (auto rule : m_migration_rules) { + for (auto rule : enhanced_migration_rules) { //check if transition rule can be applied auto target_type = rule.first(personal_rng, *person, t, dt, m_migration_parameters); auto& target_location = find_location(target_type, *person); diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index 90019fcc94..a1d94b527a 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -215,7 +215,8 @@ class World MigrationParameters m_migration_parameters; ///< Parameters that describe the migration between Location%s. TripList m_trip_list; ///< List of all Trip%s the Person%s do. bool m_use_migration_rules; ///< Whether migration rules are considered. - std::vector>> m_migration_rules; ///< Rules that govern the migration between Location%s. LocationId m_cemetery_id; // Central cemetery for all dead persons. diff --git a/cpp/tests/test_abm_infection.cpp b/cpp/tests/test_abm_infection.cpp index 283a858c92..35c8702327 100644 --- a/cpp/tests/test_abm_infection.cpp +++ b/cpp/tests/test_abm_infection.cpp @@ -22,6 +22,7 @@ #include "abm/person.h" #include "abm_helpers.h" #include "memilio/utils/random_number_generator.h" +#include "abm_helpers.h" TEST(TestInfection, init) { @@ -72,9 +73,13 @@ TEST(TestInfection, init) TEST(TestInfection, getInfectionState) { - auto t = mio::abm::TimePoint(0); + auto loc = mio::abm::Location(mio::abm::LocationType::Home, 0); + auto p = make_test_person(loc); + auto rng = mio::RandomNumberGenerator(); + auto p_rng = mio::abm::Person::RandomNumberGenerator(rng, p); + auto t = mio::abm::TimePoint(0); auto infection = - mio::abm::Infection(mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, + mio::abm::Infection(p_rng, mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, mio::abm::GlobalInfectionParameters{}, t, mio::abm::InfectionState::Exposed, true); EXPECT_EQ(infection.get_infection_state(t), mio::abm::InfectionState::Exposed); EXPECT_EQ(infection.get_infection_state(t - mio::abm::TimeSpan(1)), mio::abm::InfectionState::Susceptible); diff --git a/cpp/tests/test_graph_simulation.cpp b/cpp/tests/test_graph_simulation.cpp index 5fa795b0c6..4dd626ba0c 100644 --- a/cpp/tests/test_graph_simulation.cpp +++ b/cpp/tests/test_graph_simulation.cpp @@ -186,9 +186,6 @@ TEST(TestGraphSimulation, consistencyStochasticMobility) using testing::_; using testing::Eq; - //set seeds - mio::thread_local_rng().seed({114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); - const auto t0 = 0.0; const auto tmax = 20.; const auto dt = 0.076; @@ -204,15 +201,18 @@ TEST(TestGraphSimulation, consistencyStochasticMobility) g.add_edge(0, 1, Eigen::VectorXd::Constant(4, 0.001)); auto sim = mio::make_migration_sim(t0, dt, std::move(g)); + + //set seeds + sim.get_rng().seed({114381446, 2427727386, 806223567, 832414962, 4121923627, 1581162203}); sim.advance(tmax); auto result_n0 = sim.get_graph().nodes()[0].property.get_result().get_last_value(); auto result_n1 = sim.get_graph().nodes()[1].property.get_result().get_last_value(); - auto expected_values_n0 = std::vector{691.0, 6.3518514260209971, 31.303976182729517, 257.34417239124963}; + auto expected_values_n0 = std::vector{687.0, 6.3624463711313268, 31.61724873461494, 254.0203048942538}; auto actual_values_n0 = std::vector{result_n0[0], result_n0[1], result_n0[2], result_n0[3]}; - auto expected_values_n1 = std::vector{709.0, 6.4651647420642382, 33.101208735481720, 265.43362652245412}; + auto expected_values_n1 = std::vector{713.0, 6.4545879236128822, 32.787911286182755, 268.7575007902044}; auto actual_values_n1 = std::vector{result_n1[0], result_n1[1], result_n1[2], result_n1[3]}; for (size_t i = 0; i < expected_values_n0.size(); ++i) { From 77ad862e16aa83a30d3d718094ba640192aa8d5c Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Sat, 5 Aug 2023 00:29:38 +0200 Subject: [PATCH 036/105] handle empty base optimization in MSVC --- cpp/memilio/epidemiology/damping.h | 9 +++++---- cpp/memilio/utils/compiler_diagnostics.h | 12 ++++++++++++ cpp/memilio/utils/index.h | 9 +++++---- cpp/memilio/utils/random_number_generator.h | 4 ++-- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/cpp/memilio/epidemiology/damping.h b/cpp/memilio/epidemiology/damping.h index 7d27f21546..695301783d 100644 --- a/cpp/memilio/epidemiology/damping.h +++ b/cpp/memilio/epidemiology/damping.h @@ -21,6 +21,7 @@ #define DAMPING_H #include "memilio/math/eigen.h" +#include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/type_safe.h" #include "memilio/utils/stl_util.h" #include "memilio/math/matrix_shape.h" @@ -48,10 +49,10 @@ DECL_TYPESAFE(int, DampingType); /** * double simulation time. */ -class SimulationTime : public TypeSafe, - public OperatorAdditionSubtraction, - public OperatorScalarMultiplicationDivision, - public OperatorComparison +class MEMILIO_ENABLE_EBO SimulationTime : public TypeSafe, + public OperatorAdditionSubtraction, + public OperatorScalarMultiplicationDivision, + public OperatorComparison { public: using TypeSafe::TypeSafe; diff --git a/cpp/memilio/utils/compiler_diagnostics.h b/cpp/memilio/utils/compiler_diagnostics.h index b7d6bb4562..8df12dd8f2 100644 --- a/cpp/memilio/utils/compiler_diagnostics.h +++ b/cpp/memilio/utils/compiler_diagnostics.h @@ -63,4 +63,16 @@ void unused(T&&...) #endif //gcc, clang +//MSVC has a long standing bug that breaks empty base optimization (EBO) +//if the class has more than one empty base class. +//types that rely on empty base optimization must add this declaration +//e.g. struct MEMILIO_ENABLE_EBO Foo : EmptyBase1, EmptyBase2, ... +//see https://en.cppreference.com/w/cpp/language/ebo +//see https://stackoverflow.com/questions/12701469/why-is-the-empty-base-class-optimization-ebo-is-not-working-in-msvc +#ifdef _MSC_VER +#define MEMILIO_ENABLE_EBO __declspec(empty_bases) +#else +#define MEMILIO_ENABLE_EBO +#endif + #endif //EPI_UTILS_UNUSED_H diff --git a/cpp/memilio/utils/index.h b/cpp/memilio/utils/index.h index af2872ee88..629f80955a 100644 --- a/cpp/memilio/utils/index.h +++ b/cpp/memilio/utils/index.h @@ -20,6 +20,7 @@ #ifndef INDEX_H #define INDEX_H +#include "memilio/utils/compiler_diagnostics.h" #include "memilio/utils/type_safe.h" namespace mio @@ -53,10 +54,10 @@ class Index; * */ template -class Index : public TypeSafe>, - public OperatorComparison>, - public OperatorAdditionSubtraction>, - public OperatorScalarMultiplicationDivision, size_t> +class MEMILIO_ENABLE_EBO Index : public TypeSafe>, + public OperatorComparison>, + public OperatorAdditionSubtraction>, + public OperatorScalarMultiplicationDivision, size_t> { public: using TypeSafe>::TypeSafe; diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 4af8144578..6b95d27fc3 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -171,7 +171,7 @@ inline r123array2x32 to_r123_array(uint64_t i) * @tparam an unsigned integer type that determines the size of the key, i.e., the number of different sequences. */ template -struct Key : TypeSafe>, OperatorComparison> { +struct MEMILIO_ENABLE_EBO Key : TypeSafe>, OperatorComparison> { static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); using TypeSafe>::TypeSafe; }; @@ -181,7 +181,7 @@ struct Key : TypeSafe>, OperatorComparison> { * @tparam an unsigned integer type that determines the size of the counter, i.e., the length of the random sequence. */ template -struct Counter : TypeSafe>, OperatorComparison>, OperatorAdditionSubtraction> { +struct MEMILIO_ENABLE_EBO Counter : TypeSafe>, OperatorComparison>, OperatorAdditionSubtraction> { static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); using TypeSafe>::TypeSafe; }; From 4b7ab9012b86bc6beb98466f8ad7933a79b4017f Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Sat, 5 Aug 2023 02:14:19 +0200 Subject: [PATCH 037/105] sanity check for empty base... --- cpp/memilio/utils/random_number_generator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/memilio/utils/random_number_generator.h b/cpp/memilio/utils/random_number_generator.h index 6b95d27fc3..4d3f3c620a 100644 --- a/cpp/memilio/utils/random_number_generator.h +++ b/cpp/memilio/utils/random_number_generator.h @@ -185,6 +185,7 @@ struct MEMILIO_ENABLE_EBO Counter : TypeSafe>, OperatorComparison< static_assert(std::is_unsigned::value, "Underlying Integer type must be unsigned."); using TypeSafe>::TypeSafe; }; +static_assert(sizeof(Counter) == sizeof(uint32_t), ""); template auto RandomNumberGeneratorBase::operator()() -> result_type From 07971c6f6488001dcdc55322898098205ad0f07d Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Sun, 6 Aug 2023 15:41:53 +0200 Subject: [PATCH 038/105] fix copying and moving of Range in python bindings --- .../memilio/simulation/abm.cpp | 4 +++ .../memilio/simulation/pybind_util.h | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/pycode/memilio-simulation/memilio/simulation/abm.cpp b/pycode/memilio-simulation/memilio/simulation/abm.cpp index ee7c323b53..e124c4f5da 100644 --- a/pycode/memilio-simulation/memilio/simulation/abm.cpp +++ b/pycode/memilio-simulation/memilio/simulation/abm.cpp @@ -170,6 +170,7 @@ PYBIND11_MODULE(_simulation_abm, m) self.get_infection_parameters() = params; }); + //copying and moving of ranges enabled below, see PYMIO_IGNORE_VALUE_TYPE pymio::bind_Range().get_locations())>(m, "_WorldLocationsRange"); pymio::bind_Range().get_persons())>(m, "_WorldPersonsRange"); @@ -234,3 +235,6 @@ PYBIND11_MODULE(_simulation_abm, m) .def_property_readonly("result", &mio::abm::Simulation::get_result) .def_property_readonly("world", py::overload_cast<>(&mio::abm::Simulation::get_world)); } + +PYMIO_IGNORE_VALUE_TYPE(decltype(std::declval().get_locations())) +PYMIO_IGNORE_VALUE_TYPE(decltype(std::declval().get_persons())) diff --git a/pycode/memilio-simulation/memilio/simulation/pybind_util.h b/pycode/memilio-simulation/memilio/simulation/pybind_util.h index a929bdda93..a4451243a5 100644 --- a/pycode/memilio-simulation/memilio/simulation/pybind_util.h +++ b/pycode/memilio-simulation/memilio/simulation/pybind_util.h @@ -101,6 +101,12 @@ void bind_shape_property(C& cl) }); } +/** +* Bind a specialization of mio::Range class template. +* The python class will be a read-only container/iterable. +* You probably also want to use PYMIO_IGNORE_VALUE_TYPE to +* enable copying and moving in all cases. +*/ template auto bind_Range(pybind11::module_& m, const std::string& class_name) { @@ -134,6 +140,25 @@ auto bind_Range(pybind11::module_& m, const std::string& class_name) .def("__len__", &Range::size); } +/** +* A Range looks like a container, so pybind11 checks the value_type alias to see if +* the Range can be copied or moved. But since a Range is just a view and does not own its contents, +* it can always be copied and moved, even if the value_type is uncopieable/unmovable. +* This macro stops the value_type check. +* see {Pybind11_SRC_DIR}/include/pybind11/detail/type_caster_base.h and {Pybind11_SRC_DIR}/tests/test_stl_binders.cpp +*/ +#define PYMIO_IGNORE_VALUE_TYPE(Range) \ + namespace pybind11 \ + { \ + namespace detail \ + { \ + template \ + struct recursive_container_traits { \ + using type_to_check_recursively = recursive_bottom; \ + }; \ + } \ +} + //bind an enum class that can be iterated over //requires the class to have a member `Count` //adds a static `values` method to the enum class that returns an iterable list of the values From a56c5353171205ac245de631c855dee9cc5ff96c Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Sat, 12 Aug 2023 00:40:36 +0200 Subject: [PATCH 039/105] some performance and scaling improvements locations don't store persons to avoid expensive locks merged interaction and migration loops fewer result time points (once per day) better loop schedules needs to be cleaned up! to be reversed: made member of location/person public needs fix: disabled some unit tests needs fix: subpopulation per location --- cpp/models/abm/location.cpp | 181 +++++++----- cpp/models/abm/location.h | 41 ++- cpp/models/abm/person.cpp | 18 +- cpp/models/abm/person.h | 6 +- cpp/models/abm/simulation.cpp | 33 ++- cpp/models/abm/simulation.h | 5 +- cpp/models/abm/world.cpp | 114 +++++-- cpp/models/abm/world.h | 4 +- cpp/simulations/abm.cpp | 4 +- cpp/tests/test_abm_location.cpp | 391 +++++++++++++------------ cpp/tests/test_abm_masks.cpp | 68 ++--- cpp/tests/test_abm_migration_rules.cpp | 4 +- cpp/tests/test_abm_person.cpp | 20 +- cpp/tests/test_abm_simulation.cpp | 45 ++- cpp/tests/test_abm_world.cpp | 284 +++++++++--------- 15 files changed, 690 insertions(+), 528 deletions(-) diff --git a/cpp/models/abm/location.cpp b/cpp/models/abm/location.cpp index 63e2227295..d1363f5057 100644 --- a/cpp/models/abm/location.cpp +++ b/cpp/models/abm/location.cpp @@ -22,6 +22,7 @@ #include "abm/location.h" #include "abm/random_events.h" #include "abm/infection.h" +#include "memilio/config.h" #include "memilio/utils/random_number_generator.h" #include #include @@ -87,51 +88,80 @@ void Location::interact(Person::RandomNumberGenerator& rng, Person& person, Time } } -void Location::cache_exposure_rates(TimePoint t, TimeSpan dt) -{ - //cache for next step so it stays constant during the step while subpopulations change - //otherwise we would have to cache all state changes during a step which uses more memory - for (auto& cell : m_cells) { - cell.m_cached_exposure_rate_contacts = {{VirusVariant::Count, AgeGroup::Count}, 0.}; - cell.m_cached_exposure_rate_air = {{VirusVariant::Count}, 0.}; - for (auto&& p : cell.m_persons) { - if (p->is_infected(t)) { - auto& inf = p->get_infection(); - auto virus = inf.get_virus_variant(); - auto age = p->get_age(); - /* average infectivity over the time step - * to second order accuracy using midpoint rule - */ - cell.m_cached_exposure_rate_contacts[{virus, age}] += inf.get_infectivity(t + dt / 2); - cell.m_cached_exposure_rate_air[{virus}] += inf.get_infectivity(t + dt / 2); - } - } - if (m_capacity_adapted_transmission_risk) { - cell.m_cached_exposure_rate_air.array() *= cell.compute_space_per_person_relative(); - } - } +// void Location::cache_exposure_rates(TimePoint t, TimeSpan dt) +// { +// //cache for next step so it stays constant during the step while subpopulations change +// //otherwise we would have to cache all state changes during a step which uses more memory +// for (auto& cell : m_cells) { +// cell.m_cached_exposure_rate_contacts = {{VirusVariant::Count, AgeGroup::Count}, 0.}; +// cell.m_cached_exposure_rate_air = {{VirusVariant::Count}, 0.}; +// for (auto&& p : cell.m_persons) { +// if (p->is_infected(t)) { +// auto& inf = p->get_infection(); +// auto virus = inf.get_virus_variant(); +// auto age = p->get_age(); +// /* average infectivity over the time step +// * to second order accuracy using midpoint rule +// */ +// cell.m_cached_exposure_rate_contacts[{virus, age}] += inf.get_infectivity(t + dt / 2); +// cell.m_cached_exposure_rate_air[{virus}] += inf.get_infectivity(t + dt / 2); +// } +// } +// if (m_capacity_adapted_transmission_risk) { +// cell.m_cached_exposure_rate_air.array() *= cell.compute_space_per_person_relative(); +// } +// } +// } + +void Location::lock() +{ + m_mut.lock(); } -void Location::add_person(Person& p, std::vector cells) +void Location::unlock() { - std::lock_guard lk(m_mut); - m_persons.push_back(&p); - for (uint32_t cell_idx : cells) - m_cells[cell_idx].m_persons.push_back(&p); + m_mut.unlock(); } -void Location::remove_person(Person& p) -{ - std::lock_guard lk(m_mut); - m_persons.erase(std::remove(m_persons.begin(), m_persons.end(), &p), m_persons.end()); - for (auto&& cell : m_cells) { - cell.m_persons.erase(std::remove(cell.m_persons.begin(), cell.m_persons.end(), &p), cell.m_persons.end()); - } -} +// void Location::add_person(Person& p, std::vector cells) +// { +// std::lock_guard lk(*this); +// m_persons.push_back(&p); +// for (uint32_t cell_idx : cells) +// m_cells[cell_idx].m_persons.push_back(&p); +// } + +// void Location::add_remove_persons(const std::vector> &) +// { +// //remove +// // m_persons.erase(std::remove_if(m_persons.begin(), m_persons.end(), [this](auto&& p) { return p->m_location != this; }), m_persons.end()); +// // for (auto&& cell : m_cells) { +// // cell.m_persons.erase(std::remove_if(cell.m_persons.begin(), cell.m_persons.end(), [this](auto&& p) { return p->m_location != this; }), cell.m_persons.end()); +// // } + +// //add +// // for (auto&& p : persons) { +// // if (p->m_location == this && p->m_old_location != this) { +// // m_persons.push_back(p.get()); +// // for (uint32_t cell_idx : p->m_cells) { +// // m_cells[cell_idx].m_persons.push_back(p.get()); +// // } +// // } +// // } +// } + +// void Location::remove_person(Person& p) +// { +// std::lock_guard lk(*this); +// m_persons.erase(std::remove(m_persons.begin(), m_persons.end(), &p), m_persons.end()); +// for (auto&& cell : m_cells) { +// cell.m_persons.erase(std::remove(cell.m_persons.begin(), cell.m_persons.end(), &p), cell.m_persons.end()); +// } +// } size_t Location::get_number_persons() const { - return m_persons.size(); + return m_num_persons; } /* @@ -148,45 +178,44 @@ ScalarType Cell::compute_space_per_person_relative() } } -size_t Cell::get_subpopulation(TimePoint t, InfectionState state) const -{ - return count_if(m_persons.begin(), m_persons.end(), [&](observer_ptr p) { - return p->get_infection_state(t) == state; - }); -} - -size_t Location::get_subpopulation(TimePoint t, InfectionState state) const -{ - return count_if(m_persons.begin(), m_persons.end(), [&](observer_ptr p) { - return p->get_infection_state(t) == state; - }); -} - -void Location::store_subpopulations(const TimePoint t) -{ - m_subpopulations.add_time_point(t.days()); - Eigen::VectorXd subpopulations(Eigen::VectorXd::Zero((size_t)InfectionState::Count)); - for (auto p : m_persons) - ++subpopulations[(size_t)p->get_infection_state(t)]; - m_subpopulations.get_last_value() = subpopulations; -} - -void Location::initialize_subpopulations(const TimePoint t) -{ - if (m_subpopulations.get_num_time_points() == 0) { - store_subpopulations(t); - } - else { - if (m_subpopulations.get_last_time() != t.days()) { // if not already saved - store_subpopulations(t); - } - } -} - -const TimeSeries& Location::get_subpopulations() const -{ - return m_subpopulations; -} +// size_t Cell::get_subpopulation(TimePoint t, InfectionState state) const +// { +// return count_if(m_persons.begin(), m_persons.end(), [&](observer_ptr p) { +// return p->get_infection_state(t) == state; +// }); +// } + +// size_t Location::get_subpopulation(TimePoint t, InfectionState state) const +// { +// return count_if(m_persons.begin(), m_persons.end(), [&](observer_ptr p) { +// return p->get_infection_state(t) == state; +// }); +// } + +// void Location::store_subpopulations(const TimePoint t) +// { +// m_subpopulations.add_time_point(t.days()); +// m_subpopulations.get_last_value().setZero(); +// for (auto p : m_persons) +// m_subpopulations.get_last_value()[(Eigen::Index)p->get_infection_state(t)] += 1; +// } + +// void Location::initialize_subpopulations(const TimePoint t) +// { +// if (m_subpopulations.get_num_time_points() == 0) { +// store_subpopulations(t); +// } +// else { +// if (m_subpopulations.get_last_time() != t.days()) { // if not already saved +// store_subpopulations(t); +// } +// } +// } + +// const TimeSeries& Location::get_subpopulations() const +// { +// return m_subpopulations; +// } } // namespace abm } // namespace mio diff --git a/cpp/models/abm/location.h b/cpp/models/abm/location.h index 17952281f8..a3767967e4 100644 --- a/cpp/models/abm/location.h +++ b/cpp/models/abm/location.h @@ -32,6 +32,8 @@ #include "memilio/utils/time_series.h" #include "memilio/utils/memory.h" #include +#include +#include #include #include @@ -61,14 +63,14 @@ struct CellCapacity { * This allows a finer division of the people at the Location. */ struct Cell { - std::vector> m_persons; + // std::vector> m_persons; CustomIndexArray m_cached_exposure_rate_contacts; CustomIndexArray m_cached_exposure_rate_air; CellCapacity m_capacity; - explicit Cell(std::vector> persons = {}) - : m_persons(std::move(persons)) - , m_cached_exposure_rate_contacts({{VirusVariant::Count, AgeGroup::Count}, 0.}) + explicit Cell(std::vector> = {}) + : /*m_persons(std::move(persons)) + , */m_cached_exposure_rate_contacts({{VirusVariant::Count, AgeGroup::Count}, 0.}) , m_cached_exposure_rate_air({{VirusVariant::Count}, 0.}) , m_capacity() { @@ -86,7 +88,7 @@ struct Cell { * @param[in] state #InfectionState of interest. * @return Amount of Person%s of the #InfectionState in the Cell. */ - size_t get_subpopulation(TimePoint t, InfectionState state) const; + // size_t get_subpopulation(TimePoint t, InfectionState state) const; }; // namespace mio @@ -108,6 +110,9 @@ class Location : Location(LocationId{loc_index, loc_type}, num_cells) { } + + void lock(); + void unlock(); /** * @brief Compare two Location%s. @@ -173,20 +178,22 @@ class Location * @param[in] person The Person arriving. * @param[in] cell_idx [Default: 0] Index of the Cell the Person shall go to. */ - void add_person(Person& person, std::vector cells = {0}); + // void add_person(Person& person, std::vector cells = {0}); + + // void add_remove_persons(const std::vector>& persons); /** * @brief Remove a Person from the population of this Location. * @param[in] person The Person leaving. */ - void remove_person(Person& person); + // void remove_person(Person& person); /** * @brief Prepare the Location for the next Simulation step. * @param[in] t Current TimePoint of the Simulation. * @param[in] dt The duration of the Simulation step. */ - void cache_exposure_rates(TimePoint t, TimeSpan dt); + // void cache_exposure_rates(TimePoint t, TimeSpan dt); /** * @brief Get the Location specific Infection parameters. @@ -312,40 +319,44 @@ class Location * @param[in] state #InfectionState of interest. * @return Amount of Person%s of the #InfectionState in all Cell%s. */ - size_t get_subpopulation(TimePoint t, InfectionState state) const; + // size_t get_subpopulation(TimePoint t, InfectionState state) const; /** * Add a TimePoint to the subpopulations TimeSeries. * @param[in] t The TimePoint to be added. */ - void store_subpopulations(const TimePoint t); + // void store_subpopulations(const TimePoint t); /** * @brief Initialize the history of subpopulations. * @param[in] t The TimePoint of initialization. */ - void initialize_subpopulations(TimePoint t); + // void initialize_subpopulations(TimePoint t); /** * @brief Get the complete history of subpopulations. * @return The TimeSeries of the #InfectionState%s for each TimePoint at the Location. */ - const TimeSeries& get_subpopulations() const; + // const TimeSeries& get_subpopulations() const; private: +public: std::mutex m_mut; ///< Mutex to protect the list of persons from concurrent modification. LocationId m_id; ///< Id of the Location including type and index. bool m_capacity_adapted_transmission_risk; /**< If true considers the LocationCapacity for the computation of the transmission risk.*/ LocalInfectionParameters m_parameters; ///< Infection parameters for the Location. - std::vector> m_persons{}; ///< A vector of all Person%s at the Location. - TimeSeries m_subpopulations{Eigen::Index( - InfectionState::Count)}; ///< A TimeSeries of the #InfectionState%s for each TimePoint at the Location. + std::atomic m_num_persons = 0; + // std::vector> m_persons{}; ///< A vector of all Person%s at the Location. + // TimeSeries m_subpopulations{Eigen::Index( + // InfectionState::Count)}; ///< A TimeSeries of the #InfectionState%s for each TimePoint at the Location. std::vector 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. }; +static_assert(std::atomic::is_always_lock_free, ""); + } // namespace abm } // namespace mio diff --git a/cpp/models/abm/person.cpp b/cpp/models/abm/person.cpp index 44cece7f63..728e0d2fc4 100755 --- a/cpp/models/abm/person.cpp +++ b/cpp/models/abm/person.cpp @@ -33,6 +33,7 @@ namespace abm Person::Person(mio::RandomNumberGenerator& rng, Location& location, AgeGroup age, uint32_t person_id) : m_location(&location) + // , m_old_location(&location) , m_assigned_locations((uint32_t)LocationType::Count, INVALID_LOCATION_INDEX) , m_quarantine(false) , m_age(age) @@ -61,14 +62,27 @@ void Person::interact(RandomNumberGenerator& rng, TimePoint t, TimeSpan dt, cons void Person::migrate_to(Location& loc_new, const std::vector& cells) { if (*m_location != loc_new) { - m_location->remove_person(*this); +// bool immediate = true; +// #ifdef MEMILIO_ENABLE_OPENMP +// immediate = omp_get_num_threads() == 1; +// #endif + + // if (immediate) { + // m_location->remove_person(*this); + // } m_location = &loc_new; + // m_old_location = m_location; m_cells = cells; - loc_new.add_person(*this, cells); + // loc_new.add_person(*this, cells); m_time_at_location = TimeSpan(0); } } +void Person::stay() +{ + // m_old_location = m_location; +} + bool Person::is_infected(TimePoint t) const { if (m_infections.empty()) { diff --git a/cpp/models/abm/person.h b/cpp/models/abm/person.h index 141b0a181f..dcd3a0698e 100755 --- a/cpp/models/abm/person.h +++ b/cpp/models/abm/person.h @@ -151,6 +151,8 @@ class Person * */ void migrate_to(Location& loc_new, const std::vector& cells_new = {0}); + void stay(); + /** * @brief Get the latest Infection of the Person. * @return The latest Infection of the Person. @@ -444,8 +446,10 @@ class Person return m_rng_counter; } -private: +// private: +public: observer_ptr m_location; ///< Current Location of the Person. + // observer_ptr m_old_location; ///< Current Location of the Person. std::vector m_assigned_locations; /**! Vector with the indices of the assigned Locations so that the Person always visits the same Home or School etc. */ std::vector m_vaccinations; ///< Vector with all Vaccination%s the Person has received. diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index 698ed6943f..c7f39bf912 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -18,6 +18,7 @@ * limitations under the License. */ #include "abm/simulation.h" +#include "abm/time.h" #include "memilio/utils/logging.h" #include "memilio/utils/mioomp.h" #include "memilio/utils/random_number_generator.h" @@ -37,11 +38,12 @@ Simulation::Simulation(TimePoint t, World&& world) initialize_locations(m_t); } -void Simulation::initialize_locations(TimePoint t) +void Simulation::initialize_locations(TimePoint) { - for (auto& location : m_world.get_locations()) { - location.initialize_subpopulations(t); - } + // for (auto& location : m_world.get_locations()) { + // location.initialize_subpopulations(t); + // } + m_world.prepare(); } void Simulation::advance(TimePoint tmax) @@ -50,16 +52,19 @@ void Simulation::advance(TimePoint tmax) initialize_locations(m_t); store_result_at(m_t); while (m_t < tmax) { - evolve_world(tmax); - store_result_at(m_t); + auto dt = evolve_world(tmax); + m_t += dt; + if (m_t.time_since_midnight() < dt || m_t == tmax) { + store_result_at(m_t); + } } } -void Simulation::evolve_world(TimePoint tmax) +TimeSpan Simulation::evolve_world(TimePoint tmax) { auto dt = std::min(m_dt, tmax - m_t); m_world.evolve(m_t, dt); - m_t += m_dt; + return dt; } void Simulation::store_result_at(TimePoint t) @@ -72,12 +77,16 @@ void Simulation::store_result_at(TimePoint t) PRAGMA_OMP(parallel) { //thread local sum of subpopulations, computed in parallel + //TODO: maybe we can use atomic increments instead of the reduction if necessary for scaling? Eigen::VectorXd sum = Eigen::VectorXd::Zero(m_result.get_num_elements()); - PRAGMA_OMP(for) - for (auto i = size_t(0); i < m_world.get_locations().size(); ++i) { - auto&& location = m_world.get_locations()[i]; - sum += location.get_subpopulations().get_last_value().cast(); + auto persons = m_world.get_persons(); + + PRAGMA_OMP(for schedule(dynamic, 50)) //static? + for (auto i = size_t(0); i < persons.size(); ++i) { + auto&& person = persons[i]; + sum[Eigen::Index(person.get_infection_state(t))] += 1; } + //synchronized total sum PRAGMA_OMP(critical) { diff --git a/cpp/models/abm/simulation.h b/cpp/models/abm/simulation.h index 1de255d136..b95f17ce20 100644 --- a/cpp/models/abm/simulation.h +++ b/cpp/models/abm/simulation.h @@ -76,7 +76,8 @@ class Simulation store_result_at(m_t); history.log(*this); while (m_t < tmax) { - evolve_world(tmax); + auto dt = evolve_world(tmax); + m_t += dt; store_result_at(m_t); history.log(*this); } @@ -114,7 +115,7 @@ class Simulation private: void initialize_locations(TimePoint t); void store_result_at(TimePoint t); - void evolve_world(TimePoint tmax); + TimeSpan evolve_world(TimePoint tmax); World m_world; ///< The World to simulate. TimeSeries m_result; ///< The result of the Simulation. diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 0e0ffb760f..e84d107d2b 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -28,6 +28,8 @@ #include "memilio/utils/mioomp.h" #include "memilio/utils/random_number_generator.h" #include "memilio/utils/stl_util.h" +#include +#include namespace mio { @@ -47,7 +49,7 @@ Person& World::add_person(const LocationId id, AgeGroup age) m_persons.push_back(std::make_unique(m_rng, get_individualized_location(id), age, person_id)); auto& person = *m_persons.back(); person.set_assigned_location(m_cemetery_id); - get_individualized_location(id).add_person(person); + // get_individualized_location(id).add_person(person); return person; } @@ -57,21 +59,38 @@ void World::evolve(TimePoint t, TimeSpan dt) log_info("ABM World interaction."); interaction(t, dt); log_info("ABM World migration."); - migration(t, dt); + // migration(t, dt); end_step(t, dt); } void World::interaction(TimePoint t, TimeSpan dt) { - PRAGMA_OMP(parallel for) + PRAGMA_OMP(parallel for schedule(dynamic, 50)) //dynamic 20 for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); person->interact(personal_rng, t, dt, m_infection_parameters); + + for (auto rule : m_migration_rules) { + //check if transition rule can be applied + auto target_type = rule.first(personal_rng, *person, t, dt, m_migration_parameters); + auto& target_location = find_location(target_type, *person); + auto& current_location = person->get_location(); + if (m_testing_strategy.run_strategy(personal_rng, *person, target_location, t)) { + if (target_location != current_location && + target_location.get_number_persons() < target_location.get_capacity().persons) { + bool wears_mask = person->apply_mask_intervention(personal_rng, target_location); + if (wears_mask) { + person->migrate_to(target_location); + } + break; + } + } + } } } -void World::migration(TimePoint t, TimeSpan dt) +void World::prepare() { std::vector>> @@ -91,11 +110,17 @@ void World::migration(TimePoint t, TimeSpan dt) enhanced_migration_rules.push_back(rule); } } - PRAGMA_OMP(parallel for) + m_migration_rules = enhanced_migration_rules; +} + +void World::migration(TimePoint t, TimeSpan dt) +{ + PRAGMA_OMP(parallel for schedule(dynamic, 20)) //static? for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; + // person->stay(); auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); - for (auto rule : enhanced_migration_rules) { + for (auto rule : m_migration_rules) { //check if transition rule can be applied auto target_type = rule.first(personal_rng, *person, t, dt, m_migration_parameters); auto& target_location = find_location(target_type, *person); @@ -136,22 +161,69 @@ void World::migration(TimePoint t, TimeSpan dt) void World::begin_step(TimePoint t, TimeSpan dt) { m_testing_strategy.update_activity_status(t); - PRAGMA_OMP(parallel for) + + //cache for next step so it stays constant during the step while subpopulations change + //otherwise we would have to cache all state changes during a step which uses more memory + PRAGMA_OMP(parallel for schedule(static)) //dynamic 20? for (auto i = size_t(0); i < m_locations.size(); ++i) { auto&& location = m_locations[i]; - location->cache_exposure_rates(t, dt); + location->m_num_persons = 0; + for (auto&& cell : location->m_cells) { + cell.m_cached_exposure_rate_air.array().setZero(); + cell.m_cached_exposure_rate_contacts.array().setZero(); } } -void World::end_step(TimePoint t, TimeSpan dt) + PRAGMA_OMP(parallel for schedule(dynamic, 50)) //static? + for (auto i = size_t(0); i < m_persons.size(); ++i) { + auto&& person = m_persons[i]; + auto&& loc = person->m_location; + + + if (person->is_infected(t)) { + auto&& inf = person->get_infection(); + auto virus = inf.get_virus_variant(); + auto age = person->get_age(); + +#ifdef MEMILIO_ENABLE_OPENMP + std::lock_guard lk(*loc); +#endif + for (auto&& cell_idx : person->m_cells) { - PRAGMA_OMP(parallel for) - for (auto i = size_t(0); i < m_locations.size(); ++i) { - auto&& location = m_locations[i]; - location->store_subpopulations(t + dt); + auto&& cell = loc->m_cells[cell_idx]; + + /* average infectivity over the time step + * to second order accuracy using midpoint rule + */ + cell.m_cached_exposure_rate_contacts[{virus, age}] += inf.get_infectivity(t + dt / 2); + auto air_factor = loc->m_capacity_adapted_transmission_risk ? cell.compute_space_per_person_relative() : 1.0; + cell.m_cached_exposure_rate_air[{virus}] += inf.get_infectivity(t + dt / 2) * air_factor; + } + } + + ++loc->m_num_persons; } } +void World::end_step(TimePoint , TimeSpan ) +{ + // bool immediate = true; +// #ifdef MEMILIO_ENABLE_OPENMP +// immediate = omp_get_num_threads() == 1; +// #endif + + // if ((t + dt).time_since_midnight() < dt) { + // PRAGMA_OMP(parallel for schedule(dynamic, 20)) //dynamic 20? + // for (auto i = size_t(0); i < m_locations.size(); ++i) { + // auto&& location = m_locations[i]; + // // if (!immediate) { + // // location->add_remove_persons(m_persons); + // // } + // location->store_subpopulations(t + dt); + // } + // } +} + auto World::get_locations() const -> Range> { return std::make_pair(ConstLocationIterator(m_locations.begin()), ConstLocationIterator(m_locations.end())); @@ -179,14 +251,14 @@ Location& World::find_location(LocationType type, const Person& person) return get_individualized_location({index, type}); } -size_t World::get_subpopulation_combined(TimePoint t, InfectionState s, LocationType type) const -{ - return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, - [t, s, type](size_t running_sum, const std::unique_ptr& loc) { - return loc->get_type() == type ? running_sum + loc->get_subpopulation(t, s) - : running_sum; - }); -} +// size_t World::get_subpopulation_combined(TimePoint t, InfectionState s, LocationType type) const +// { +// return std::accumulate(m_locations.begin(), m_locations.end(), (size_t)0, +// [t, s, type](size_t running_sum, const std::unique_ptr& loc) { +// return loc->get_type() == type ? running_sum + loc->get_subpopulation(t, s) +// : running_sum; +// }); +// } MigrationParameters& World::get_migration_parameters() { diff --git a/cpp/models/abm/world.h b/cpp/models/abm/world.h index a1d94b527a..619f9df88e 100644 --- a/cpp/models/abm/world.h +++ b/cpp/models/abm/world.h @@ -140,7 +140,7 @@ class World * @param[in] s Specified #InfectionState. * @param[in] type Specified #LocationType. */ - size_t get_subpopulation_combined(TimePoint t, InfectionState s, LocationType type) const; + // size_t get_subpopulation_combined(TimePoint t, InfectionState s, LocationType type) const; /** * @brief Get the MigrationParameters. @@ -175,6 +175,8 @@ class World void use_migration_rules(bool param); bool use_migration_rules() const; + void prepare(); + /** * @brief Get the TestingStrategy. * @return Reference to the list of TestingScheme%s that are checked for testing. diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 352c4c02fd..187cd25e08 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -820,7 +820,7 @@ mio::abm::Simulation create_sampled_simulation(const mio::abm::TimePoint& t0) * @param save_single_runs [Default: true] Defines if single run results are written to the disk. * @returns Any io error that occurs during reading or writing of files. */ -mio::IOResult run(const fs::path& result_dir, size_t num_runs, bool save_single_runs = true) +mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num_runs, bool save_single_runs = true) { auto t0 = mio::abm::TimePoint(0); // Start time per simulation @@ -889,7 +889,7 @@ int main(int argc, char** argv) return 0; } - auto result = run(result_dir, num_runs, save_single_runs); + auto result = run_abm_simulation_xx(result_dir, num_runs, save_single_runs); if (!result) { printf("%s\n", result.error().formatted_message().c_str()); return -1; diff --git a/cpp/tests/test_abm_location.cpp b/cpp/tests/test_abm_location.cpp index 6f72ed186e..00217b477b 100644 --- a/cpp/tests/test_abm_location.cpp +++ b/cpp/tests/test_abm_location.cpp @@ -28,11 +28,11 @@ TEST(TestLocation, init) mio::abm::Location location(mio::abm::LocationType::School, 0); for (mio::abm::InfectionState i = mio::abm::InfectionState(0); i < mio::abm::InfectionState::Count; i = mio::abm::InfectionState(size_t(i) + 1)) { - ASSERT_EQ(location.get_subpopulation(mio::abm::TimePoint(0), i), 0); + // ASSERT_EQ(location.get_subpopulation(mio::abm::TimePoint(0), i), 0); } - location.initialize_subpopulations(mio::abm::TimePoint(0)); - ASSERT_EQ(print_wrap(location.get_subpopulations().get_last_value()), - print_wrap(mio::TimeSeries::Vector::Zero((size_t)mio::abm::InfectionState::Count))); + // location.initialize_subpopulations(mio::abm::TimePoint(0)); + // ASSERT_EQ(print_wrap(location.get_subpopulations().get_last_value()), + // print_wrap(mio::TimeSeries::Vector::Zero((size_t)mio::abm::InfectionState::Count))); EXPECT_EQ(location.get_number_persons(), 0); } @@ -48,40 +48,40 @@ TEST(TestLocation, getIndex) ASSERT_EQ((int)location.get_index(), 0); } -TEST(TestLocation, addRemovePerson) -{ - mio::abm::Location home(mio::abm::LocationType::Home, 0, 1); - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); - - auto person1 = make_test_person(home, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms); - auto person2 = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); - auto person3 = make_test_person(home, mio::abm::AgeGroup::Age35to59, mio::abm::InfectionState::Exposed); - - home.add_person(person1, {0}); - home.add_person(person2, {0}); - home.add_person(person3, {0}); - - person1.migrate_to(location, {0, 1}); - person2.migrate_to(location, {0}); - person3.migrate_to(location, {0, 1}); - - auto t = mio::abm::TimePoint(0); - ASSERT_EQ(home.get_number_persons(), 0u); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 2); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); - ASSERT_EQ(location.get_cells()[0].m_persons.size(), 3u); - ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); - ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); - - location.remove_person(person2); - - EXPECT_EQ(location.get_number_persons(), 2u); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 1); - ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); - ASSERT_EQ(location.get_cells()[0].m_persons.size(), 2u); - ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); - ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); -} +// TEST(TestLocation, addRemovePerson) +// { +// mio::abm::Location home(mio::abm::LocationType::Home, 0, 1); +// mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); + +// auto person1 = make_test_person(home, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms); +// auto person2 = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms); +// auto person3 = make_test_person(home, mio::abm::AgeGroup::Age35to59, mio::abm::InfectionState::Exposed); + +// home.add_person(person1, {0}); +// home.add_person(person2, {0}); +// home.add_person(person3, {0}); + +// person1.migrate_to(location, {0, 1}); +// person2.migrate_to(location, {0}); +// person3.migrate_to(location, {0, 1}); + +// auto t = mio::abm::TimePoint(0); +// ASSERT_EQ(home.get_number_persons(), 0u); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 2); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); +// ASSERT_EQ(location.get_cells()[0].m_persons.size(), 3u); +// ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); +// ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); + +// location.remove_person(person2); + +// EXPECT_EQ(location.get_number_persons(), 2u); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::InfectedSymptoms), 1); +// ASSERT_EQ(location.get_subpopulation(t, mio::abm::InfectionState::Exposed), 1); +// ASSERT_EQ(location.get_cells()[0].m_persons.size(), 2u); +// ASSERT_EQ(location.get_cells()[1].m_persons.size(), 2u); +// ASSERT_EQ(location.get_cells()[2].m_persons.size(), 0u); +// } TEST(TestLocation, CacheExposureRate) { @@ -97,24 +97,27 @@ TEST(TestLocation, CacheExposureRate) auto t = mio::abm::TimePoint(0); auto dt = mio::abm::seconds(10000); - mio::abm::GlobalInfectionParameters params; + auto world = mio::abm::World(); // setup a location with some chance of exposure - mio::abm::Location home(mio::abm::LocationType::Home, 0, 1); - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); - auto infected1 = mio::abm::Person(rng, home, age); + auto home_id = world.add_location(mio::abm::LocationType::Home); + auto location_id = world.add_location(mio::abm::LocationType::PublicTransport, 3); + auto& location = world.get_individualized_location(location_id); + + auto& infected1 = world.add_person(home_id, age); auto rng_infected1 = mio::abm::Person::RandomNumberGenerator(rng, infected1); infected1.add_new_infection( - mio::abm::Infection(rng_infected1, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); + mio::abm::Infection(rng_infected1, variant, age, world.get_global_infection_parameters(), t, mio::abm::InfectionState::InfectedNoSymptoms)); infected1.migrate_to(location, {0}); - auto infected2 = mio::abm::Person(rng, home, age); + auto& infected2 = world.add_person(home_id, age); auto rng_infected2 = mio::abm::Person::RandomNumberGenerator(rng, infected2); infected2.add_new_infection( - mio::abm::Infection(rng_infected2, variant, age, params, t, mio::abm::InfectionState::InfectedNoSymptoms)); + mio::abm::Infection(rng_infected2, variant, age, world.get_global_infection_parameters(), t, mio::abm::InfectionState::InfectedNoSymptoms)); infected2.migrate_to(location, {0, 1}); //cache precomputed results - location.cache_exposure_rates(t, dt); + world.begin_step(t, dt); + // location.cache_exposure_rates(t, dt); EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_contacts[{variant, age}]), 0.015015859523894731, 1e-14); @@ -130,7 +133,8 @@ TEST(TestLocation, CacheExposureRate) location.set_capacity(2, 22, 0); // Capacity for Cell 1 location.set_capacity(2, 22, 1); // Capacity for Cell 2 location.set_capacity(2, 22, 2); // Capacity for Cell 3 - location.cache_exposure_rates(t, dt); + + world.begin_step(t, dt); EXPECT_NEAR((location.get_cells()[0].m_cached_exposure_rate_air[{variant}]), 0.045047578571684191, 1e-14); EXPECT_NEAR((location.get_cells()[1].m_cached_exposure_rate_air[{variant}]), 0.022523789285842095, 1e-14); @@ -188,6 +192,7 @@ TEST(TestLocation, reachCapacity) EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillRepeatedly(Return(1.)); //no state transitions world.evolve(t, dt); + world.begin_step(t + dt, dt); ASSERT_EQ(p1.get_location(), school); ASSERT_EQ(p2.get_location(), home); // p2 should not be able to enter the school @@ -210,62 +215,62 @@ TEST(TestLocation, computeSpacePerPersonRelative) ASSERT_EQ(cells[2].compute_space_per_person_relative(), 1.); } -TEST(TestLocation, interact) -{ - using testing::Return; - - auto rng = mio::RandomNumberGenerator(); - - // Test should work identically work with any age. - mio::abm::AgeGroup age = mio::abm::AgeGroup( - mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::AgeGroup::Count) - 1)); - mio::abm::VirusVariant variant = mio::abm::VirusVariant( - mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::VirusVariant::Count) - 1)); - - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::seconds(8640); //0.1 days - - mio::abm::GlobalInfectionParameters params; - params.set_default(); - params.get()[{variant, age, mio::abm::VaccinationState::Unvaccinated}] = { - {1., 1.}, {0.0001, 0.0001}, {-0.0001, -0.0001}}; - params.set_default(); - params.get()[{variant, age}] = {{1., 1.}, {1., 1.}}; - - // set incubtion period to two days so that the newly infected person is still exposed - params.get()[{variant, age, mio::abm::VaccinationState::Unvaccinated}] = 2.; - - //setup location with some chance of exposure - mio::abm::Location location(mio::abm::LocationType::Work, 0); - auto infected1 = make_test_person(location, mio::abm::AgeGroup::Age15to34, - mio::abm::InfectionState::InfectedNoSymptoms, t, params); - auto infected2 = make_test_person(location, mio::abm::AgeGroup::Age80plus, - mio::abm::InfectionState::InfectedSymptoms, t, params); - auto infected3 = - make_test_person(location, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms, t, params); - - location.add_person(infected1, {0}); - location.add_person(infected2, {0}); - location.add_person(infected3, {0}); - - //cache precomputed results - location.cache_exposure_rates(t, dt); - - ScopedMockDistribution>>> - mock_exponential_dist; - ScopedMockDistribution>>> mock_discrete_dist; - - auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); - EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); - auto person_rng = mio::abm::Person::RandomNumberGenerator(rng, susceptible); - location.interact(person_rng, susceptible, t, dt, params); - EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); - - EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); - EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); - location.interact(person_rng, susceptible, t, dt, params); - EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); -} +// TEST(TestLocation, interact) +// { +// using testing::Return; + +// auto rng = mio::RandomNumberGenerator(); + +// // Test should work identically work with any age. +// mio::abm::AgeGroup age = mio::abm::AgeGroup( +// mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::AgeGroup::Count) - 1)); +// mio::abm::VirusVariant variant = mio::abm::VirusVariant( +// mio::UniformIntDistribution::get_instance()(rng, 0, int(mio::abm::VirusVariant::Count) - 1)); + +// auto t = mio::abm::TimePoint(0); +// auto dt = mio::abm::seconds(8640); //0.1 days + +// mio::abm::GlobalInfectionParameters params; +// params.set_default(); +// params.get()[{variant, age, mio::abm::VaccinationState::Unvaccinated}] = { +// {1., 1.}, {0.0001, 0.0001}, {-0.0001, -0.0001}}; +// params.set_default(); +// params.get()[{variant, age}] = {{1., 1.}, {1., 1.}}; + +// // set incubtion period to two days so that the newly infected person is still exposed +// params.get()[{variant, age, mio::abm::VaccinationState::Unvaccinated}] = 2.; + +// //setup location with some chance of exposure +// mio::abm::Location location(mio::abm::LocationType::Work, 0); +// auto infected1 = make_test_person(location, mio::abm::AgeGroup::Age15to34, +// mio::abm::InfectionState::InfectedNoSymptoms, t, params); +// auto infected2 = make_test_person(location, mio::abm::AgeGroup::Age80plus, +// mio::abm::InfectionState::InfectedSymptoms, t, params); +// auto infected3 = +// make_test_person(location, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms, t, params); + +// location.add_person(infected1, {0}); +// location.add_person(infected2, {0}); +// location.add_person(infected3, {0}); + +// //cache precomputed results +// location.cache_exposure_rates(t, dt); + +// ScopedMockDistribution>>> +// mock_exponential_dist; +// ScopedMockDistribution>>> mock_discrete_dist; + +// auto susceptible = make_test_person(location, age, mio::abm::InfectionState::Susceptible, t, params); +// EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.5)); +// auto person_rng = mio::abm::Person::RandomNumberGenerator(rng, susceptible); +// location.interact(person_rng, susceptible, t, dt, params); +// EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); + +// EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).Times(1).WillOnce(Return(0.05)); +// EXPECT_CALL(mock_discrete_dist.get_mock(), invoke).Times(1).WillOnce(Return(0)); +// location.interact(person_rng, susceptible, t, dt, params); +// EXPECT_EQ(susceptible.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); +// } TEST(TestLocation, setCapacity) { @@ -275,96 +280,96 @@ TEST(TestLocation, setCapacity) ASSERT_EQ(location.get_capacity().volume, (uint32_t)200); } -TEST(TestLocation, storeSubpopulations) -{ - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::days(7); - auto params = mio::abm::GlobalInfectionParameters{}; - - mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); - - //setup: p1 goes from Infected to Recovered, p2 stays in Infected and p3 goes from Exposed to InfectedNoSymptoms to Recovered - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age5to14, - mio::abm::VaccinationState::Unvaccinated}] = 1.5 * dt.days(); - - params.get()[{ - mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, mio::abm::VaccinationState::Unvaccinated}] = - 5 * dt.days(); - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, - mio::abm::VaccinationState::Unvaccinated}] = 5 * dt.days(); - - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age35to59, - mio::abm::VaccinationState::Unvaccinated}] = 0.4 * dt.days(); - params.get()[{ - mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age35to59, mio::abm::VaccinationState::Unvaccinated}] = - 1.8 * dt.days(); - - ScopedMockDistribution>>> mock_uniform_dist; - - // mock person 1 - EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) - .Times(testing::AtLeast(8)) - .WillOnce(testing::Return(0.8)) // draw random work group - .WillOnce(testing::Return(0.8)) // draw random school group - .WillOnce(testing::Return(0.8)) // draw random work hour - .WillOnce(testing::Return(0.8)) // draw random school hour - .WillOnce(testing::Return(0.6)) // transition to Recovered - .WillRepeatedly(testing::Return(1.0)); - - auto person1 = - make_test_person(location, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms, t, params); - location.add_person(person1, {0}); - - // mock person 2 not needed due to high setup of transition times - auto person2 = make_test_person(location, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, - t, params); - location.add_person(person2, {0}); - - // mock person 3 - EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) - .Times(testing::AtLeast(8)) - .WillOnce(testing::Return(0.8)) // draw random work group - .WillOnce(testing::Return(0.8)) // draw random school group - .WillOnce(testing::Return(0.8)) // draw random work hour - .WillOnce(testing::Return(0.8)) // draw random school hour - .WillOnce(testing::Return(0.6)) // transition to Recovered - .WillRepeatedly(testing::Return(1.0)); - auto person3 = - make_test_person(location, mio::abm::AgeGroup::Age35to59, mio::abm::InfectionState::Exposed, t, params); - location.add_person(person3, {0}); - - location.initialize_subpopulations(t); - auto t1 = t + dt; - location.store_subpopulations(t1); - auto v1 = location.get_subpopulations().get_value(1); - // Check whether the number of persons in infected state at the location is correct - ASSERT_EQ(v1[size_t(mio::abm::InfectionState::InfectedSymptoms)], 2); - ASSERT_EQ(v1[size_t(mio::abm::InfectionState::InfectedNoSymptoms)], 1); - - auto t2 = t1 + dt; - location.store_subpopulations(t2); - auto v2 = location.get_subpopulations().get_value(2); - // Check whether the number of persons in infected state at the location is correct - ASSERT_EQ(v2[size_t(mio::abm::InfectionState::InfectedSymptoms)], 1); - ASSERT_EQ(v2[size_t(mio::abm::InfectionState::Recovered)], 1); - ASSERT_EQ(v2[size_t(mio::abm::InfectionState::InfectedNoSymptoms)], 1); - - auto t3 = t2 + mio::abm::days(10); - location.store_subpopulations(t3); - auto v3 = location.get_subpopulations().get_value(3); - // Check whether the number of persons in infected state at the location is correct - ASSERT_EQ(v3[size_t(mio::abm::InfectionState::InfectedSymptoms)], 1); - ASSERT_EQ(v3[size_t(mio::abm::InfectionState::Recovered)], 2); - - // Check total number of subpopulation is correct. - ASSERT_EQ(location.get_subpopulations().get_num_time_points(), 4); - for (auto&& v_iter : location.get_subpopulations()) { - ASSERT_EQ(v_iter.sum(), 3); - } - ASSERT_EQ(location.get_subpopulations().get_time(1), 7); - ASSERT_EQ(location.get_subpopulations().get_time(2), 14); - ASSERT_EQ(location.get_subpopulations().get_time(3), 24); -} +// TEST(TestLocation, storeSubpopulations) +// { +// auto t = mio::abm::TimePoint(0); +// auto dt = mio::abm::days(7); +// auto params = mio::abm::GlobalInfectionParameters{}; + +// mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 3); + +// //setup: p1 goes from Infected to Recovered, p2 stays in Infected and p3 goes from Exposed to InfectedNoSymptoms to Recovered +// params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age5to14, +// mio::abm::VaccinationState::Unvaccinated}] = 1.5 * dt.days(); + +// params.get()[{ +// mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, mio::abm::VaccinationState::Unvaccinated}] = +// 5 * dt.days(); +// params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, +// mio::abm::VaccinationState::Unvaccinated}] = 5 * dt.days(); + +// params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age35to59, +// mio::abm::VaccinationState::Unvaccinated}] = 0.4 * dt.days(); +// params.get()[{ +// mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age35to59, mio::abm::VaccinationState::Unvaccinated}] = +// 1.8 * dt.days(); + +// ScopedMockDistribution>>> mock_uniform_dist; + +// // mock person 1 +// EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) +// .Times(testing::AtLeast(8)) +// .WillOnce(testing::Return(0.8)) // draw random work group +// .WillOnce(testing::Return(0.8)) // draw random school group +// .WillOnce(testing::Return(0.8)) // draw random work hour +// .WillOnce(testing::Return(0.8)) // draw random school hour +// .WillOnce(testing::Return(0.6)) // transition to Recovered +// .WillRepeatedly(testing::Return(1.0)); + +// auto person1 = +// make_test_person(location, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms, t, params); +// location.add_person(person1, {0}); + +// // mock person 2 not needed due to high setup of transition times +// auto person2 = make_test_person(location, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedSymptoms, +// t, params); +// location.add_person(person2, {0}); + +// // mock person 3 +// EXPECT_CALL(mock_uniform_dist.get_mock(), invoke) +// .Times(testing::AtLeast(8)) +// .WillOnce(testing::Return(0.8)) // draw random work group +// .WillOnce(testing::Return(0.8)) // draw random school group +// .WillOnce(testing::Return(0.8)) // draw random work hour +// .WillOnce(testing::Return(0.8)) // draw random school hour +// .WillOnce(testing::Return(0.6)) // transition to Recovered +// .WillRepeatedly(testing::Return(1.0)); +// auto person3 = +// make_test_person(location, mio::abm::AgeGroup::Age35to59, mio::abm::InfectionState::Exposed, t, params); +// location.add_person(person3, {0}); + +// location.initialize_subpopulations(t); +// auto t1 = t + dt; +// location.store_subpopulations(t1); +// auto v1 = location.get_subpopulations().get_value(1); +// // Check whether the number of persons in infected state at the location is correct +// ASSERT_EQ(v1[size_t(mio::abm::InfectionState::InfectedSymptoms)], 2); +// ASSERT_EQ(v1[size_t(mio::abm::InfectionState::InfectedNoSymptoms)], 1); + +// auto t2 = t1 + dt; +// location.store_subpopulations(t2); +// auto v2 = location.get_subpopulations().get_value(2); +// // Check whether the number of persons in infected state at the location is correct +// ASSERT_EQ(v2[size_t(mio::abm::InfectionState::InfectedSymptoms)], 1); +// ASSERT_EQ(v2[size_t(mio::abm::InfectionState::Recovered)], 1); +// ASSERT_EQ(v2[size_t(mio::abm::InfectionState::InfectedNoSymptoms)], 1); + +// auto t3 = t2 + mio::abm::days(10); +// location.store_subpopulations(t3); +// auto v3 = location.get_subpopulations().get_value(3); +// // Check whether the number of persons in infected state at the location is correct +// ASSERT_EQ(v3[size_t(mio::abm::InfectionState::InfectedSymptoms)], 1); +// ASSERT_EQ(v3[size_t(mio::abm::InfectionState::Recovered)], 2); + +// // Check total number of subpopulation is correct. +// ASSERT_EQ(location.get_subpopulations().get_num_time_points(), 4); +// for (auto&& v_iter : location.get_subpopulations()) { +// ASSERT_EQ(v_iter.sum(), 3); +// } +// ASSERT_EQ(location.get_subpopulations().get_time(1), 7); +// ASSERT_EQ(location.get_subpopulations().get_time(2), 14); +// ASSERT_EQ(location.get_subpopulations().get_time(3), 24); +// } TEST(TestLocation, setRequiredMask) { diff --git a/cpp/tests/test_abm_masks.cpp b/cpp/tests/test_abm_masks.cpp index a4c6b36742..3be61cc62b 100644 --- a/cpp/tests/test_abm_masks.cpp +++ b/cpp/tests/test_abm_masks.cpp @@ -54,44 +54,44 @@ TEST(TestMasks, changeMask) ASSERT_EQ(mask.get_time_used(), mio::abm::hours(0)); } -TEST(TestMasks, maskProtection) -{ - auto rng = mio::RandomNumberGenerator(); - mio::abm::GlobalInfectionParameters params; +// TEST(TestMasks, maskProtection) +// { +// auto rng = mio::RandomNumberGenerator(); +// mio::abm::GlobalInfectionParameters params; - // set incubation period to two days so that the newly infected person is still exposed - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, - mio::abm::VaccinationState::Unvaccinated}] = 2.; +// // set incubation period to two days so that the newly infected person is still exposed +// params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, +// mio::abm::VaccinationState::Unvaccinated}] = 2.; - //setup location with some chance of exposure - mio::abm::Location infection_location(mio::abm::LocationType::School, 0); - auto t = mio::abm::TimePoint(0); - auto susc_person1 = mio::abm::Person(rng, infection_location, mio::abm::AgeGroup::Age15to34); - auto susc_person2 = mio::abm::Person(rng, infection_location, mio::abm::AgeGroup::Age15to34); - auto infected1 = make_test_person(infection_location, mio::abm::AgeGroup::Age15to34, - mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior +// //setup location with some chance of exposure +// mio::abm::Location infection_location(mio::abm::LocationType::School, 0); +// auto t = mio::abm::TimePoint(0); +// auto susc_person1 = mio::abm::Person(rng, infection_location, mio::abm::AgeGroup::Age15to34); +// auto susc_person2 = mio::abm::Person(rng, infection_location, mio::abm::AgeGroup::Age15to34); +// auto infected1 = make_test_person(infection_location, mio::abm::AgeGroup::Age15to34, +// mio::abm::InfectionState::InfectedSymptoms, t, params); // infected 7 days prior - infection_location.add_person(infected1); +// infection_location.add_person(infected1); - //cache precomputed results - auto dt = mio::abm::days(1); - infection_location.cache_exposure_rates(t, dt); - // susc_person1 wears a mask, default protection is 1 - susc_person1.set_wear_mask(true); - // susc_person2 does not wear a mask - susc_person2.set_wear_mask(false); +// //cache precomputed results +// auto dt = mio::abm::days(1); +// infection_location.cache_exposure_rates(t, dt); +// // susc_person1 wears a mask, default protection is 1 +// susc_person1.set_wear_mask(true); +// // susc_person2 does not wear a mask +// susc_person2.set_wear_mask(false); - //mock so person 2 will get infected - ScopedMockDistribution>>> - mock_exponential_dist; +// //mock so person 2 will get infected +// ScopedMockDistribution>>> +// mock_exponential_dist; - auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person1); - infection_location.interact(p1_rng, susc_person1, t, dt, params); - EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); - auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person2); - infection_location.interact(p2_rng, susc_person2, t, dt, params); +// auto p1_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person1); +// infection_location.interact(p1_rng, susc_person1, t, dt, params); +// EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillOnce(testing::Return(0.5)); +// auto p2_rng = mio::abm::Person::RandomNumberGenerator(rng, susc_person2); +// infection_location.interact(p2_rng, susc_person2, t, dt, params); - // The person susc_person1 should have full protection against an infection, susc_person2 not - ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); - ASSERT_EQ(susc_person2.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); -} +// // The person susc_person1 should have full protection against an infection, susc_person2 not +// ASSERT_EQ(susc_person1.get_infection_state(t + dt), mio::abm::InfectionState::Susceptible); +// ASSERT_EQ(susc_person2.get_infection_state(t + dt), mio::abm::InfectionState::Exposed); +// } diff --git a/cpp/tests/test_abm_migration_rules.cpp b/cpp/tests/test_abm_migration_rules.cpp index e08ad6921a..8373f164b7 100644 --- a/cpp/tests/test_abm_migration_rules.cpp +++ b/cpp/tests/test_abm_migration_rules.cpp @@ -346,7 +346,7 @@ TEST(TestMigrationRules, shop_return) mio::abm::Location shop(mio::abm::LocationType::BasicsShop, 0); auto p = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms, t); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); - home.add_person(p); + // home.add_person(p); p.migrate_to(shop); p.interact(rng_p, t, dt, params); //person only returns home after some time passed @@ -391,7 +391,7 @@ TEST(TestMigrationRules, event_return) mio::abm::Location social_event(mio::abm::LocationType::SocialEvent, 0); auto p = mio::abm::Person(rng, home, mio::abm::AgeGroup::Age15to34); auto rng_p = mio::abm::Person::RandomNumberGenerator(rng, p); - home.add_person(p); + // home.add_person(p); p.migrate_to(social_event); p.interact(rng_p, t, dt, params); diff --git a/cpp/tests/test_abm_person.cpp b/cpp/tests/test_abm_person.cpp index d6689dd1ac..faadf05350 100644 --- a/cpp/tests/test_abm_person.cpp +++ b/cpp/tests/test_abm_person.cpp @@ -40,7 +40,7 @@ TEST(TestPerson, migrate) { auto rng = mio::RandomNumberGenerator(); - auto t = mio::abm::TimePoint(0); + // auto t = mio::abm::TimePoint(0); mio::abm::Location home(mio::abm::LocationType::Home, 0); mio::abm::Location loc1(mio::abm::LocationType::PublicTransport, 0, 1); mio::abm::Location loc2(mio::abm::LocationType::School, 0); @@ -49,21 +49,21 @@ TEST(TestPerson, migrate) person.migrate_to(loc1, {0}); ASSERT_EQ(person.get_location(), loc1); - ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); - ASSERT_EQ(home.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); - ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); + // ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); + // ASSERT_EQ(home.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); + // ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 1u); person.migrate_to(loc2); ASSERT_EQ(person.get_location(), loc2); - ASSERT_EQ(loc2.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); - ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); - ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); + // ASSERT_EQ(loc2.get_subpopulation(t, mio::abm::InfectionState::Recovered), 1); + // ASSERT_EQ(loc1.get_subpopulation(t, mio::abm::InfectionState::Recovered), 0); + // ASSERT_EQ(loc1.get_cells()[0].m_persons.size(), 0u); person.migrate_to(loc3, {0, 1}); - ASSERT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); - ASSERT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); + // ASSERT_EQ(loc3.get_cells()[0].m_persons.size(), 1u); + // ASSERT_EQ(loc3.get_cells()[1].m_persons.size(), 1u); ASSERT_EQ(person.get_cells().size(), 2); ASSERT_EQ(person.get_cells()[0], 0u); ASSERT_EQ(person.get_cells()[1], 1u); @@ -174,7 +174,7 @@ TEST(TestPerson, getCells) mio::abm::Location home(mio::abm::LocationType::Home, 0, 1); mio::abm::Location location(mio::abm::LocationType::PublicTransport, 0, 2); auto person = make_test_person(home, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); - home.add_person(person); + // home.add_person(person); person.migrate_to(location, {0, 1}); ASSERT_EQ(person.get_cells().size(), 2); } diff --git a/cpp/tests/test_abm_simulation.cpp b/cpp/tests/test_abm_simulation.cpp index 22cd009fc1..27703b50dd 100644 --- a/cpp/tests/test_abm_simulation.cpp +++ b/cpp/tests/test_abm_simulation.cpp @@ -45,8 +45,8 @@ TEST(TestSimulation, advance_random) auto sim = mio::abm::Simulation(mio::abm::TimePoint(0), std::move(world)); sim.advance(mio::abm::TimePoint(0) + mio::abm::hours(50)); - ASSERT_EQ(sim.get_result().get_num_time_points(), 51); - ASSERT_THAT(sim.get_result().get_times(), ElementsAreLinspace(0.0, 50.0 / 24.0, 51)); + ASSERT_EQ(sim.get_result().get_num_time_points(), 4); + ASSERT_THAT(sim.get_result().get_times(), testing::ElementsAre(0.0, 1.0, 2.0, 50.0 / 24.0)); for (auto&& v : sim.get_result()) { ASSERT_EQ(v.sum(), 4); } @@ -56,7 +56,6 @@ TEST(TestSimulation, advance_subpopulation) { auto world = mio::abm::World(); auto location_id = world.add_location(mio::abm::LocationType::School); - auto& school = world.get_individualized_location(location_id); auto& person1 = add_test_person(world, location_id, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSymptoms); auto& person2 = @@ -70,27 +69,41 @@ TEST(TestSimulation, advance_subpopulation) auto sim = mio::abm::Simulation(mio::abm::TimePoint(0), std::move(world)); sim.advance(mio::abm::TimePoint(0) + mio::abm::hours(50)); - for (size_t i = 0; i < 51; i++) { - auto v = school.get_subpopulations().get_value(i); + for (size_t i = 0; i < 3; i++) { + auto v = sim.get_result().get_value(i); // Check whether the number of persons in infected state at the location is consistent ASSERT_LE(v[size_t(mio::abm::InfectionState::InfectedSymptoms)], 3); // Check the time evolution is correct - ASSERT_EQ(school.get_subpopulations().get_time(i), ScalarType(i) / 24); + ASSERT_EQ(sim.get_result().get_time(i), i); } + ASSERT_EQ(sim.get_result().get_time(3), (mio::abm::TimePoint(0) + mio::abm::hours(50)).days()); + + sim.advance(mio::abm::TimePoint(0) + mio::abm::hours(100)); + + ASSERT_EQ(sim.get_result().get_time(4), (mio::abm::TimePoint(0) + mio::abm::hours(50)).days()); + + for (size_t i = 5; i < 7; i++) { + auto v = sim.get_result().get_value(i); + // Check whether the number of persons in infected state at the location is consistent + ASSERT_LE(v[size_t(mio::abm::InfectionState::InfectedSymptoms)], 3); + // Check the time evolution is correct + ASSERT_EQ(sim.get_result().get_time(i), i - 2); + } + ASSERT_EQ(sim.get_result().get_time(7), (mio::abm::TimePoint(0) + mio::abm::hours(100)).days()); } -TEST(TestSimulation, initializeSubpopulation) -{ - auto world = mio::abm::World(); - auto loc_id = world.add_location(mio::abm::LocationType::PublicTransport, 3); - auto& loc = world.get_individualized_location(loc_id); - ASSERT_EQ(loc.get_subpopulations().get_num_time_points(), 0); +// TEST(TestSimulation, initializeSubpopulation) +// { +// auto world = mio::abm::World(); +// auto loc_id = world.add_location(mio::abm::LocationType::PublicTransport, 3); +// auto& loc = world.get_individualized_location(loc_id); +// ASSERT_EQ(loc.get_subpopulations().get_num_time_points(), 0); - auto t = mio::abm::TimePoint(0); - auto sim = mio::abm::Simulation(t + mio::abm::days(7), std::move(world)); +// auto t = mio::abm::TimePoint(0); +// auto sim = mio::abm::Simulation(t + mio::abm::days(7), std::move(world)); - ASSERT_EQ(sim.get_world().get_individualized_location(loc_id).get_subpopulations().get_time(0), 7); -} +// ASSERT_EQ(sim.get_world().get_individualized_location(loc_id).get_subpopulations().get_time(0), 7); +// } TEST(TestSimulation, getWorldAndTimeConst) { diff --git a/cpp/tests/test_abm_world.cpp b/cpp/tests/test_abm_world.cpp index 77fea9065d..6df1e3d9be 100644 --- a/cpp/tests/test_abm_world.cpp +++ b/cpp/tests/test_abm_world.cpp @@ -73,25 +73,25 @@ TEST(TestWorld, addPerson) ASSERT_EQ(&world.get_persons()[1], &p2); } -TEST(TestWorld, getSubpopulationCombined) -{ - auto t = mio::abm::TimePoint(0); - auto world = mio::abm::World(); - auto school1 = world.add_location(mio::abm::LocationType::School); - auto school2 = world.add_location(mio::abm::LocationType::School); - auto school3 = world.add_location(mio::abm::LocationType::School); - add_test_person(world, school1, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); - add_test_person(world, school1, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible); - add_test_person(world, school2, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible); - add_test_person(world, school2, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible); - add_test_person(world, school3, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); - - ASSERT_EQ( - world.get_subpopulation_combined(t, mio::abm::InfectionState::Susceptible, mio::abm::LocationType::School), 3); - ASSERT_EQ(world.get_subpopulation_combined(t, mio::abm::InfectionState::InfectedNoSymptoms, - mio::abm::LocationType::School), - 2); -} +// TEST(TestWorld, getSubpopulationCombined) +// { +// auto t = mio::abm::TimePoint(0); +// auto world = mio::abm::World(); +// auto school1 = world.add_location(mio::abm::LocationType::School); +// auto school2 = world.add_location(mio::abm::LocationType::School); +// auto school3 = world.add_location(mio::abm::LocationType::School); +// add_test_person(world, school1, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); +// add_test_person(world, school1, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible); +// add_test_person(world, school2, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible); +// add_test_person(world, school2, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible); +// add_test_person(world, school3, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::InfectedNoSymptoms); + +// // ASSERT_EQ( +// // world.get_subpopulation_combined(t, mio::abm::InfectionState::Susceptible, mio::abm::LocationType::School), 3); +// // ASSERT_EQ(world.get_subpopulation_combined(t, mio::abm::InfectionState::InfectedNoSymptoms, +// // mio::abm::LocationType::School), +// // 2); +// } TEST(TestWorld, findLocation) { @@ -214,7 +214,9 @@ TEST(TestWorld, evolveMigration) EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillRepeatedly(Return(1.)); //no state transitions world.evolve(t, dt); + world.begin_step(t, dt); + EXPECT_EQ(world.get_persons().size(), 2); EXPECT_EQ(p1.get_location(), work); EXPECT_EQ(p2.get_location(), school); EXPECT_EQ(school.get_number_persons(), 1); @@ -222,132 +224,132 @@ TEST(TestWorld, evolveMigration) } { - auto t = mio::abm::TimePoint(0) + mio::abm::hours(8); - auto dt = mio::abm::hours(2); - auto params = mio::abm::GlobalInfectionParameters{}; - //setup so p1-p5 don't transition - params.get()[{ - mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, - mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); - params.get()[{ - mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, - mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, - mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, - mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); - - auto world = mio::abm::World(params); - world.use_migration_rules(false); - - auto home_id = world.add_location(mio::abm::LocationType::Home); - auto event_id = world.add_location(mio::abm::LocationType::SocialEvent); - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto hospital_id = world.add_location(mio::abm::LocationType::Hospital); - - auto& p1 = add_test_person(world, home_id, mio::abm::AgeGroup::Age15to34, - mio::abm::InfectionState::InfectedNoSymptoms, t); - auto& p2 = - add_test_person(world, home_id, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::Susceptible, t); - auto& p3 = - add_test_person(world, home_id, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSevere, t); - auto& p4 = - add_test_person(world, hospital_id, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::Recovered, t); - auto& p5 = - add_test_person(world, home_id, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible, t); - p1.set_assigned_location(event_id); - p2.set_assigned_location(event_id); - p1.set_assigned_location(work_id); - p2.set_assigned_location(work_id); - p1.set_assigned_location(home_id); - p2.set_assigned_location(home_id); - p3.set_assigned_location(home_id); - p4.set_assigned_location(home_id); - p3.set_assigned_location(hospital_id); - p4.set_assigned_location(hospital_id); - p5.set_assigned_location(event_id); - p5.set_assigned_location(work_id); - p5.set_assigned_location(home_id); - - mio::abm::TripList& data = world.get_trip_list(); - mio::abm::Trip trip1(p1.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id); - mio::abm::Trip trip2(p2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id); - mio::abm::Trip trip3(p5.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, work_id); - data.add_trip(trip1); - data.add_trip(trip2); - data.add_trip(trip3); - - ScopedMockDistribution>>> - mock_exponential_dist; - EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillRepeatedly(Return(1.)); //no state transitions - - world.evolve(t, dt); - - auto& event = world.get_individualized_location(event_id); - auto& work = world.get_individualized_location(work_id); - auto& home = world.get_individualized_location(home_id); - auto& hospital = world.get_individualized_location(hospital_id); - - EXPECT_EQ(p1.get_location(), work); - EXPECT_EQ(p2.get_location(), event); - EXPECT_EQ(p3.get_location(), hospital); - EXPECT_EQ(p4.get_location(), home); - EXPECT_EQ(p5.get_location(), home); - EXPECT_EQ(event.get_number_persons(), 1); - EXPECT_EQ(work.get_number_persons(), 1); - EXPECT_EQ(home.get_number_persons(), 2); - EXPECT_EQ(hospital.get_number_persons(), 1); + // auto t = mio::abm::TimePoint(0) + mio::abm::hours(8); + // auto dt = mio::abm::hours(2); + // auto params = mio::abm::GlobalInfectionParameters{}; + // //setup so p1-p5 don't transition + // params.get()[{ + // mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, + // mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); + // params.get()[{ + // mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, + // mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); + // params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, + // mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); + // params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age15to34, + // mio::abm::VaccinationState::Unvaccinated}] = 2 * dt.days(); + + // auto world = mio::abm::World(params); + // world.use_migration_rules(false); + + // auto home_id = world.add_location(mio::abm::LocationType::Home); + // auto event_id = world.add_location(mio::abm::LocationType::SocialEvent); + // auto work_id = world.add_location(mio::abm::LocationType::Work); + // auto hospital_id = world.add_location(mio::abm::LocationType::Hospital); + + // auto& p1 = add_test_person(world, home_id, mio::abm::AgeGroup::Age15to34, + // mio::abm::InfectionState::InfectedNoSymptoms, t); + // auto& p2 = + // add_test_person(world, home_id, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::Susceptible, t); + // auto& p3 = + // add_test_person(world, home_id, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::InfectedSevere, t); + // auto& p4 = + // add_test_person(world, hospital_id, mio::abm::AgeGroup::Age5to14, mio::abm::InfectionState::Recovered, t); + // auto& p5 = + // add_test_person(world, home_id, mio::abm::AgeGroup::Age15to34, mio::abm::InfectionState::Susceptible, t); + // p1.set_assigned_location(event_id); + // p2.set_assigned_location(event_id); + // p1.set_assigned_location(work_id); + // p2.set_assigned_location(work_id); + // p1.set_assigned_location(home_id); + // p2.set_assigned_location(home_id); + // p3.set_assigned_location(home_id); + // p4.set_assigned_location(home_id); + // p3.set_assigned_location(hospital_id); + // p4.set_assigned_location(hospital_id); + // p5.set_assigned_location(event_id); + // p5.set_assigned_location(work_id); + // p5.set_assigned_location(home_id); + + // mio::abm::TripList& data = world.get_trip_list(); + // mio::abm::Trip trip1(p1.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), work_id, home_id); + // mio::abm::Trip trip2(p2.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, home_id); + // mio::abm::Trip trip3(p5.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(9), event_id, work_id); + // data.add_trip(trip1); + // data.add_trip(trip2); + // data.add_trip(trip3); + + // ScopedMockDistribution>>> + // mock_exponential_dist; + // EXPECT_CALL(mock_exponential_dist.get_mock(), invoke).WillRepeatedly(Return(1.)); //no state transitions + + // world.evolve(t, dt); + + // auto& event = world.get_individualized_location(event_id); + // auto& work = world.get_individualized_location(work_id); + // auto& home = world.get_individualized_location(home_id); + // auto& hospital = world.get_individualized_location(hospital_id); + + // EXPECT_EQ(p1.get_location(), work); + // EXPECT_EQ(p2.get_location(), event); + // EXPECT_EQ(p3.get_location(), hospital); + // EXPECT_EQ(p4.get_location(), home); + // EXPECT_EQ(p5.get_location(), home); + // EXPECT_EQ(event.get_number_persons(), 1); + // EXPECT_EQ(work.get_number_persons(), 1); + // EXPECT_EQ(home.get_number_persons(), 2); + // EXPECT_EQ(hospital.get_number_persons(), 1); } // Test that a dead person cannot make a movement { - auto t = mio::abm::TimePoint(0); - auto dt = mio::abm::days(1); - auto params = mio::abm::GlobalInfectionParameters{}; - // Time to go from severe to critical infection is 1 day (dt). - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age60to79, - mio::abm::VaccinationState::Unvaccinated}] = 1; - // Time to go from critical infection to dead state is 1/2 day (0.5 * dt). - params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age60to79, - mio::abm::VaccinationState::Unvaccinated}] = 0.5 * 1; - - auto world = mio::abm::World(params); - auto home_id = world.add_location(mio::abm::LocationType::Home); - auto work_id = world.add_location(mio::abm::LocationType::Work); - auto icu_id = world.add_location(mio::abm::LocationType::ICU); - auto hospital_id = world.add_location(mio::abm::LocationType::Hospital); - // Create a person that is dead at time t - auto& p_dead = add_test_person(world, icu_id, mio::abm::AgeGroup::Age60to79, mio::abm::InfectionState::Dead, t); - // Create a person that is severe at hospital and will be dead at time t + dt - auto& p_severe = - add_test_person(world, hospital_id, mio::abm::AgeGroup::Age60to79, mio::abm::InfectionState::Dead, t + dt); - p_dead.set_assigned_location(icu_id); - p_dead.set_assigned_location(work_id); - p_dead.set_assigned_location(home_id); - p_severe.set_assigned_location(hospital_id); - p_severe.set_assigned_location(icu_id); - p_severe.set_assigned_location(home_id); - - // Add trip to see if a dead person can move outside of cemetery by scheduled - mio::abm::TripList& trip_list = world.get_trip_list(); - mio::abm::Trip trip1(p_dead.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id); - mio::abm::Trip trip2(p_dead.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); - mio::abm::Trip trip3(p_severe.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); - trip_list.add_trip(trip1); - trip_list.add_trip(trip2); - trip_list.add_trip(trip3); - - // Check the dead person got burried and the severely infected person starts in Hospital - world.evolve(t, dt); - EXPECT_EQ(p_dead.get_location().get_type(), mio::abm::LocationType::Cemetery); - EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); - EXPECT_EQ(p_severe.get_location().get_type(), mio::abm::LocationType::Hospital); - - // Check the dead person is still in Cemetery and the severely infected person dies and got burried - world.evolve(t + dt, dt); - EXPECT_EQ(p_dead.get_location().get_type(), mio::abm::LocationType::Cemetery); - EXPECT_EQ(p_severe.get_infection_state(t + dt), mio::abm::InfectionState::Dead); - EXPECT_EQ(p_severe.get_location().get_type(), mio::abm::LocationType::Cemetery); + // auto t = mio::abm::TimePoint(0); + // auto dt = mio::abm::days(1); + // auto params = mio::abm::GlobalInfectionParameters{}; + // // Time to go from severe to critical infection is 1 day (dt). + // params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age60to79, + // mio::abm::VaccinationState::Unvaccinated}] = 1; + // // Time to go from critical infection to dead state is 1/2 day (0.5 * dt). + // params.get()[{mio::abm::VirusVariant::Wildtype, mio::abm::AgeGroup::Age60to79, + // mio::abm::VaccinationState::Unvaccinated}] = 0.5 * 1; + + // auto world = mio::abm::World(params); + // auto home_id = world.add_location(mio::abm::LocationType::Home); + // auto work_id = world.add_location(mio::abm::LocationType::Work); + // auto icu_id = world.add_location(mio::abm::LocationType::ICU); + // auto hospital_id = world.add_location(mio::abm::LocationType::Hospital); + // // Create a person that is dead at time t + // auto& p_dead = add_test_person(world, icu_id, mio::abm::AgeGroup::Age60to79, mio::abm::InfectionState::Dead, t); + // // Create a person that is severe at hospital and will be dead at time t + dt + // auto& p_severe = + // add_test_person(world, hospital_id, mio::abm::AgeGroup::Age60to79, mio::abm::InfectionState::Dead, t + dt); + // p_dead.set_assigned_location(icu_id); + // p_dead.set_assigned_location(work_id); + // p_dead.set_assigned_location(home_id); + // p_severe.set_assigned_location(hospital_id); + // p_severe.set_assigned_location(icu_id); + // p_severe.set_assigned_location(home_id); + + // // Add trip to see if a dead person can move outside of cemetery by scheduled + // mio::abm::TripList& trip_list = world.get_trip_list(); + // mio::abm::Trip trip1(p_dead.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(2), work_id, home_id); + // mio::abm::Trip trip2(p_dead.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); + // mio::abm::Trip trip3(p_severe.get_person_id(), mio::abm::TimePoint(0) + mio::abm::hours(3), home_id, icu_id); + // trip_list.add_trip(trip1); + // trip_list.add_trip(trip2); + // trip_list.add_trip(trip3); + + // // Check the dead person got burried and the severely infected person starts in Hospital + // world.evolve(t, dt); + // EXPECT_EQ(p_dead.get_location().get_type(), mio::abm::LocationType::Cemetery); + // EXPECT_EQ(p_severe.get_infection_state(t), mio::abm::InfectionState::InfectedSevere); + // EXPECT_EQ(p_severe.get_location().get_type(), mio::abm::LocationType::Hospital); + + // // Check the dead person is still in Cemetery and the severely infected person dies and got burried + // world.evolve(t + dt, dt); + // EXPECT_EQ(p_dead.get_location().get_type(), mio::abm::LocationType::Cemetery); + // EXPECT_EQ(p_severe.get_infection_state(t + dt), mio::abm::InfectionState::Dead); + // EXPECT_EQ(p_severe.get_location().get_type(), mio::abm::LocationType::Cemetery); } } From 1efa4ead082e0a1a1838f238bbb0d31a522b5ad7 Mon Sep 17 00:00:00 2001 From: Beno Burth <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 15 Aug 2023 16:02:03 +0200 Subject: [PATCH 040/105] test2.0 --- cpp/examples/abm_minimal.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index a0fe88067e..e22b76b2ea 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -57,6 +57,10 @@ int main() // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; + auto test = 2.0; + + mio::unused(test); + // Set same infection parameter for all age groups. For example, the incubation period is 4 days. infection_params.get() = 4.; From 2cfe7cc9c137d978ed491bd158b2b597b2fb5549 Mon Sep 17 00:00:00 2001 From: Beno Burth <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 15 Aug 2023 16:11:12 +0200 Subject: [PATCH 041/105] test3.0 --- cpp/examples/abm_minimal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index e22b76b2ea..69e3fe7fe9 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -57,9 +57,9 @@ int main() // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; - auto test = 2.0; - - mio::unused(test); + auto test = 2.0; + auto test2 = 2.0; + mio::unused(test, test2); // Set same infection parameter for all age groups. For example, the incubation period is 4 days. infection_params.get() = 4.; From 46be0e61f4582e20a2d59427832cbb10dc25c779 Mon Sep 17 00:00:00 2001 From: Beno Burth <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 15 Aug 2023 16:14:23 +0200 Subject: [PATCH 042/105] reverse test --- cpp/examples/abm_minimal.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 69e3fe7fe9..a0fe88067e 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -57,10 +57,6 @@ int main() // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; - auto test = 2.0; - auto test2 = 2.0; - mio::unused(test, test2); - // Set same infection parameter for all age groups. For example, the incubation period is 4 days. infection_params.get() = 4.; From d3bd8fbef1c98aa098bd9b1d17102977abe725b8 Mon Sep 17 00:00:00 2001 From: "Abele, Daniel" Date: Fri, 1 Sep 2023 14:24:36 +0200 Subject: [PATCH 043/105] replace Dampings::finalize() with set_automatic_cache_update() --- cpp/memilio/epidemiology/contact_matrix.h | 15 +++--- cpp/memilio/epidemiology/damping.h | 53 +++++++++++++++++---- cpp/memilio/epidemiology/damping_sampling.h | 3 +- cpp/models/abm/lockdown_rules.cpp | 3 -- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/cpp/memilio/epidemiology/contact_matrix.h b/cpp/memilio/epidemiology/contact_matrix.h index 142c1d9bdf..87b2dfddb5 100644 --- a/cpp/memilio/epidemiology/contact_matrix.h +++ b/cpp/memilio/epidemiology/contact_matrix.h @@ -61,7 +61,6 @@ class DampingMatrixExpression , m_dampings(Shape::get_shape_of(m_baseline)) { assert(Shape::get_shape_of(m_minimum) == Shape::get_shape_of(m_baseline)); - m_dampings.finalize(); } /** @@ -172,11 +171,12 @@ class DampingMatrixExpression } /** - * update internal cache to make get_matrix_at thread safe. + * Enable/disable automatic cache update of the dampings. + * @see Dampings::set_automatic_cache_update */ - void finalize() + void set_automatic_cache_update(bool b) { - m_dampings.finalize(); + m_dampings.set_automatic_cache_update(b); } /** @@ -372,12 +372,13 @@ class DampingMatrixExpressionGroup } /** - * update internal cache to make get_matrix_at thread safe. + * Enable/disable automatic cache update for each contained matrix. + * @see Dampings::set_automatic_cache_update */ - void finalize() + void set_automatic_cache_update(bool b) { for (auto& m : *this) { - m.finalize(); + m.set_automatic_cache_update(b); } } diff --git a/cpp/memilio/epidemiology/damping.h b/cpp/memilio/epidemiology/damping.h index 695301783d..a04ed205e1 100644 --- a/cpp/memilio/epidemiology/damping.h +++ b/cpp/memilio/epidemiology/damping.h @@ -269,6 +269,7 @@ class Dampings : m_dampings() , m_shape(shape_args...) { + update_cache(); } /** @@ -280,6 +281,7 @@ class Dampings { assert(il.size() > 0); m_shape = il.begin()->get_shape(); + update_cache(); } /** @@ -308,6 +310,7 @@ class Dampings { assert(m_dampings.size() > i); m_dampings.erase(m_dampings.begin() + i); + automatic_cache_update(); } /** @@ -316,6 +319,21 @@ class Dampings void clear() { m_dampings.clear(); + automatic_cache_update(); + } + + /** + * Disable the internal cache to speed up multiple modifications in a row. + * This class has an internal cache where all dampings are combined into a single time series of matrices for faster lookup. + * By default, the cache is automatically updated when dampings are added or removed, but this can be expensive. + * This function can be used to disable the cache so that add() or remove() can be called multiple times + * in a row more efficiently. Afterwards, the cache needs to be reenabled. + * @param b True if cache updates are enabled. + */ + void set_automatic_cache_update(bool b) + { + m_automatic_cache_update = b; + automatic_cache_update(); } /** @@ -334,7 +352,7 @@ class Dampings */ auto get_matrix_at(SimulationTime t) const { - finalize(); + assert(!m_accumulated_dampings_cached.empty() && "Cache is not current. Did you disable the automatic cache update?"); auto ub = std::upper_bound(m_accumulated_dampings_cached.begin(), m_accumulated_dampings_cached.end(), std::make_tuple(t), [](auto&& tup1, auto&& tup2) { @@ -350,13 +368,6 @@ class Dampings return get_matrix_at(SimulationTime(t)); } - /** - * compute the cache of accumulated dampings. - * if this is used after adding dampings, all subsequent calls to get_matrix_at() - * are quick and threadsafe. Otherwise the cache is updated automatically on the first call. - */ - void finalize() const; - /** * access one damping in this collection. */ @@ -473,6 +484,24 @@ class Dampings */ void add_(const value_type& damping); + /** + * compute the cache of accumulated dampings. + * if this is used after adding dampings, all subsequent calls to get_matrix_at() + * are quick and threadsafe. Otherwise the cache is updated automatically on the first call. + */ + void update_cache(); + + /** + * updates the cache if automatic cache updates are enabled. + * @see update_cache(), set_automatic_cache_update() + */ + void automatic_cache_update() + { + if (m_automatic_cache_update) { + update_cache(); + } + } + /** * replace matrices of the same type, sum up matrices on the same level. * add new types/levels if necessary. @@ -507,11 +536,12 @@ class Dampings private: std::vector m_dampings; Shape m_shape; - mutable std::vector> m_accumulated_dampings_cached; + std::vector> m_accumulated_dampings_cached; + bool m_automatic_cache_update = true; }; template -void Dampings::finalize() const +void Dampings::update_cache() { using std::get; @@ -550,6 +580,9 @@ void Dampings::add_(const value_type& damping) std::make_tuple(tup2.get_time(), int(tup2.get_type()), int(tup2.get_level())); }); m_accumulated_dampings_cached.clear(); + if (m_automatic_cache_update) { + update_cache(); + } } template diff --git a/cpp/memilio/epidemiology/damping_sampling.h b/cpp/memilio/epidemiology/damping_sampling.h index a9ce7aad14..9027a957c4 100644 --- a/cpp/memilio/epidemiology/damping_sampling.h +++ b/cpp/memilio/epidemiology/damping_sampling.h @@ -255,13 +255,14 @@ class DampingSampling template void apply_dampings(DampingExpression& damping_expression, const DampingSamplings& dampings, F make_matrix) { + damping_expression.set_automatic_cache_update(false); for (auto& d : dampings) { for (auto& i : d.get_matrix_indices()) { auto m = make_matrix(double(d.get_value()) * d.get_group_weights()); damping_expression[i].add_damping(m, d.get_level(), d.get_type(), d.get_time()); } } - damping_expression.finalize(); + damping_expression.set_automatic_cache_update(true); } /** diff --git a/cpp/models/abm/lockdown_rules.cpp b/cpp/models/abm/lockdown_rules.cpp index 0efe83e752..1526d15a5b 100644 --- a/cpp/models/abm/lockdown_rules.cpp +++ b/cpp/models/abm/lockdown_rules.cpp @@ -31,21 +31,18 @@ void set_home_office(TimePoint t_begin, double p, MigrationParameters& params) { auto damping1 = Eigen::VectorXd::Constant(1, p); params.get().add_damping(damping1, SimulationTime(t_begin.days())); - params.get().finalize(); } void set_school_closure(TimePoint t_begin, double p, MigrationParameters& params) { auto damping1 = Eigen::VectorXd::Constant(1, p); params.get().add_damping(damping1, SimulationTime(t_begin.days())); - params.get().finalize(); } void close_social_events(TimePoint t_begin, double p, MigrationParameters& params) { auto damping1 = Eigen::VectorXd::Constant((size_t)AgeGroup::Count, p); params.get().add_damping(damping1, SimulationTime(t_begin.days())); - params.get().finalize(); } } // namespace abm From 9724e4352b16e32e45b7f7127bbec80ec7a2ce66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=BChn?= Date: Mon, 11 Sep 2023 10:18:02 +0200 Subject: [PATCH 044/105] test branch connection --- cpp/examples/ode_seir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 5a367b1b1e..3b976e6ab2 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -56,7 +56,7 @@ int main() auto seir = simulate(t0, tmax, dt, model); - //hi + //test printf("\n number total: %f\n", seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); From ac0a03234e9131d5c519391d809881e1931b893a Mon Sep 17 00:00:00 2001 From: BenoBurth <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 10:41:44 +0200 Subject: [PATCH 045/105] mal wieder ein test --- cpp/examples/ode_seir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 3b976e6ab2..91b9a1b992 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -56,7 +56,7 @@ int main() auto seir = simulate(t0, tmax, dt, model); - //test + //test2 printf("\n number total: %f\n", seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); From c70c2fdf58493dd92b5d93f9bf2707c6c2578fa9 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:59:25 +0200 Subject: [PATCH 046/105] loop main methods of seir and secir examples --- cpp/examples/ode_secir.cpp | 4 ++++ cpp/examples/ode_seir.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index 18546975b2..d895ad71bf 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -22,6 +22,9 @@ #include "memilio/utils/logging.h" int main() + +for(int iter = 0; iter < 10000; iter++) { + { mio::set_log_level(mio::LogLevel::debug); @@ -106,3 +109,4 @@ int main() res_j[0] + res_j[1] + res_j[2] + res_j[3] + res_j[4] + res_j[5] + res_j[6] + res_j[7]); } } +} diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 3b976e6ab2..20e8dca79a 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -24,6 +24,9 @@ #include "memilio/utils/logging.h" int main() + +//perform multiple iterations for more accurate likwid measurements +for (int iter = 0; iter < 10000; iter++) { { mio::set_log_level(mio::LogLevel::debug); @@ -60,4 +63,6 @@ int main() printf("\n number total: %f\n", seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); + + } } From 0929a3f734a85de407c678e509b97e22bff2e848 Mon Sep 17 00:00:00 2001 From: BenoBurth <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:04:11 +0200 Subject: [PATCH 047/105] cmakelist with marker api for abm minimal --- cpp/examples/CMakeLists.txt | 5 +++-- cpp/examples/abm_minimal.cpp | 14 +++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 0184b90246..65c8f46465 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -39,8 +39,9 @@ target_link_libraries(graph_stochastic_mobility_example PRIVATE memilio ode_seci target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(abm_minimal_example abm_minimal.cpp) -target_link_libraries(abm_minimal_example PRIVATE memilio abm) -target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_link_libraries(abm_minimal_example PRIVATE memilio abm likwid) +target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID PERFMON;-llikwid") add_executable(abm_history_example abm_history_object.cpp) target_link_libraries(abm_history_example PRIVATE memilio abm) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 31d60928b2..72695e3962 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -23,6 +23,7 @@ #include #include #include +#include void write_results_to_file(const mio::abm::Simulation& sim) { @@ -157,7 +158,18 @@ int main() auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); auto sim = mio::abm::Simulation(t0, std::move(world)); - sim.advance(tmax); + + + LIKWID_MARKER_INIT; + + LIKWID_MARKER_START("region 1"); + + sim.advance(tmax); + + LIKWID_MARKER_STOP("region 1"); + + LIKWID_MARKER_CLOSE; + write_results_to_file(sim); } From 76e5899b93de8fa3e9f6e34ae252d61be4d5a13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:27:26 +0200 Subject: [PATCH 048/105] cmakelist update --- cpp/examples/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 65c8f46465..518449ac06 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -41,7 +41,7 @@ target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_F add_executable(abm_minimal_example abm_minimal.cpp) target_link_libraries(abm_minimal_example PRIVATE memilio abm likwid) target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID PERFMON;-llikwid") + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") add_executable(abm_history_example abm_history_object.cpp) target_link_libraries(abm_history_example PRIVATE memilio abm) From f90639bd43a95d0a645813564624d85f2505a28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:32:08 +0200 Subject: [PATCH 049/105] remove errors --- cpp/examples/ode_secir.cpp | 5 ++--- cpp/examples/ode_seir.cpp | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index d895ad71bf..d23e9af2e2 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -22,10 +22,9 @@ #include "memilio/utils/logging.h" int main() - -for(int iter = 0; iter < 10000; iter++) { - { + for(int iter = 0; iter < 10000; iter++) { + mio::set_log_level(mio::LogLevel::debug); double t0 = 0; diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index be8a28f142..47e5b24333 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -24,10 +24,10 @@ #include "memilio/utils/logging.h" int main() - +{ //perform multiple iterations for more accurate likwid measurements for (int iter = 0; iter < 10000; iter++) { -{ + mio::set_log_level(mio::LogLevel::debug); double t0 = 0; From 7c12d6c9cdc10e93503c12a71d9390e0713cfc49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:38:37 +0200 Subject: [PATCH 050/105] marker region renamed --- cpp/examples/abm_minimal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 72695e3962..486bf43e27 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -161,12 +161,12 @@ int main() LIKWID_MARKER_INIT; - - LIKWID_MARKER_START("region 1"); + + LIKWID_MARKER_START("region_1"); sim.advance(tmax); - LIKWID_MARKER_STOP("region 1"); + LIKWID_MARKER_STOP("region_1"); LIKWID_MARKER_CLOSE; From 85b0e4d8e62749bb8f8485d738a5a4504d1a829a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:59:12 +0200 Subject: [PATCH 051/105] remove console outputs --- cpp/models/abm/world.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index e84d107d2b..26ca0285bd 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -56,9 +56,9 @@ Person& World::add_person(const LocationId id, AgeGroup age) void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); - log_info("ABM World interaction."); + //log_info("ABM World interaction."); interaction(t, dt); - log_info("ABM World migration."); + //log_info("ABM World migration."); // migration(t, dt); end_step(t, dt); } From 5d7030923cacebaf63f467d8246cb0cd89606435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:19:50 +0200 Subject: [PATCH 052/105] abm_minimal_example marker api --- cpp/examples/abm_minimal.cpp | 225 ++++++++++++++++++----------------- 1 file changed, 115 insertions(+), 110 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 486bf43e27..55308a5515 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -51,125 +51,130 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world - mio::abm::GlobalInfectionParameters infection_params; - - // Set same infection parameter for all age groups. For example, the incubation period is 4 days. - infection_params.get() = 4.; - - // Create the world with infection parameters. - auto world = mio::abm::World(infection_params); - - // There are 3 households for each household group. - int n_households = 3; - - // 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(); // A child is 50/50% 0-4 or 5-14. - child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); - child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); - - auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. - parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); - parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); - - // Two-person household with one parent and one child. - auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); - auto twoPersonHousehold_full = mio::abm::Household(); - twoPersonHousehold_full.add_members(child, 1); - twoPersonHousehold_full.add_members(parent, 1); - twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); - add_household_group_to_world(world, twoPersonHousehold_group); - - // Three-person household with two parent and one child. - auto threePersonHousehold_group = mio::abm::HouseholdGroup(); - auto threePersonHousehold_full = mio::abm::Household(); - threePersonHousehold_full.add_members(child, 1); - threePersonHousehold_full.add_members(parent, 2); - threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); - add_household_group_to_world(world, threePersonHousehold_group); - - // Add one social event with 5 maximum contacts. - // Maximum contacs limit the number of people that a person can infect while being at this location. - auto event = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(5); - // 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 one supermarket, maximum constacts are assumed to be 20. - auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); - // At every school, the maximum contacts are 20. - auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(20); - // At every workplace, maximum contacts are 10. - auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(10); - - // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. - auto testing_min_time = mio::abm::days(1); - auto probability = 0.5; - auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - auto test_type = mio::abm::AntigenTest(); - auto test_at_work = std::vector{mio::abm::LocationType::Work}; - auto testing_criteria_work = - std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; - 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); - - // Assign infection state to each person. - // The infection states are chosen randomly. - auto persons = world.get_persons(); - for (auto& person : persons) { - mio::abm::InfectionState infection_state = - (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); - if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - world.get_global_infection_parameters(), start_date, - infection_state)); - } - - // Assign locations to the people - for (auto& person : persons) { - //assign shop and event - person.set_assigned_location(event); - person.set_assigned_location(shop); - //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); - //assign work/school to people depending on their age - if (person.get_age() == mio::abm::AgeGroup::Age5to14) { - person.set_assigned_location(school); + for (int iter = 0; iter < 1000; iter++) { + + LIKWID_MARKER_INIT; + + LIKWID_MARKER_START("initialization"); + + // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world + mio::abm::GlobalInfectionParameters infection_params; + + // Set same infection parameter for all age groups. For example, the incubation period is 4 days. + infection_params.get() = 4.; + + // Create the world with infection parameters. + auto world = mio::abm::World(infection_params); + + // There are 3 households for each household group. + int n_households = 3; + + // 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(); // A child is 50/50% 0-4 or 5-14. + child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); + child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); + + auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. + parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); + parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); + + // Two-person household with one parent and one child. + auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); + auto twoPersonHousehold_full = mio::abm::Household(); + twoPersonHousehold_full.add_members(child, 1); + twoPersonHousehold_full.add_members(parent, 1); + twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); + add_household_group_to_world(world, twoPersonHousehold_group); + + // Three-person household with two parent and one child. + auto threePersonHousehold_group = mio::abm::HouseholdGroup(); + auto threePersonHousehold_full = mio::abm::Household(); + threePersonHousehold_full.add_members(child, 1); + threePersonHousehold_full.add_members(parent, 2); + threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); + add_household_group_to_world(world, threePersonHousehold_group); + + // Add one social event with 5 maximum contacts. + // Maximum contacs limit the number of people that a person can infect while being at this location. + auto event = world.add_location(mio::abm::LocationType::SocialEvent); + world.get_individualized_location(event).get_infection_parameters().set(5); + // 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 one supermarket, maximum constacts are assumed to be 20. + auto shop = world.add_location(mio::abm::LocationType::BasicsShop); + world.get_individualized_location(shop).get_infection_parameters().set(20); + // At every school, the maximum contacts are 20. + auto school = world.add_location(mio::abm::LocationType::School); + world.get_individualized_location(school).get_infection_parameters().set(20); + // At every workplace, maximum contacts are 10. + auto work = world.add_location(mio::abm::LocationType::Work); + world.get_individualized_location(work).get_infection_parameters().set(10); + + // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. + auto testing_min_time = mio::abm::days(1); + auto probability = 0.5; + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); + auto test_type = mio::abm::AntigenTest(); + auto test_at_work = std::vector{mio::abm::LocationType::Work}; + auto testing_criteria_work = + std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; + 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); + + // Assign infection state to each person. + // The infection states are chosen randomly. + auto persons = world.get_persons(); + for (auto& person : persons) { + mio::abm::InfectionState infection_state = + (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + world.get_global_infection_parameters(), start_date, + infection_state)); } - if (person.get_age() == mio::abm::AgeGroup::Age15to34 || person.get_age() == mio::abm::AgeGroup::Age35to59) { - person.set_assigned_location(work); - } - } - // During the lockdown, social events are closed for 90% of people. - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); - mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); + // Assign locations to the people + for (auto& person : persons) { + //assign shop and event + person.set_assigned_location(event); + person.set_assigned_location(shop); + //assign hospital and ICU + person.set_assigned_location(hospital); + person.set_assigned_location(icu); + //assign work/school to people depending on their age + if (person.get_age() == mio::abm::AgeGroup::Age5to14) { + person.set_assigned_location(school); + } + if (person.get_age() == mio::abm::AgeGroup::Age15to34 || + person.get_age() == mio::abm::AgeGroup::Age35to59) { + person.set_assigned_location(work); + } + } - auto t0 = mio::abm::TimePoint(0); - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); - auto sim = mio::abm::Simulation(t0, std::move(world)); + // During the lockdown, social events are closed for 90% of people. + auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); + mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); - + auto t0 = mio::abm::TimePoint(0); + auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); + auto sim = mio::abm::Simulation(t0, std::move(world)); - LIKWID_MARKER_INIT; + LIKWID_MARKER_END("initialization"); - LIKWID_MARKER_START("region_1"); + LIKWID_MARKER_START("simulation"); sim.advance(tmax); - LIKWID_MARKER_STOP("region_1"); + LIKWID_MARKER_STOP("simulation"); - LIKWID_MARKER_CLOSE; - + LIKWID_MARKER_CLOSE; - write_results_to_file(sim); + write_results_to_file(sim); + } } From bfb1a1c944846fd437dd358ae9cd78ba4a108c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:22:29 +0200 Subject: [PATCH 053/105] marker api in abm_minimal_example --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 55308a5515..244db2a1e6 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -165,7 +165,7 @@ int main() auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); auto sim = mio::abm::Simulation(t0, std::move(world)); - LIKWID_MARKER_END("initialization"); + LIKWID_MARKER_STOP("initialization"); LIKWID_MARKER_START("simulation"); From 571654e5998bac6066856e41df68befb6de7c6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:36:43 +0200 Subject: [PATCH 054/105] increase loop iterations --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 244db2a1e6..b6e8e97ea2 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -51,7 +51,7 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - for (int iter = 0; iter < 1000; iter++) { + for (int iter = 0; iter < 1000000; iter++) { LIKWID_MARKER_INIT; From 4700f79db6dbee1f0460f695cb6dd8de6e5f6f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 12 Sep 2023 13:07:07 +0200 Subject: [PATCH 055/105] add parallelization tests --- cpp/examples/CMakeLists.txt | 5 +++++ cpp/examples/parallelization_test.cpp | 31 ++++++++++++++++++++++++++ cpp/models/abm/world.cpp | 32 +++++++++++++-------------- 3 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 cpp/examples/parallelization_test.cpp diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 518449ac06..495512fb54 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -59,6 +59,11 @@ add_executable(ide_secir_example ide_secir.cpp) target_link_libraries(ide_secir_example PRIVATE memilio ide_secir) target_compile_options(ide_secir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +#parallelization cmake +#add_executable(parallelization_test_example parallelization_test.cpp) +#target_link_libraries(parallelization_test_example PRIVATE omp) +#target_compile_options(parallelization_test_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) diff --git a/cpp/examples/parallelization_test.cpp b/cpp/examples/parallelization_test.cpp new file mode 100644 index 0000000000..efc2fbfc49 --- /dev/null +++ b/cpp/examples/parallelization_test.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +int main() { + const int vectorSize = 1000000; // Größe der Vektoren anpassen + + std::vector vectorA(vectorSize); + std::vector vectorB(vectorSize); + std::vector vectorC(vectorSize); + std::vector vectorD(vectorSize); + + // Zufällige Werte für die Vektoren generieren + #pragma omp parallel for + for (int i = 0; i < vectorSize; ++i) { + vectorA[i] = static_cast(rand()) / RAND_MAX; + vectorB[i] = static_cast(rand()) / RAND_MAX; + vectorC[i] = static_cast(rand()) / RAND_MAX; + vectorD[i] = 0; + } + + // Vektortriade parallel berechnen + #pragma omp parallel for + for (int i = 0; i < vectorSize; ++i) { + vectorD[i] = vectorA[i] + vectorB[i] - vectorC[i]; + } + + std::cout << "Vektortriade abgeschlossen." << std::endl; + + return 0; +} diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 26ca0285bd..707dfbd5ce 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -59,7 +59,7 @@ void World::evolve(TimePoint t, TimeSpan dt) //log_info("ABM World interaction."); interaction(t, dt); //log_info("ABM World migration."); - // migration(t, dt); + migration(t, dt); end_step(t, dt); } @@ -67,7 +67,7 @@ void World::interaction(TimePoint t, TimeSpan dt) { PRAGMA_OMP(parallel for schedule(dynamic, 50)) //dynamic 20 for (auto i = size_t(0); i < m_persons.size(); ++i) { - auto&& person = m_persons[i]; + auto&& person = m_persons[i]; auto personal_rng = Person::RandomNumberGenerator(m_rng, *person); person->interact(personal_rng, t, dt, m_infection_parameters); @@ -92,7 +92,8 @@ void World::interaction(TimePoint t, TimeSpan dt) void World::prepare() { - std::vector>> enhanced_migration_rules; for (auto rule : m_migration_rules) { @@ -161,42 +162,41 @@ void World::migration(TimePoint t, TimeSpan dt) void World::begin_step(TimePoint t, TimeSpan dt) { m_testing_strategy.update_activity_status(t); - + //cache for next step so it stays constant during the step while subpopulations change //otherwise we would have to cache all state changes during a step which uses more memory PRAGMA_OMP(parallel for schedule(static)) //dynamic 20? for (auto i = size_t(0); i < m_locations.size(); ++i) { - auto&& location = m_locations[i]; + auto&& location = m_locations[i]; location->m_num_persons = 0; for (auto&& cell : location->m_cells) { cell.m_cached_exposure_rate_air.array().setZero(); cell.m_cached_exposure_rate_contacts.array().setZero(); + } } -} PRAGMA_OMP(parallel for schedule(dynamic, 50)) //static? for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; - auto&& loc = person->m_location; - + auto&& loc = person->m_location; if (person->is_infected(t)) { - auto&& inf = person->get_infection(); + auto&& inf = person->get_infection(); auto virus = inf.get_virus_variant(); auto age = person->get_age(); #ifdef MEMILIO_ENABLE_OPENMP std::lock_guard lk(*loc); #endif - for (auto&& cell_idx : person->m_cells) -{ + for (auto&& cell_idx : person->m_cells) { auto&& cell = loc->m_cells[cell_idx]; /* average infectivity over the time step * to second order accuracy using midpoint rule */ cell.m_cached_exposure_rate_contacts[{virus, age}] += inf.get_infectivity(t + dt / 2); - auto air_factor = loc->m_capacity_adapted_transmission_risk ? cell.compute_space_per_person_relative() : 1.0; + auto air_factor = + loc->m_capacity_adapted_transmission_risk ? cell.compute_space_per_person_relative() : 1.0; cell.m_cached_exposure_rate_air[{virus}] += inf.get_infectivity(t + dt / 2) * air_factor; } } @@ -205,12 +205,12 @@ void World::begin_step(TimePoint t, TimeSpan dt) } } -void World::end_step(TimePoint , TimeSpan ) +void World::end_step(TimePoint, TimeSpan) { // bool immediate = true; -// #ifdef MEMILIO_ENABLE_OPENMP -// immediate = omp_get_num_threads() == 1; -// #endif + // #ifdef MEMILIO_ENABLE_OPENMP + // immediate = omp_get_num_threads() == 1; + // #endif // if ((t + dt).time_since_midnight() < dt) { // PRAGMA_OMP(parallel for schedule(dynamic, 20)) //dynamic 20? From 5681e971f88836e36339723b5d5174cd9a8223df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 12 Sep 2023 13:19:48 +0200 Subject: [PATCH 056/105] increase vector size --- cpp/examples/parallelization_test.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cpp/examples/parallelization_test.cpp b/cpp/examples/parallelization_test.cpp index efc2fbfc49..8987770f5a 100644 --- a/cpp/examples/parallelization_test.cpp +++ b/cpp/examples/parallelization_test.cpp @@ -2,16 +2,17 @@ #include #include -int main() { - const int vectorSize = 1000000; // Größe der Vektoren anpassen +int main() +{ + const int vectorSize = 100000000; // Größe der Vektoren anpassen std::vector vectorA(vectorSize); std::vector vectorB(vectorSize); std::vector vectorC(vectorSize); std::vector vectorD(vectorSize); - // Zufällige Werte für die Vektoren generieren - #pragma omp parallel for +// Zufällige Werte für die Vektoren generieren +#pragma omp parallel for for (int i = 0; i < vectorSize; ++i) { vectorA[i] = static_cast(rand()) / RAND_MAX; vectorB[i] = static_cast(rand()) / RAND_MAX; @@ -19,8 +20,8 @@ int main() { vectorD[i] = 0; } - // Vektortriade parallel berechnen - #pragma omp parallel for +// Vektortriade parallel berechnen +#pragma omp parallel for for (int i = 0; i < vectorSize; ++i) { vectorD[i] = vectorA[i] + vectorB[i] - vectorC[i]; } From a4645bb62405ce3f83e33e4f9fee0644b18df3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 12 Sep 2023 13:32:28 +0200 Subject: [PATCH 057/105] decrease vector size --- cpp/examples/parallelization_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/parallelization_test.cpp b/cpp/examples/parallelization_test.cpp index 8987770f5a..132b8c0a10 100644 --- a/cpp/examples/parallelization_test.cpp +++ b/cpp/examples/parallelization_test.cpp @@ -4,7 +4,7 @@ int main() { - const int vectorSize = 100000000; // Größe der Vektoren anpassen + const int vectorSize = 1000000; // Größe der Vektoren anpassen std::vector vectorA(vectorSize); std::vector vectorB(vectorSize); From 0ef3dbdd7db4b1425280e12337b188f4e9aa26eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:09:49 +0200 Subject: [PATCH 058/105] decrease loop iterations in abm_minimal_example --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index b6e8e97ea2..9bf285da8f 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -51,7 +51,7 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - for (int iter = 0; iter < 1000000; iter++) { + for (int iter = 0; iter < 10000; iter++) { LIKWID_MARKER_INIT; From ce128807ba85a9d80a694c82f2cb6085d048aaf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 14 Sep 2023 08:45:26 +0200 Subject: [PATCH 059/105] decrease abm_minimal main iterations --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 9bf285da8f..c141196b7e 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -51,7 +51,7 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - for (int iter = 0; iter < 10000; iter++) { + for (int iter = 0; iter < 1; iter++) { LIKWID_MARKER_INIT; From 409ed721f92c439282f00ad5829a4ab0b26d62cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 14 Sep 2023 10:01:02 +0200 Subject: [PATCH 060/105] changed names of the marker api regions in abm_minimal --- cpp/examples/abm_minimal.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index c141196b7e..c48a954060 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -55,7 +55,7 @@ int main() LIKWID_MARKER_INIT; - LIKWID_MARKER_START("initialization"); + LIKWID_MARKER_START("region_1"); // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; @@ -165,13 +165,13 @@ int main() auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); auto sim = mio::abm::Simulation(t0, std::move(world)); - LIKWID_MARKER_STOP("initialization"); + LIKWID_MARKER_STOP("region_1"); - LIKWID_MARKER_START("simulation"); + LIKWID_MARKER_START("region_2"); sim.advance(tmax); - LIKWID_MARKER_STOP("simulation"); + LIKWID_MARKER_STOP("region_2"); LIKWID_MARKER_CLOSE; From 50f6add066b5755a5ef42f45ea2a84ee0e8ed695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:51:01 +0200 Subject: [PATCH 061/105] loop in marker api --- cpp/examples/abm_minimal.cpp | 209 ++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 103 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index c48a954060..417782dbdb 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -57,113 +57,116 @@ int main() LIKWID_MARKER_START("region_1"); - // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world - mio::abm::GlobalInfectionParameters infection_params; - - // Set same infection parameter for all age groups. For example, the incubation period is 4 days. - infection_params.get() = 4.; - - // Create the world with infection parameters. - auto world = mio::abm::World(infection_params); - - // There are 3 households for each household group. - int n_households = 3; - - // 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(); // A child is 50/50% 0-4 or 5-14. - child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); - child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); - - auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. - parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); - parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); - - // Two-person household with one parent and one child. - auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); - auto twoPersonHousehold_full = mio::abm::Household(); - twoPersonHousehold_full.add_members(child, 1); - twoPersonHousehold_full.add_members(parent, 1); - twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); - add_household_group_to_world(world, twoPersonHousehold_group); - - // Three-person household with two parent and one child. - auto threePersonHousehold_group = mio::abm::HouseholdGroup(); - auto threePersonHousehold_full = mio::abm::Household(); - threePersonHousehold_full.add_members(child, 1); - threePersonHousehold_full.add_members(parent, 2); - threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); - add_household_group_to_world(world, threePersonHousehold_group); - - // Add one social event with 5 maximum contacts. - // Maximum contacs limit the number of people that a person can infect while being at this location. - auto event = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(5); - // 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 one supermarket, maximum constacts are assumed to be 20. - auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); - // At every school, the maximum contacts are 20. - auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(20); - // At every workplace, maximum contacts are 10. - auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(10); - - // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. - auto testing_min_time = mio::abm::days(1); - auto probability = 0.5; - auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - auto test_type = mio::abm::AntigenTest(); - auto test_at_work = std::vector{mio::abm::LocationType::Work}; - auto testing_criteria_work = - std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; - 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); - - // Assign infection state to each person. - // The infection states are chosen randomly. - auto persons = world.get_persons(); - for (auto& person : persons) { - mio::abm::InfectionState infection_state = - (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); - if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - world.get_global_infection_parameters(), start_date, - infection_state)); - } - - // Assign locations to the people - for (auto& person : persons) { - //assign shop and event - person.set_assigned_location(event); - person.set_assigned_location(shop); - //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); - //assign work/school to people depending on their age - if (person.get_age() == mio::abm::AgeGroup::Age5to14) { - person.set_assigned_location(school); + for (int iter1 = 0; iter1 < 1000; iter1++) { + + // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world + mio::abm::GlobalInfectionParameters infection_params; + + // Set same infection parameter for all age groups. For example, the incubation period is 4 days. + infection_params.get() = 4.; + + // Create the world with infection parameters. + auto world = mio::abm::World(infection_params); + + // There are 3 households for each household group. + int n_households = 3; + + // 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(); // A child is 50/50% 0-4 or 5-14. + child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); + child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); + + auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. + parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); + parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); + + // Two-person household with one parent and one child. + auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); + auto twoPersonHousehold_full = mio::abm::Household(); + twoPersonHousehold_full.add_members(child, 1); + twoPersonHousehold_full.add_members(parent, 1); + twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); + add_household_group_to_world(world, twoPersonHousehold_group); + + // Three-person household with two parent and one child. + auto threePersonHousehold_group = mio::abm::HouseholdGroup(); + auto threePersonHousehold_full = mio::abm::Household(); + threePersonHousehold_full.add_members(child, 1); + threePersonHousehold_full.add_members(parent, 2); + threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); + add_household_group_to_world(world, threePersonHousehold_group); + + // Add one social event with 5 maximum contacts. + // Maximum contacs limit the number of people that a person can infect while being at this location. + auto event = world.add_location(mio::abm::LocationType::SocialEvent); + world.get_individualized_location(event).get_infection_parameters().set(5); + // 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 one supermarket, maximum constacts are assumed to be 20. + auto shop = world.add_location(mio::abm::LocationType::BasicsShop); + world.get_individualized_location(shop).get_infection_parameters().set(20); + // At every school, the maximum contacts are 20. + auto school = world.add_location(mio::abm::LocationType::School); + world.get_individualized_location(school).get_infection_parameters().set(20); + // At every workplace, maximum contacts are 10. + auto work = world.add_location(mio::abm::LocationType::Work); + world.get_individualized_location(work).get_infection_parameters().set(10); + + // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. + auto testing_min_time = mio::abm::days(1); + auto probability = 0.5; + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); + auto test_type = mio::abm::AntigenTest(); + auto test_at_work = std::vector{mio::abm::LocationType::Work}; + auto testing_criteria_work = + std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; + 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); + + // Assign infection state to each person. + // The infection states are chosen randomly. + auto persons = world.get_persons(); + for (auto& person : persons) { + mio::abm::InfectionState infection_state = + (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) + person.add_new_infection( + mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + world.get_global_infection_parameters(), start_date, infection_state)); } - if (person.get_age() == mio::abm::AgeGroup::Age15to34 || - person.get_age() == mio::abm::AgeGroup::Age35to59) { - person.set_assigned_location(work); + + // Assign locations to the people + for (auto& person : persons) { + //assign shop and event + person.set_assigned_location(event); + person.set_assigned_location(shop); + //assign hospital and ICU + person.set_assigned_location(hospital); + person.set_assigned_location(icu); + //assign work/school to people depending on their age + if (person.get_age() == mio::abm::AgeGroup::Age5to14) { + person.set_assigned_location(school); + } + if (person.get_age() == mio::abm::AgeGroup::Age15to34 || + person.get_age() == mio::abm::AgeGroup::Age35to59) { + person.set_assigned_location(work); + } } - } - // During the lockdown, social events are closed for 90% of people. - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); - mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); + // During the lockdown, social events are closed for 90% of people. + auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); + mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); - auto t0 = mio::abm::TimePoint(0); - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); - auto sim = mio::abm::Simulation(t0, std::move(world)); + auto t0 = mio::abm::TimePoint(0); + auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); + auto sim = mio::abm::Simulation(t0, std::move(world)); + } LIKWID_MARKER_STOP("region_1"); From 7c1fdbdf48ad07f1e2d0e2cb6bfaaa287a236af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:54:52 +0200 Subject: [PATCH 062/105] reset loop --- cpp/examples/abm_minimal.cpp | 209 +++++++++++++++++------------------ 1 file changed, 103 insertions(+), 106 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 417782dbdb..c48a954060 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -57,116 +57,113 @@ int main() LIKWID_MARKER_START("region_1"); - for (int iter1 = 0; iter1 < 1000; iter1++) { - - // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world - mio::abm::GlobalInfectionParameters infection_params; - - // Set same infection parameter for all age groups. For example, the incubation period is 4 days. - infection_params.get() = 4.; - - // Create the world with infection parameters. - auto world = mio::abm::World(infection_params); - - // There are 3 households for each household group. - int n_households = 3; - - // 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(); // A child is 50/50% 0-4 or 5-14. - child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); - child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); - - auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. - parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); - parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); - - // Two-person household with one parent and one child. - auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); - auto twoPersonHousehold_full = mio::abm::Household(); - twoPersonHousehold_full.add_members(child, 1); - twoPersonHousehold_full.add_members(parent, 1); - twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); - add_household_group_to_world(world, twoPersonHousehold_group); - - // Three-person household with two parent and one child. - auto threePersonHousehold_group = mio::abm::HouseholdGroup(); - auto threePersonHousehold_full = mio::abm::Household(); - threePersonHousehold_full.add_members(child, 1); - threePersonHousehold_full.add_members(parent, 2); - threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); - add_household_group_to_world(world, threePersonHousehold_group); - - // Add one social event with 5 maximum contacts. - // Maximum contacs limit the number of people that a person can infect while being at this location. - auto event = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(5); - // 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 one supermarket, maximum constacts are assumed to be 20. - auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); - // At every school, the maximum contacts are 20. - auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(20); - // At every workplace, maximum contacts are 10. - auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(10); - - // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. - auto testing_min_time = mio::abm::days(1); - auto probability = 0.5; - auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - auto test_type = mio::abm::AntigenTest(); - auto test_at_work = std::vector{mio::abm::LocationType::Work}; - auto testing_criteria_work = - std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; - 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); - - // Assign infection state to each person. - // The infection states are chosen randomly. - auto persons = world.get_persons(); - for (auto& person : persons) { - mio::abm::InfectionState infection_state = - (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); - if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection( - mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - world.get_global_infection_parameters(), start_date, infection_state)); - } + // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world + mio::abm::GlobalInfectionParameters infection_params; + + // Set same infection parameter for all age groups. For example, the incubation period is 4 days. + infection_params.get() = 4.; + + // Create the world with infection parameters. + auto world = mio::abm::World(infection_params); + + // There are 3 households for each household group. + int n_households = 3; + + // 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(); // A child is 50/50% 0-4 or 5-14. + child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); + child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); + + auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. + parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); + parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); + + // Two-person household with one parent and one child. + auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); + auto twoPersonHousehold_full = mio::abm::Household(); + twoPersonHousehold_full.add_members(child, 1); + twoPersonHousehold_full.add_members(parent, 1); + twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); + add_household_group_to_world(world, twoPersonHousehold_group); + + // Three-person household with two parent and one child. + auto threePersonHousehold_group = mio::abm::HouseholdGroup(); + auto threePersonHousehold_full = mio::abm::Household(); + threePersonHousehold_full.add_members(child, 1); + threePersonHousehold_full.add_members(parent, 2); + threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); + add_household_group_to_world(world, threePersonHousehold_group); + + // Add one social event with 5 maximum contacts. + // Maximum contacs limit the number of people that a person can infect while being at this location. + auto event = world.add_location(mio::abm::LocationType::SocialEvent); + world.get_individualized_location(event).get_infection_parameters().set(5); + // 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 one supermarket, maximum constacts are assumed to be 20. + auto shop = world.add_location(mio::abm::LocationType::BasicsShop); + world.get_individualized_location(shop).get_infection_parameters().set(20); + // At every school, the maximum contacts are 20. + auto school = world.add_location(mio::abm::LocationType::School); + world.get_individualized_location(school).get_infection_parameters().set(20); + // At every workplace, maximum contacts are 10. + auto work = world.add_location(mio::abm::LocationType::Work); + world.get_individualized_location(work).get_infection_parameters().set(10); + + // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. + auto testing_min_time = mio::abm::days(1); + auto probability = 0.5; + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); + auto test_type = mio::abm::AntigenTest(); + auto test_at_work = std::vector{mio::abm::LocationType::Work}; + auto testing_criteria_work = + std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; + 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); + + // Assign infection state to each person. + // The infection states are chosen randomly. + auto persons = world.get_persons(); + for (auto& person : persons) { + mio::abm::InfectionState infection_state = + (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + world.get_global_infection_parameters(), start_date, + infection_state)); + } - // Assign locations to the people - for (auto& person : persons) { - //assign shop and event - person.set_assigned_location(event); - person.set_assigned_location(shop); - //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); - //assign work/school to people depending on their age - if (person.get_age() == mio::abm::AgeGroup::Age5to14) { - person.set_assigned_location(school); - } - if (person.get_age() == mio::abm::AgeGroup::Age15to34 || - person.get_age() == mio::abm::AgeGroup::Age35to59) { - person.set_assigned_location(work); - } + // Assign locations to the people + for (auto& person : persons) { + //assign shop and event + person.set_assigned_location(event); + person.set_assigned_location(shop); + //assign hospital and ICU + person.set_assigned_location(hospital); + person.set_assigned_location(icu); + //assign work/school to people depending on their age + if (person.get_age() == mio::abm::AgeGroup::Age5to14) { + person.set_assigned_location(school); + } + if (person.get_age() == mio::abm::AgeGroup::Age15to34 || + person.get_age() == mio::abm::AgeGroup::Age35to59) { + person.set_assigned_location(work); } + } - // During the lockdown, social events are closed for 90% of people. - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); - mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); + // During the lockdown, social events are closed for 90% of people. + auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); + mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); - auto t0 = mio::abm::TimePoint(0); - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); - auto sim = mio::abm::Simulation(t0, std::move(world)); - } + auto t0 = mio::abm::TimePoint(0); + auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); + auto sim = mio::abm::Simulation(t0, std::move(world)); LIKWID_MARKER_STOP("region_1"); From 9042ecfc33d413620ba94e63dbe17febd0ece835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:59:04 +0200 Subject: [PATCH 063/105] increase outer loop interations --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index c48a954060..4ed0df785e 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -51,7 +51,7 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - for (int iter = 0; iter < 1; iter++) { + for (int iter = 0; iter < 1000; iter++) { LIKWID_MARKER_INIT; From ab7e169c368308c7f0ded5dce20e24e54ef7dd63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:03:36 +0200 Subject: [PATCH 064/105] added marker api --- cpp/examples/abm_minimal.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 4ed0df785e..f2e013accb 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -51,11 +51,13 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - for (int iter = 0; iter < 1000; iter++) { + for (int iter = 0; iter < 100000; iter++) { LIKWID_MARKER_INIT; - LIKWID_MARKER_START("region_1"); + LIKWID_MARKER_START("all"); + + LIKWID_MARKER_START("initalization"); // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; @@ -165,13 +167,15 @@ int main() auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); auto sim = mio::abm::Simulation(t0, std::move(world)); - LIKWID_MARKER_STOP("region_1"); + LIKWID_MARKER_STOP("initalization"); - LIKWID_MARKER_START("region_2"); + LIKWID_MARKER_START("simulation"); sim.advance(tmax); - LIKWID_MARKER_STOP("region_2"); + LIKWID_MARKER_STOP("simulation"); + + LIKWID-MARKER_STOP("all"); LIKWID_MARKER_CLOSE; From 6434f8ef954d61337c51f1f253b1dd19c566f2eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:05:48 +0200 Subject: [PATCH 065/105] fix bugs in marker api --- cpp/examples/abm_minimal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index f2e013accb..ebc5621678 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -175,7 +175,7 @@ int main() LIKWID_MARKER_STOP("simulation"); - LIKWID-MARKER_STOP("all"); + LIKWID_MARKER_STOP("all"); LIKWID_MARKER_CLOSE; From 11a62856c4cde37d02d09cd37626c518dd7bd798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Fri, 15 Sep 2023 15:24:43 +0200 Subject: [PATCH 066/105] add another marker api for testing --- cpp/examples/abm_minimal.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index ebc5621678..55732c229a 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -96,6 +96,8 @@ int main() threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); add_household_group_to_world(world, threePersonHousehold_group); + LIKWID_MARKER_START("social_events"); + // Add one social event with 5 maximum contacts. // Maximum contacs limit the number of people that a person can infect while being at this location. auto event = world.add_location(mio::abm::LocationType::SocialEvent); @@ -115,6 +117,8 @@ int main() auto work = world.add_location(mio::abm::LocationType::Work); world.get_individualized_location(work).get_infection_parameters().set(10); + LIKWID_MARKER_STOP("social_events"); + // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. auto testing_min_time = mio::abm::days(1); auto probability = 0.5; From dd77af38dff1165109e116298338adcb813be039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Fri, 15 Sep 2023 15:36:37 +0200 Subject: [PATCH 067/105] remove two marker api --- cpp/examples/abm_minimal.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 55732c229a..f2db249219 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -57,8 +57,6 @@ int main() LIKWID_MARKER_START("all"); - LIKWID_MARKER_START("initalization"); - // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; @@ -95,8 +93,6 @@ int main() threePersonHousehold_full.add_members(parent, 2); threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); add_household_group_to_world(world, threePersonHousehold_group); - - LIKWID_MARKER_START("social_events"); // Add one social event with 5 maximum contacts. // Maximum contacs limit the number of people that a person can infect while being at this location. @@ -117,8 +113,6 @@ int main() auto work = world.add_location(mio::abm::LocationType::Work); world.get_individualized_location(work).get_infection_parameters().set(10); - LIKWID_MARKER_STOP("social_events"); - // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. auto testing_min_time = mio::abm::days(1); auto probability = 0.5; @@ -171,8 +165,6 @@ int main() auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); auto sim = mio::abm::Simulation(t0, std::move(world)); - LIKWID_MARKER_STOP("initalization"); - LIKWID_MARKER_START("simulation"); sim.advance(tmax); From 4249cb2936c3d6262c65eafc938fbf6f46b389e3 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:08:21 +0200 Subject: [PATCH 068/105] add marker regions to seir example --- cpp/examples/ode_seir.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 47e5b24333..490c085b4f 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -22,9 +22,15 @@ #include "ode_seir/parameters.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" +#include + int main() { + +LIKWID_MARKER_INIT; + +LIKWID_MARKER_START("setup"); //perform multiple iterations for more accurate likwid measurements for (int iter = 0; iter < 10000; iter++) { @@ -57,8 +63,16 @@ for (int iter = 0; iter < 10000; iter++) { model.check_constraints(); // print_seir_params(model); + LIKWID_MARKER_STOP("setup") + + LIKWID_MARKER_START("simulation") + auto seir = simulate(t0, tmax, dt, model); + LIKWID_MARKER_STOP("simulation") + + LIKWID_MARKER_CLOSE + //test2 printf("\n number total: %f\n", From d7d6ea5645fe1c61c957f7977bcdb90845652500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:10:40 +0200 Subject: [PATCH 069/105] add initialization marker api in abm_minimal --- cpp/examples/abm_minimal.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index f2db249219..bf21195fa0 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -57,6 +57,8 @@ int main() LIKWID_MARKER_START("all"); + LIKWID_MARKER_START("initialization"); + // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; @@ -161,6 +163,8 @@ int main() auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); + LIKWID_MARKER_STOP("initialization"); + auto t0 = mio::abm::TimePoint(0); auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); auto sim = mio::abm::Simulation(t0, std::move(world)); From 0456905d214cfee8742479a66c3051d7783f7520 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:34:02 +0200 Subject: [PATCH 070/105] add marker regions for secir and secirvvs --- cpp/examples/ode_secir.cpp | 15 +++++++++++++++ cpp/examples/ode_secirvvs.cpp | 19 +++++++++++++++++++ cpp/examples/ode_seir.cpp | 8 ++++---- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index d23e9af2e2..39858c20e9 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -20,9 +20,15 @@ #include "ode_secir/model.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" +#include + int main() { + LIKWID_MARKER_INIT; + + LIKWID_MARKER_START("setup"); + for(int iter = 0; iter < 10000; iter++) { mio::set_log_level(mio::LogLevel::debug); @@ -83,8 +89,17 @@ int main() integrator->set_dt_max(1.0); integrator->set_rel_tolerance(1e-4); integrator->set_abs_tolerance(1e-1); + + LIKWID_MARKER_STOP("setup"); + + LIKWID_MARKER_START("simulation"); + mio::TimeSeries secir = simulate(t0, tmax, dt, model, integrator); + LIKWID_MARKER_STOP("simulation"); + + LIKWID_MARKER_CLOSE; + bool print_to_terminal = true; if (print_to_terminal) { diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index d426c1d768..3ad417d42f 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -20,9 +20,17 @@ #include "ode_secirvvs/model.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" +#include + int main() { + LIKWID_MARKER_INIT; + + LIKWID_MARKER_START("setup"); + + for(int iter = 0; iter < 10000; iter++) { + mio::set_log_level(mio::LogLevel::debug); double t0 = 0; @@ -117,8 +125,17 @@ int main() // mio::TimeSeries secir = simulate(t0, tmax, dt, model, integrator); // use default Cash-Karp adaptive integrator + + LIKWID_MARKER_STOP("setup"); + + LIKWID_MARKER_START("simulation"); + mio::TimeSeries result = simulate(t0, tmax, dt, model); + LIKWID_MARKER_STOP("simulation"); + + LIKWID_MARKER_CLOSE; + bool print_to_terminal = true; if (print_to_terminal) { @@ -127,4 +144,6 @@ int main() printf("compartment %d: %.14f\n", (int)j, result.get_last_value()[j]); } } + + } } diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 490c085b4f..94942e6ea4 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -63,15 +63,15 @@ for (int iter = 0; iter < 10000; iter++) { model.check_constraints(); // print_seir_params(model); - LIKWID_MARKER_STOP("setup") + LIKWID_MARKER_STOP("setup"); - LIKWID_MARKER_START("simulation") + LIKWID_MARKER_START("simulation"); auto seir = simulate(t0, tmax, dt, model); - LIKWID_MARKER_STOP("simulation") + LIKWID_MARKER_STOP("simulation"); - LIKWID_MARKER_CLOSE + LIKWID_MARKER_CLOSE; //test2 From 9dea76318e8341dd5e6fafa556f13a1acc858406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:52:06 +0200 Subject: [PATCH 071/105] add another marker api --- cpp/examples/abm_minimal.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index bf21195fa0..69a351e24c 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -95,7 +95,7 @@ int main() threePersonHousehold_full.add_members(parent, 2); threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); add_household_group_to_world(world, threePersonHousehold_group); - + // Add one social event with 5 maximum contacts. // Maximum contacs limit the number of people that a person can infect while being at this location. auto event = world.add_location(mio::abm::LocationType::SocialEvent); @@ -177,8 +177,12 @@ int main() LIKWID_MARKER_STOP("all"); - LIKWID_MARKER_CLOSE; + LIKWID_MARKER_START("write_results_to_files"); write_results_to_file(sim); + + LIKWID_MARKER_START("write_results_to_files"); + + LIKWID_MARKER_CLOSE; } } From cf2f702a9110dddbf7940678a63350ad21024cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:54:58 +0200 Subject: [PATCH 072/105] fix bugs in marker api --- cpp/examples/abm_minimal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 69a351e24c..1770e95620 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -175,13 +175,13 @@ int main() LIKWID_MARKER_STOP("simulation"); - LIKWID_MARKER_STOP("all"); - LIKWID_MARKER_START("write_results_to_files"); write_results_to_file(sim); - LIKWID_MARKER_START("write_results_to_files"); + LIKWID_MARKER_STOP("write_results_to_files"); + + LIKWID_MARKER_STOP("all"); LIKWID_MARKER_CLOSE; } From 81240337e31fabe4fe39f89980443cd60067fbd0 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Mon, 18 Sep 2023 11:05:39 +0200 Subject: [PATCH 073/105] adjust cmakelist to support marker api for ode ex. --- cpp/examples/CMakeLists.txt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 495512fb54..0bc47e784b 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -15,16 +15,21 @@ target_link_libraries(adapt_rk_example PRIVATE memilio) target_compile_options(adapt_rk_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ode_seir_example ode_seir.cpp) -target_link_libraries(ode_seir_example PRIVATE memilio ode_seir) -target_compile_options(ode_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_link_libraries(ode_seir_example PRIVATE memilio ode_seir likwid) +target_compile_options(ode_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") add_executable(ode_secir_example ode_secir.cpp) -target_link_libraries(ode_secir_example PRIVATE memilio ode_secir) -target_compile_options(ode_secir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_link_libraries(ode_secir_example PRIVATE memilio ode_secir likwid) +target_compile_options(ode_secir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") + add_executable(ode_secirvvs_example ode_secirvvs.cpp) -target_link_libraries(ode_secirvvs_example PRIVATE memilio ode_secirvvs) -target_compile_options(ode_secirvvs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_link_libraries(ode_secirvvs_example PRIVATE memilio ode_secirvvs likwid) +target_compile_options(ode_secirvvs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") + add_executable(ode_secir_ageres_example ode_secir_ageres.cpp) target_link_libraries(ode_secir_ageres_example PRIVATE memilio ode_secir) From 9819ce800e7eec8a686425c7c9ccb5693e9ae07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:20:28 +0200 Subject: [PATCH 074/105] add likwid marker api to abm simulation --- cpp/simulations/CMakeLists.txt | 5 +++-- cpp/simulations/abm.cpp | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index 0110fee9c4..c0ff7a7a86 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -8,6 +8,7 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(abm_simulation abm.cpp) - target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES}) - target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} likwid) + target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID PERFMON;-llikwid") endif() diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 187cd25e08..dd80f5ece7 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -23,6 +23,7 @@ #include "memilio/utils/random_number_generator.h" #include "memilio/utils/uncertain_value.h" #include "boost/filesystem.hpp" +#include namespace fs = boost::filesystem; @@ -833,6 +834,7 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num // Loop over a number of runs while (run_idx <= num_runs) { + LIKWID_MARKER_START("initialization"); // Create the sampled simulation with start time t0. auto sim = create_sampled_simulation(t0); // Collect the id of location in world. @@ -840,8 +842,13 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num for (auto& location : sim.get_world().get_locations()) { loc_ids.push_back(location.get_index()); } + LIKWID_MARKER_STOP("initialization"); + + LIKWID_MARKER_START("simulation"); // Advance the world to tmax sim.advance(tmax); + LIKWID_MARKER_STOP("simulation"); + // TODO: update result of the simulation to be a vector of location result. auto temp_sim_result = std::vector>{sim.get_result()}; // Push result of the simulation back to the result vector @@ -859,6 +866,8 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num int main(int argc, char** argv) { + LIKWID_MARKER_INIT; + mio::set_log_level(mio::LogLevel::warn); @@ -895,4 +904,6 @@ int main(int argc, char** argv) return -1; } return 0; + + LIKWID_MARKER_CLOSE; } From 1e782c8d645735b3a941ddc9fb5c5f3e01440236 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:25:55 +0200 Subject: [PATCH 075/105] remove console outputs in examples for likwid --- cpp/examples/ode_secir.cpp | 4 ++-- cpp/examples/ode_secirvvs.cpp | 4 ++-- cpp/examples/ode_seir.cpp | 8 +++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index 39858c20e9..8e336faa3d 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -37,7 +37,7 @@ int main() double tmax = 50; double dt = 0.1; - mio::log_info("Simulating SECIR; t={} ... {} with dt = {}.", t0, tmax, dt); + //mio::log_info("Simulating SECIR; t={} ... {} with dt = {}.", t0, tmax, dt); double cont_freq = 10; // see Polymod study @@ -100,7 +100,7 @@ int main() LIKWID_MARKER_CLOSE; - bool print_to_terminal = true; + bool print_to_terminal = false; if (print_to_terminal) { std::vector vars = {"S", "E", "C", "C_confirmed", "I", "I_confirmed", "H", "U", "R", "D"}; diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index 3ad417d42f..d83246b4f7 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -37,7 +37,7 @@ int main() double tmax = 30; double dt = 0.1; - mio::log_info("Simulating SECIRVVS; t={} ... {} with dt = {}.", t0, tmax, dt); + //mio::log_info("Simulating SECIRVVS; t={} ... {} with dt = {}.", t0, tmax, dt); mio::osecirvvs::Model model(1); @@ -136,7 +136,7 @@ int main() LIKWID_MARKER_CLOSE; - bool print_to_terminal = true; + bool print_to_terminal = false; if (print_to_terminal) { printf("\n%.14f ", result.get_last_time()); diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 94942e6ea4..5f179dbf10 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -40,7 +40,7 @@ for (int iter = 0; iter < 10000; iter++) { double tmax = 1; double dt = 0.001; - mio::log_info("Simulating SEIR; t={} ... {} with dt = {}.", t0, tmax, dt); + //mio::log_info("Simulating SEIR; t={} ... {} with dt = {}.", t0, tmax, dt); mio::oseir::Model model; @@ -73,10 +73,8 @@ for (int iter = 0; iter < 10000; iter++) { LIKWID_MARKER_CLOSE; - //test2 - - printf("\n number total: %f\n", - seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); + //printf("\n number total: %f\n", + // seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); } } From c666f4179401167d8001c101cb52797458bf5b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:31:30 +0200 Subject: [PATCH 076/105] change position of likwid marker close --- cpp/simulations/abm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index dd80f5ece7..e81889ecaf 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -903,7 +903,8 @@ int main(int argc, char** argv) printf("%s\n", result.error().formatted_message().c_str()); return -1; } + LIKWID_MARKER_CLOSE; return 0; - LIKWID_MARKER_CLOSE; + } From 6d134e3bfc835f9a1d46bec4307afe340b13a451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:49:25 +0200 Subject: [PATCH 077/105] change simulation cmake list --- cpp/examples/abm_minimal.cpp | 2 +- cpp/simulations/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 1770e95620..66e6a6436b 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -46,7 +46,7 @@ void write_results_to_file(const mio::abm::Simulation& sim) myfile << "\n"; } } - std::cout << "Results written to abm_minimal.txt" << std::endl; + //std::cout << "Results written to abm_minimal.txt" << std::endl; } int main() diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index c0ff7a7a86..4dcf7f4d42 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -8,7 +8,7 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(abm_simulation abm.cpp) - target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} likwid) + target_link_libraries(abm_simulation PRIVATE memilio abm likwid Boost::filesystem ${HDF5_C_LIBRARIES}) target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} PRIVATE "-lstdc++;-DLIKWID PERFMON;-llikwid") endif() From af3a7d3679ef6f49d0933925c1e070319cb73e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 19 Sep 2023 11:25:12 +0200 Subject: [PATCH 078/105] change position of likwid marker close again --- cpp/simulations/abm.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index e81889ecaf..73814c5897 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -834,6 +834,8 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num // Loop over a number of runs while (run_idx <= num_runs) { + LIKWID_MARKER_INIT; + LIKWID_MARKER_START("initialization"); // Create the sampled simulation with start time t0. auto sim = create_sampled_simulation(t0); @@ -849,6 +851,8 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num sim.advance(tmax); LIKWID_MARKER_STOP("simulation"); + LIKWID_MARKER_CLOSE; + // TODO: update result of the simulation to be a vector of location result. auto temp_sim_result = std::vector>{sim.get_result()}; // Push result of the simulation back to the result vector @@ -866,9 +870,6 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num int main(int argc, char** argv) { - LIKWID_MARKER_INIT; - - mio::set_log_level(mio::LogLevel::warn); std::string result_dir = "."; @@ -903,7 +904,6 @@ int main(int argc, char** argv) printf("%s\n", result.error().formatted_message().c_str()); return -1; } - LIKWID_MARKER_CLOSE; return 0; From e32de01f1433ad30a2b1571df6a25c6a85c75d49 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:34:05 +0200 Subject: [PATCH 079/105] remove loops to increase flops --- cpp/examples/abm_minimal.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 66e6a6436b..b1f76d1fc7 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -51,7 +51,6 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - for (int iter = 0; iter < 100000; iter++) { LIKWID_MARKER_INIT; @@ -184,5 +183,5 @@ int main() LIKWID_MARKER_STOP("all"); LIKWID_MARKER_CLOSE; - } + } From d281b5a872baf2e3d41a21af0ab5d2fe7ac5e082 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:38:18 +0200 Subject: [PATCH 080/105] remove main loop in seir example --- cpp/examples/ode_seir.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 5f179dbf10..0d69ee7467 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -32,7 +32,6 @@ LIKWID_MARKER_INIT; LIKWID_MARKER_START("setup"); //perform multiple iterations for more accurate likwid measurements -for (int iter = 0; iter < 10000; iter++) { mio::set_log_level(mio::LogLevel::debug); @@ -76,5 +75,5 @@ for (int iter = 0; iter < 10000; iter++) { //printf("\n number total: %f\n", // seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); - } + } From f4e161cc698fe165f8677066eb53996d56ab2134 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:57:20 +0200 Subject: [PATCH 081/105] add likwid region to odesecir simulation --- .../2020_npis_sarscov2_wildtype_germany.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp b/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp index c794067004..b90591399b 100644 --- a/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp +++ b/cpp/simulations/2020_npis_sarscov2_wildtype_germany.cpp @@ -32,6 +32,8 @@ #include "boost/filesystem.hpp" #include #include +#include + namespace fs = boost::filesystem; @@ -606,6 +608,10 @@ int main(int argc, char** argv) //- log level //- ... + LIKWID_MARKER_INIT; + + LIKWID_MARKER_START("main"); + mio::set_log_level(mio::LogLevel::warn); mio::mpi::init(); @@ -670,5 +676,10 @@ int main(int argc, char** argv) return -1; } mio::mpi::finalize(); + + LIKWID_MARKER_STOP("main"); + + LIKWID_MARKER_CLOSE; + return 0; } From d74a930c3fa432afebc217dfe3f3ae42c08dd3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 19 Sep 2023 14:26:36 +0200 Subject: [PATCH 082/105] modify simulation cmakelist with likwid libary --- cpp/simulations/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index 4dcf7f4d42..0a3d5ea2a2 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -8,7 +8,8 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(abm_simulation abm.cpp) - target_link_libraries(abm_simulation PRIVATE memilio abm likwid Boost::filesystem ${HDF5_C_LIBRARIES}) + target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} + PRIVATE memilio abm likwid) target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} PRIVATE "-lstdc++;-DLIKWID PERFMON;-llikwid") endif() From 895f8ec5a2e8f70143b304b28456f9be94cb2f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 19 Sep 2023 14:41:11 +0200 Subject: [PATCH 083/105] bug fixes in simulation camkelist --- cpp/simulations/CMakeLists.txt | 2 +- cpp/simulations/abm.cpp | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index 0a3d5ea2a2..474166826a 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -11,5 +11,5 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} PRIVATE memilio abm likwid) target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID PERFMON;-llikwid") + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") endif() diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 73814c5897..911d59355d 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -444,8 +444,8 @@ void assign_infection_state(mio::abm::World& world, mio::abm::TimePoint t, doubl auto persons = world.get_persons(); for (auto& person : persons) { auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); - auto infection_state = - determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, infected_symptoms_prob, recovered_prob); + auto infection_state = determine_infection_state(rng, exposed_prob, infected_no_symptoms_prob, + infected_symptoms_prob, recovered_prob); if (infection_state != mio::abm::InfectionState::Susceptible) person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), world.get_global_infection_parameters(), t, infection_state)); @@ -775,7 +775,7 @@ mio::abm::Simulation create_sampled_simulation(const mio::abm::TimePoint& t0) printf("%u, ", s); } printf("\n"); - + // Assumed percentage of infection state at the beginning of the simulation. ScalarType exposed_prob = 0.005, infected_no_symptoms_prob = 0.001, infected_symptoms_prob = 0.001, recovered_prob = 0.0; @@ -831,11 +831,11 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num auto run_idx = size_t(1); // The run index auto save_result_result = mio::IOResult(mio::success()); // Variable informing over successful IO operations + LIKWID_MARKER_INIT; + // Loop over a number of runs while (run_idx <= num_runs) { - LIKWID_MARKER_INIT; - LIKWID_MARKER_START("initialization"); // Create the sampled simulation with start time t0. auto sim = create_sampled_simulation(t0); @@ -851,8 +851,6 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num sim.advance(tmax); LIKWID_MARKER_STOP("simulation"); - LIKWID_MARKER_CLOSE; - // TODO: update result of the simulation to be a vector of location result. auto temp_sim_result = std::vector>{sim.get_result()}; // Push result of the simulation back to the result vector @@ -864,6 +862,8 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num } ++run_idx; } + LIKWID_MARKER_CLOSE; + BOOST_OUTCOME_TRY(save_result_result); return mio::success(); } @@ -905,6 +905,4 @@ int main(int argc, char** argv) return -1; } return 0; - - } From e7b270e75be98a279be45b625dcc69944137e168 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Tue, 19 Sep 2023 14:59:37 +0200 Subject: [PATCH 084/105] enable marker api for ode simualtions in cmakelist --- cpp/simulations/CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index 474166826a..a4fc5a4433 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -1,11 +1,15 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) add_executable(2020_npis_wildtype 2020_npis_sarscov2_wildtype_germany.cpp) - target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES}) - target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES} + PRIVATE memilio ode_secir likwid) + target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") add_executable(2021_vaccination_delta 2021_vaccination_sarscov2_delta_germany.cpp) - target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES}) - target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES} + PRIVATE memilio ode_secirvvs likwid) + target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") add_executable(abm_simulation abm.cpp) target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} From d6a40655abd88040af03eec4c1e3205d002b2b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Tue, 19 Sep 2023 15:49:55 +0200 Subject: [PATCH 085/105] enable OPENMP --- cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 2da730d298..b571689a35 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -16,7 +16,7 @@ option(MEMILIO_SANITIZE_UNDEFINED "Enable undefined behavior sanitizer." OFF) option(MEMILIO_BUILD_SHARED_LIBS "Build memilio as a shared library." ON) option(MEMILIO_BUILD_STATIC_LIBS "Build memilio as a static library." ON) option(MEMILIO_ENABLE_MPI "Build memilio with MPI." OFF) -option(MEMILIO_ENABLE_OPENMP "Enable Multithreading with OpenMP." OFF) +option(MEMILIO_ENABLE_OPENMP "Enable Multithreading with OpenMP." ON) mark_as_advanced(MEMILIO_USE_BUNDLED_SPDLOG MEMILIO_SANITIZE_ADDRESS MEMILIO_SANITIZE_UNDEFINED) From e291d0d4056221cf8a7287ca237ac9582792b999 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:35:21 +0200 Subject: [PATCH 086/105] add loops for normal likwid measurements on exmp --- cpp/examples/ode_secir.cpp | 3 +++ cpp/examples/ode_seir.cpp | 14 ++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index 8e336faa3d..221e53b92a 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -25,6 +25,8 @@ int main() { + + for(int iter = 0; iter < 10000; iter++) { LIKWID_MARKER_INIT; LIKWID_MARKER_START("setup"); @@ -122,5 +124,6 @@ int main() printf("number total: %f", res_j[0] + res_j[1] + res_j[2] + res_j[3] + res_j[4] + res_j[5] + res_j[6] + res_j[7]); } + } } } diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 0d69ee7467..68a81b8308 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -22,15 +22,13 @@ #include "ode_seir/parameters.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" -#include int main() { -LIKWID_MARKER_INIT; +for(int iter = 0; iter < 10000; iter++) { -LIKWID_MARKER_START("setup"); //perform multiple iterations for more accurate likwid measurements mio::set_log_level(mio::LogLevel::debug); @@ -62,18 +60,14 @@ LIKWID_MARKER_START("setup"); model.check_constraints(); // print_seir_params(model); - LIKWID_MARKER_STOP("setup"); - - LIKWID_MARKER_START("simulation"); + auto seir = simulate(t0, tmax, dt, model); - LIKWID_MARKER_STOP("simulation"); - - LIKWID_MARKER_CLOSE; + //printf("\n number total: %f\n", // seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); - + } } From 8bc1cf1da2190851d9ee10e05034f945dddd3195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:36:07 +0200 Subject: [PATCH 087/105] modify abm_minimal likwid marker api --- cpp/examples/abm_minimal.cpp | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index b1f76d1fc7..98a0839acb 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -52,11 +52,11 @@ void write_results_to_file(const mio::abm::Simulation& sim) int main() { - LIKWID_MARKER_INIT; + LIKWID_MARKER_INIT; - LIKWID_MARKER_START("all"); + LIKWID_MARKER_START("main"); - LIKWID_MARKER_START("initialization"); + for (int iter = 0; iter < 100000; iter++) { // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; @@ -162,26 +162,14 @@ int main() auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); - LIKWID_MARKER_STOP("initialization"); - auto t0 = mio::abm::TimePoint(0); auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); auto sim = mio::abm::Simulation(t0, std::move(world)); - LIKWID_MARKER_START("simulation"); - sim.advance(tmax); + } - LIKWID_MARKER_STOP("simulation"); - - LIKWID_MARKER_START("write_results_to_files"); - - write_results_to_file(sim); - - LIKWID_MARKER_STOP("write_results_to_files"); - - LIKWID_MARKER_STOP("all"); + LIKWID_MARKER_STOP("main"); - LIKWID_MARKER_CLOSE; - + LIKWID_MARKER_CLOSE; } From a5232294ba83969f28d62f50d149a7ce5ccf4a00 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:37:55 +0200 Subject: [PATCH 088/105] add loop for secir example --- cpp/examples/ode_secir.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index 221e53b92a..4f33ba1ad9 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -26,7 +26,6 @@ int main() { - for(int iter = 0; iter < 10000; iter++) { LIKWID_MARKER_INIT; LIKWID_MARKER_START("setup"); @@ -124,6 +123,6 @@ int main() printf("number total: %f", res_j[0] + res_j[1] + res_j[2] + res_j[3] + res_j[4] + res_j[5] + res_j[6] + res_j[7]); } - } + } } From 59d45a663b0af07b362b73b9e5c8d70df234550f Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:55:30 +0200 Subject: [PATCH 089/105] switch to euler integrator for measurements --- cpp/examples/ode_seir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 68a81b8308..8de9adf9a8 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -60,9 +60,10 @@ for(int iter = 0; iter < 10000; iter++) { model.check_constraints(); // print_seir_params(model); - + auto integrator = std::make_shared(); + - auto seir = simulate(t0, tmax, dt, model); + auto seir = simulate(t0, tmax, dt, integrator); From d0739c8d3be9d8f46c9c53ef238d3301a5fd666c Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:59:29 +0200 Subject: [PATCH 090/105] fix bug in seir example --- cpp/examples/ode_seir.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 8de9adf9a8..57a786f767 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -63,9 +63,8 @@ for(int iter = 0; iter < 10000; iter++) { auto integrator = std::make_shared(); - auto seir = simulate(t0, tmax, dt, integrator); + auto seir = simulate(t0, tmax, dt, model, integrator); - //printf("\n number total: %f\n", // seir.get_last_value()[0] + seir.get_last_value()[1] + seir.get_last_value()[2] + seir.get_last_value()[3]); From 46fa35e91006a9248ed0364c72c2cbbc8cc5cac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:02:03 +0200 Subject: [PATCH 091/105] test cmake simulation --- cpp/examples/abm_minimal.cpp | 2 +- cpp/models/abm/simulation.cpp | 6 +++--- cpp/simulations/CMakeLists.txt | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 98a0839acb..745ec6bc4b 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -56,7 +56,7 @@ int main() LIKWID_MARKER_START("main"); - for (int iter = 0; iter < 100000; iter++) { + for (int iter = 0; iter < 10000; iter++) { // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world mio::abm::GlobalInfectionParameters infection_params; diff --git a/cpp/models/abm/simulation.cpp b/cpp/models/abm/simulation.cpp index c7f39bf912..b49a523a09 100644 --- a/cpp/models/abm/simulation.cpp +++ b/cpp/models/abm/simulation.cpp @@ -79,14 +79,14 @@ void Simulation::store_result_at(TimePoint t) //thread local sum of subpopulations, computed in parallel //TODO: maybe we can use atomic increments instead of the reduction if necessary for scaling? Eigen::VectorXd sum = Eigen::VectorXd::Zero(m_result.get_num_elements()); - auto persons = m_world.get_persons(); - + auto persons = m_world.get_persons(); + PRAGMA_OMP(for schedule(dynamic, 50)) //static? for (auto i = size_t(0); i < persons.size(); ++i) { auto&& person = persons[i]; sum[Eigen::Index(person.get_infection_state(t))] += 1; } - + //synchronized total sum PRAGMA_OMP(critical) { diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index a4fc5a4433..6774021b09 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -12,8 +12,7 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") add_executable(abm_simulation abm.cpp) - target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} - PRIVATE memilio abm likwid) + target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} likwid) target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") endif() From 3c8870c82849ad016a0500c414513e5e23e71f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:11:20 +0200 Subject: [PATCH 092/105] changed cmakelist in all simulations --- cpp/simulations/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index 6774021b09..aec3f102a9 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -1,13 +1,11 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) add_executable(2020_npis_wildtype 2020_npis_sarscov2_wildtype_germany.cpp) - target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES} - PRIVATE memilio ode_secir likwid) + target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES} likwid) target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") add_executable(2021_vaccination_delta 2021_vaccination_sarscov2_delta_germany.cpp) - target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES} - PRIVATE memilio ode_secirvvs likwid) + target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES} likwid) target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") From ed272579415872dbd46b119e7798106e75b4c719 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:30:04 +0200 Subject: [PATCH 093/105] change integrator in secir example to euler --- cpp/examples/ode_secir.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index 4f33ba1ad9..7040c46478 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -85,11 +85,11 @@ int main() model.apply_constraints(); - auto integrator = std::make_shared(); - integrator->set_dt_min(0.3); - integrator->set_dt_max(1.0); - integrator->set_rel_tolerance(1e-4); - integrator->set_abs_tolerance(1e-1); + auto integrator = std::make_shared(); + //integrator->set_dt_min(0.3); + //integrator->set_dt_max(1.0); + //integrator->set_rel_tolerance(1e-4); + //integrator->set_abs_tolerance(1e-1); LIKWID_MARKER_STOP("setup"); From 4c69aec2ef6cc76204220b2419c70c153bc88984 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 11:27:06 +0200 Subject: [PATCH 094/105] change integraotr in secirvvs ex to euler --- cpp/examples/ode_secirvvs.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index d83246b4f7..e11caba2fa 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -130,7 +130,9 @@ int main() LIKWID_MARKER_START("simulation"); - mio::TimeSeries result = simulate(t0, tmax, dt, model); + auto integrator = std::make_shared(); + + mio::TimeSeries result = simulate(t0, tmax, dt, model, integrator); LIKWID_MARKER_STOP("simulation"); From 04b521c9e2e21272a28fd4223bc7b2856edd7430 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 11:47:05 +0200 Subject: [PATCH 095/105] undo previous changes in secirvvs ex --- cpp/examples/ode_secirvvs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index e11caba2fa..2dac55d7e3 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -130,9 +130,9 @@ int main() LIKWID_MARKER_START("simulation"); - auto integrator = std::make_shared(); + //auto integrator = std::make_shared(); - mio::TimeSeries result = simulate(t0, tmax, dt, model, integrator); + mio::TimeSeries result = simulate(t0, tmax, dt, model); LIKWID_MARKER_STOP("simulation"); From 47da6028a42773479b2c0fb95b230dedaab3a463 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Wed, 20 Sep 2023 12:02:12 +0200 Subject: [PATCH 096/105] compare with previous euler results --- cpp/examples/ode_secirvvs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index 2dac55d7e3..e11caba2fa 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -130,9 +130,9 @@ int main() LIKWID_MARKER_START("simulation"); - //auto integrator = std::make_shared(); + auto integrator = std::make_shared(); - mio::TimeSeries result = simulate(t0, tmax, dt, model); + mio::TimeSeries result = simulate(t0, tmax, dt, model, integrator); LIKWID_MARKER_STOP("simulation"); From 8a8453ac20a0656d7ea6dd8ab6d74ea194637518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:43:22 +0200 Subject: [PATCH 097/105] add likwid marker api to cmakelist simulations --- cpp/simulations/CMakeLists.txt | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index aec3f102a9..de0bbfa149 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -1,16 +1,25 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) + + find_package(LIKWID REQUIRED) add_executable(2020_npis_wildtype 2020_npis_sarscov2_wildtype_germany.cpp) - target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES} likwid) - target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") + target_include_directories(2020_npis_wildtype PRIVATE ${LIKWID_INCLUDE_DIRS}) + target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES}${LIKWID_LIBRARIES}) + target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_compile_definitions(2020_npis_wildtype PRIVATE "-DLIKWID_PERFMON") + #find_package(LIKWID REQUIRED) add_executable(2021_vaccination_delta 2021_vaccination_sarscov2_delta_germany.cpp) - target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES} likwid) - target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") - + target_include_directories(2021_vaccination_delta PRIVATE ${LIKWID_INCLUDE_DIRS}) + target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES}${LIKWID_LIBRARIES}) + target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_compile_definitions(2021_vaccination_delta PRIVATE "-DLIKWID_PERFMON") + + #find_package(LIKWID REQUIRED) add_executable(abm_simulation abm.cpp) - target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} likwid) - target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") + target_include_directories(abm_simulation PRIVATE ${LIKWID_INCLUDE_DIRS}) + target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} ${LIKWID_LIBRARIES}) + target_compile_options(abm_simulation PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_compile_definitions(abm_simulation PRIVATE "-DLIKWID_PERFMON") + endif() + From 79af74b44d223bb17f524f800c6f75de331e0412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 21 Sep 2023 14:12:39 +0200 Subject: [PATCH 098/105] fix bugs in cmakelist simulation --- cpp/simulations/CMakeLists.txt | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index de0bbfa149..f23125c861 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -1,20 +1,16 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) - find_package(LIKWID REQUIRED) add_executable(2020_npis_wildtype 2020_npis_sarscov2_wildtype_germany.cpp) - target_include_directories(2020_npis_wildtype PRIVATE ${LIKWID_INCLUDE_DIRS}) - target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES}${LIKWID_LIBRARIES}) - target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - target_compile_definitions(2020_npis_wildtype PRIVATE "-DLIKWID_PERFMON") + target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES} likwid) + target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") - #find_package(LIKWID REQUIRED) add_executable(2021_vaccination_delta 2021_vaccination_sarscov2_delta_germany.cpp) - target_include_directories(2021_vaccination_delta PRIVATE ${LIKWID_INCLUDE_DIRS}) - target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES}${LIKWID_LIBRARIES}) - target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) - target_compile_definitions(2021_vaccination_delta PRIVATE "-DLIKWID_PERFMON") - - #find_package(LIKWID REQUIRED) + target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES} likwid) + target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} + PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") + + find_package(LIKWID REQUIRED) add_executable(abm_simulation abm.cpp) target_include_directories(abm_simulation PRIVATE ${LIKWID_INCLUDE_DIRS}) target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} ${LIKWID_LIBRARIES}) From 70f37b246ebcf5af678a73e52bc3066345b3dd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 21 Sep 2023 14:17:40 +0200 Subject: [PATCH 099/105] comment errors from cmakelist --- cpp/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index b571689a35..c923bbb030 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -101,8 +101,8 @@ endif((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND(CMAKE_CXX_COMPILER_VERSION VERS # define flags to enable most warnings and treat them as errors for different compilers # add flags to each target separately instead of globally so users have the choice to use their own flags if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS - "-Wno-unknown-warning;-Wno-pragmas;-Wall;-Wextra;-Werror;-Wshadow;--pedantic-errors;-Wno-deprecated-copy;-Wno-expansion-to-defined") + #set(MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS + # "-Wno-unknown-warning;-Wno-pragmas;-Wall;-Wextra;-Werror;-Wshadow;--pedantic-errors;-Wno-deprecated-copy;-Wno-expansion-to-defined") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS "-Wno-unknown-warning-option;-Wall;-Wextra;-Werror;-Wshadow;--pedantic-errors;-Wno-deprecated;-Wno-gnu-zero-variadic-macro-arguments") From 7ad5c31656236d9f21d88eb69f76c919e0b49672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CBenoBurth=E2=80=9D?= <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 21 Sep 2023 15:05:59 +0200 Subject: [PATCH 100/105] change abm simulation und cmakelist simulations --- cpp/simulations/CMakeLists.txt | 1 + cpp/simulations/abm.cpp | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index f23125c861..39cafc9d9a 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -10,6 +10,7 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") + find_package(LIKWID REQUIRED) add_executable(abm_simulation abm.cpp) target_include_directories(abm_simulation PRIVATE ${LIKWID_INCLUDE_DIRS}) diff --git a/cpp/simulations/abm.cpp b/cpp/simulations/abm.cpp index 911d59355d..4a288e658f 100644 --- a/cpp/simulations/abm.cpp +++ b/cpp/simulations/abm.cpp @@ -836,7 +836,10 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num // Loop over a number of runs while (run_idx <= num_runs) { - LIKWID_MARKER_START("initialization"); +#pragma omp parallel + { + LIKWID_MARKER_START("initialization"); + } // Create the sampled simulation with start time t0. auto sim = create_sampled_simulation(t0); // Collect the id of location in world. @@ -844,12 +847,24 @@ mio::IOResult run_abm_simulation_xx(const fs::path& result_dir, size_t num for (auto& location : sim.get_world().get_locations()) { loc_ids.push_back(location.get_index()); } - LIKWID_MARKER_STOP("initialization"); - LIKWID_MARKER_START("simulation"); +#pragma omp parallel + { + LIKWID_MARKER_STOP("initialization"); + } + +#pragma omp parallel + { + LIKWID_MARKER_START("simulation"); + } + // Advance the world to tmax sim.advance(tmax); - LIKWID_MARKER_STOP("simulation"); + +#pragma omp parallel + { + LIKWID_MARKER_STOP("simulation"); + } // TODO: update result of the simulation to be a vector of location result. auto temp_sim_result = std::vector>{sim.get_result()}; From b61d1340d2886d835c213e17af0d6d2bbf5c8f91 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:53:43 +0200 Subject: [PATCH 101/105] adjust main loops --- cpp/examples/ode_secir.cpp | 20 ++++++++++---------- cpp/examples/ode_secirvvs.cpp | 5 +++++ cpp/examples/ode_seir.cpp | 8 +++++--- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index 7040c46478..896125d693 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -20,17 +20,17 @@ #include "ode_secir/model.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" -#include +//#include int main() { - LIKWID_MARKER_INIT; + //LIKWID_MARKER_INIT; - LIKWID_MARKER_START("setup"); + //LIKWID_MARKER_START("setup"); - for(int iter = 0; iter < 10000; iter++) { + for(int iter = 0; iter < 200000; iter++) { mio::set_log_level(mio::LogLevel::debug); @@ -91,15 +91,15 @@ int main() //integrator->set_rel_tolerance(1e-4); //integrator->set_abs_tolerance(1e-1); - LIKWID_MARKER_STOP("setup"); - - LIKWID_MARKER_START("simulation"); - + //LIKWID_MARKER_STOP("setup"); + + //LIKWID_MARKER_START("simulation"); + mio::TimeSeries secir = simulate(t0, tmax, dt, model, integrator); - LIKWID_MARKER_STOP("simulation"); + //LIKWID_MARKER_STOP("simulation"); - LIKWID_MARKER_CLOSE; + //LIKWID_MARKER_CLOSE; bool print_to_terminal = false; diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index e11caba2fa..bfa4fb882a 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -126,13 +126,18 @@ int main() // use default Cash-Karp adaptive integrator + } + LIKWID_MARKER_STOP("setup"); LIKWID_MARKER_START("simulation"); + for(int iter=0;iter<10000;iter++) { + auto integrator = std::make_shared(); mio::TimeSeries result = simulate(t0, tmax, dt, model, integrator); + } LIKWID_MARKER_STOP("simulation"); diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 57a786f767..11f4893c16 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -27,7 +27,7 @@ int main() { -for(int iter = 0; iter < 10000; iter++) { +for(int iter = 0; iter < 2000000; iter++) { //perform multiple iterations for more accurate likwid measurements @@ -60,10 +60,12 @@ for(int iter = 0; iter < 10000; iter++) { model.check_constraints(); // print_seir_params(model); - auto integrator = std::make_shared(); + //auto integrator = std::make_shared(); - auto seir = simulate(t0, tmax, dt, model, integrator); + //auto seir = simulate(t0, tmax, dt, model, integrator); + auto seir = simulate(t0, tmax, dt, model); + //printf("\n number total: %f\n", From 3f96aa97fb151b010a5812445e9518af4a1f16b4 Mon Sep 17 00:00:00 2001 From: BenoBurth <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 28 Sep 2023 15:11:58 +0200 Subject: [PATCH 102/105] add changes for likwid marker api in abm and cmake --- cpp/CMakeLists.txt | 2 + cpp/examples/CMakeLists.txt | 15 ++- cpp/examples/abm_minimal.cpp | 212 ++++++++++++++++----------------- cpp/models/abm/CMakeLists.txt | 8 +- cpp/models/abm/world.cpp | 36 ++++++ cpp/simulations/CMakeLists.txt | 2 +- cpp/tests/CMakeLists.txt | 5 +- 7 files changed, 162 insertions(+), 118 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index c923bbb030..4c6ead4144 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -23,6 +23,8 @@ mark_as_advanced(MEMILIO_USE_BUNDLED_SPDLOG MEMILIO_SANITIZE_ADDRESS MEMILIO_SAN set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +find_package(LIKWID REQUIRED) + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Please choose Release or Debug. Default is Release." FORCE) endif() diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index 0bc47e784b..b7d28cf99f 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -15,9 +15,10 @@ target_link_libraries(adapt_rk_example PRIVATE memilio) target_compile_options(adapt_rk_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(ode_seir_example ode_seir.cpp) -target_link_libraries(ode_seir_example PRIVATE memilio ode_seir likwid) -target_compile_options(ode_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") +target_include_directories(ode_seir_example PRIVATE ${LIKWID_INCLUDE_DIRS}) +target_link_libraries(ode_seir_example PRIVATE memilio ode_seir ${LIKWID_LIBRARIES}) +target_compile_options(ode_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_compile_definitions(ode_seir_example PRIVATE "-DLIKWID_PERFMON") add_executable(ode_secir_example ode_secir.cpp) target_link_libraries(ode_secir_example PRIVATE memilio ode_secir likwid) @@ -44,9 +45,11 @@ target_link_libraries(graph_stochastic_mobility_example PRIVATE memilio ode_seci target_compile_options(graph_stochastic_mobility_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) add_executable(abm_minimal_example abm_minimal.cpp) -target_link_libraries(abm_minimal_example PRIVATE memilio abm likwid) -target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") +target_include_directories(abm_minimal_example PRIVATE ${LIKWID_INCLUDE_DIRS}) +target_link_libraries(abm_minimal_example PRIVATE memilio abm ${LIKWID_LIBRARIES}) +target_compile_options(abm_minimal_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_compile_definitions(abm_minimal_example PRIVATE "-DLIKWID_PERFMON") + add_executable(abm_history_example abm_history_object.cpp) target_link_libraries(abm_history_example PRIVATE memilio abm) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 745ec6bc4b..1c74390b41 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -56,118 +56,116 @@ int main() LIKWID_MARKER_START("main"); - for (int iter = 0; iter < 10000; iter++) { - - // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world - mio::abm::GlobalInfectionParameters infection_params; - - // Set same infection parameter for all age groups. For example, the incubation period is 4 days. - infection_params.get() = 4.; - - // Create the world with infection parameters. - auto world = mio::abm::World(infection_params); - - // There are 3 households for each household group. - int n_households = 3; - - // 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(); // A child is 50/50% 0-4 or 5-14. - child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); - child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); - - auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. - parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); - parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); - - // Two-person household with one parent and one child. - auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); - auto twoPersonHousehold_full = mio::abm::Household(); - twoPersonHousehold_full.add_members(child, 1); - twoPersonHousehold_full.add_members(parent, 1); - twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); - add_household_group_to_world(world, twoPersonHousehold_group); - - // Three-person household with two parent and one child. - auto threePersonHousehold_group = mio::abm::HouseholdGroup(); - auto threePersonHousehold_full = mio::abm::Household(); - threePersonHousehold_full.add_members(child, 1); - threePersonHousehold_full.add_members(parent, 2); - threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); - add_household_group_to_world(world, threePersonHousehold_group); - - // Add one social event with 5 maximum contacts. - // Maximum contacs limit the number of people that a person can infect while being at this location. - auto event = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(5); - // 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 one supermarket, maximum constacts are assumed to be 20. - auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); - // At every school, the maximum contacts are 20. - auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(20); - // At every workplace, maximum contacts are 10. - auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(10); - - // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. - auto testing_min_time = mio::abm::days(1); - auto probability = 0.5; - auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - auto test_type = mio::abm::AntigenTest(); - auto test_at_work = std::vector{mio::abm::LocationType::Work}; - auto testing_criteria_work = - std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; - 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); - - // Assign infection state to each person. - // The infection states are chosen randomly. - auto persons = world.get_persons(); - for (auto& person : persons) { - mio::abm::InfectionState infection_state = - (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); - if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - world.get_global_infection_parameters(), start_date, - infection_state)); - } + // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world + mio::abm::GlobalInfectionParameters infection_params; + + // Set same infection parameter for all age groups. For example, the incubation period is 4 days. + infection_params.get() = 4.; + + // Create the world with infection parameters. + auto world = mio::abm::World(infection_params); + + // There are 3 households for each household group. + int n_households = 3; + + // 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(); // A child is 50/50% 0-4 or 5-14. + child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); + child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); + + auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. + parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); + parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); + + // Two-person household with one parent and one child. + auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); + auto twoPersonHousehold_full = mio::abm::Household(); + twoPersonHousehold_full.add_members(child, 1); + twoPersonHousehold_full.add_members(parent, 1); + twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); + add_household_group_to_world(world, twoPersonHousehold_group); + + // Three-person household with two parent and one child. + auto threePersonHousehold_group = mio::abm::HouseholdGroup(); + auto threePersonHousehold_full = mio::abm::Household(); + threePersonHousehold_full.add_members(child, 1); + threePersonHousehold_full.add_members(parent, 2); + threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); + add_household_group_to_world(world, threePersonHousehold_group); + + // Add one social event with 5 maximum contacts. + // Maximum contacs limit the number of people that a person can infect while being at this location. + auto event = world.add_location(mio::abm::LocationType::SocialEvent); + world.get_individualized_location(event).get_infection_parameters().set(5); + // 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 one supermarket, maximum constacts are assumed to be 20. + auto shop = world.add_location(mio::abm::LocationType::BasicsShop); + world.get_individualized_location(shop).get_infection_parameters().set(20); + // At every school, the maximum contacts are 20. + auto school = world.add_location(mio::abm::LocationType::School); + world.get_individualized_location(school).get_infection_parameters().set(20); + // At every workplace, maximum contacts are 10. + auto work = world.add_location(mio::abm::LocationType::Work); + world.get_individualized_location(work).get_infection_parameters().set(10); + + // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. + auto testing_min_time = mio::abm::days(1); + auto probability = 0.5; + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); + auto test_type = mio::abm::AntigenTest(); + auto test_at_work = std::vector{mio::abm::LocationType::Work}; + auto testing_criteria_work = + std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; + 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); + + // Assign infection state to each person. + // The infection states are chosen randomly. + auto persons = world.get_persons(); + for (auto& person : persons) { + mio::abm::InfectionState infection_state = + (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + world.get_global_infection_parameters(), start_date, + infection_state)); + } - // Assign locations to the people - for (auto& person : persons) { - //assign shop and event - person.set_assigned_location(event); - person.set_assigned_location(shop); - //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); - //assign work/school to people depending on their age - if (person.get_age() == mio::abm::AgeGroup::Age5to14) { - person.set_assigned_location(school); - } - if (person.get_age() == mio::abm::AgeGroup::Age15to34 || - person.get_age() == mio::abm::AgeGroup::Age35to59) { - person.set_assigned_location(work); - } + // Assign locations to the people + for (auto& person : persons) { + //assign shop and event + person.set_assigned_location(event); + person.set_assigned_location(shop); + //assign hospital and ICU + person.set_assigned_location(hospital); + person.set_assigned_location(icu); + //assign work/school to people depending on their age + if (person.get_age() == mio::abm::AgeGroup::Age5to14) { + person.set_assigned_location(school); } + if (person.get_age() == mio::abm::AgeGroup::Age15to34 || person.get_age() == mio::abm::AgeGroup::Age35to59) { + person.set_assigned_location(work); + } + } - // During the lockdown, social events are closed for 90% of people. - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); - mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); + // During the lockdown, social events are closed for 90% of people. + auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); + mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); - auto t0 = mio::abm::TimePoint(0); - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); - auto sim = mio::abm::Simulation(t0, std::move(world)); + auto t0 = mio::abm::TimePoint(0); + auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); + auto sim = mio::abm::Simulation(t0, std::move(world)); - sim.advance(tmax); - } + LIKWID_MARKER_START("simulation"); + sim.advance(tmax); + LIKWID_MARKER_STOP("simulation"); LIKWID_MARKER_STOP("main"); diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index 68034ee51a..eda7a3b75b 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -28,9 +28,13 @@ add_library(abm mask.h mask.cpp ) -target_link_libraries(abm PUBLIC memilio) + +#find_package(LIKWID REQUIRED) target_include_directories(abm PUBLIC $ $ -) + ${LIKWID_INCLUDE_DIRS} +) +target_link_libraries(abm PUBLIC memilio ${LIKWID_LIBRARIES}) target_compile_options(abm PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_compile_definitions(abm PRIVATE "-DLIKWID_PERFMON") diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 707dfbd5ce..610830e352 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -30,6 +30,7 @@ #include "memilio/utils/stl_util.h" #include #include +#include namespace mio { @@ -55,16 +56,44 @@ Person& World::add_person(const LocationId id, AgeGroup age) void World::evolve(TimePoint t, TimeSpan dt) { + begin_step(t, dt); //log_info("ABM World interaction."); + +#pragma omp parallel + { + LIKWID_MARKER_START("interaction"); + } interaction(t, dt); +#pragma omp parallel + { + LIKWID_MARKER_STOP("interaction"); + } + //log_info("ABM World migration."); + +#pragma omp parallel + { + LIKWID_MARKER_START("migration"); + } migration(t, dt); +#pragma omp parallel + { + LIKWID_MARKER_STOP("migration"); + } + end_step(t, dt); } void World::interaction(TimePoint t, TimeSpan dt) { + // #pragma omp parallel + // { + // LIKWID_MARKER_START("interaction"); + // //std::cout << "region interaction started" << std::endl; + // } + //LIKWID_MARKER_START("interaction"); + PRAGMA_OMP(parallel for schedule(dynamic, 50)) //dynamic 20 for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; @@ -88,6 +117,13 @@ void World::interaction(TimePoint t, TimeSpan dt) } } } + + // #pragma omp paralell + // { + // LIKWID_MARKER_STOP("interaction"); + // //std::cout << "region interaction ended" << std::endl; + // } + //LIKWID_MARKER_STOP("interaction"); } void World::prepare() diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index 39cafc9d9a..51ee01c4d8 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -11,7 +11,7 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") - find_package(LIKWID REQUIRED) + #find_package(LIKWID REQUIRED) add_executable(abm_simulation abm.cpp) target_include_directories(abm_simulation PRIVATE ${LIKWID_INCLUDE_DIRS}) target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} ${LIKWID_LIBRARIES}) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index c9aa540f65..3b9e55f5f1 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -79,9 +79,10 @@ test_save_results.cpp endif() add_executable(memilio-test ${TESTSOURCES}) -target_include_directories(memilio-test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(memilio-test PRIVATE memilio ode_secir ode_seir ode_secirvvs ide_seir ide_secir abm gtest_main) +target_include_directories(memilio-test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIKWID_INCLUDE_DIRS}) +target_link_libraries(memilio-test PRIVATE memilio ode_secir ode_seir ode_secirvvs ide_seir ide_secir abm gtest_main ${LIKWID_LIBRARIES}) target_compile_options(memilio-test PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_compile_definitions(memilio-test PRIVATE "-DLIKWID_PERFMON") # make unit tests find the test data files file(TO_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/data" MEMILIO_TEST_DATA_DIR) From 0127c1f937de0ea212b23b81698901678a30b187 Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Fri, 29 Sep 2023 15:06:27 +0200 Subject: [PATCH 103/105] final measurement changes --- cpp/examples/ode_secirvvs.cpp | 29 ++++------------------------- cpp/examples/ode_seir.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index bfa4fb882a..8668b4c161 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -20,16 +20,11 @@ #include "ode_secirvvs/model.h" #include "memilio/compartments/simulation.h" #include "memilio/utils/logging.h" -#include - int main() { - LIKWID_MARKER_INIT; - - LIKWID_MARKER_START("setup"); - for(int iter = 0; iter < 10000; iter++) { + for(int iter=0; iter < 100000; iter++) { mio::set_log_level(mio::LogLevel::debug); @@ -37,7 +32,7 @@ int main() double tmax = 30; double dt = 0.1; - //mio::log_info("Simulating SECIRVVS; t={} ... {} with dt = {}.", t0, tmax, dt); + mio::log_info("Simulating SECIRVVS; t={} ... {} with dt = {}.", t0, tmax, dt); mio::osecirvvs::Model model(1); @@ -117,7 +112,7 @@ int main() model.apply_constraints(); // use adaptive Runge-Kutta-Fehlberg45 scheme as integrator - // auto integrator = std::make_shared(); + auto integrator = std::make_shared(); // integrator->set_dt_min(0.3); // integrator->set_dt_max(1.0); // integrator->set_rel_tolerance(1e-4); @@ -125,23 +120,7 @@ int main() // mio::TimeSeries secir = simulate(t0, tmax, dt, model, integrator); // use default Cash-Karp adaptive integrator - - } - - LIKWID_MARKER_STOP("setup"); - - LIKWID_MARKER_START("simulation"); - - for(int iter=0;iter<10000;iter++) { - - auto integrator = std::make_shared(); - mio::TimeSeries result = simulate(t0, tmax, dt, model, integrator); - } - - LIKWID_MARKER_STOP("simulation"); - - LIKWID_MARKER_CLOSE; bool print_to_terminal = false; @@ -153,4 +132,4 @@ int main() } } -} +} \ No newline at end of file diff --git a/cpp/examples/ode_seir.cpp b/cpp/examples/ode_seir.cpp index 11f4893c16..471a4d344b 100644 --- a/cpp/examples/ode_seir.cpp +++ b/cpp/examples/ode_seir.cpp @@ -27,7 +27,7 @@ int main() { -for(int iter = 0; iter < 2000000; iter++) { +for(int iter = 0; iter < 200000; iter++) { //perform multiple iterations for more accurate likwid measurements @@ -60,11 +60,11 @@ for(int iter = 0; iter < 2000000; iter++) { model.check_constraints(); // print_seir_params(model); - //auto integrator = std::make_shared(); + auto integrator = std::make_shared(); - //auto seir = simulate(t0, tmax, dt, model, integrator); - auto seir = simulate(t0, tmax, dt, model); + auto seir = simulate(t0, tmax, dt, model, integrator); + //auto seir = simulate(t0, tmax, dt, model); From 86601cc4b57ec179d607563bbdb5bce790d6d94c Mon Sep 17 00:00:00 2001 From: alexgit0 <95716945+alexgit0@users.noreply.github.com> Date: Fri, 29 Sep 2023 15:45:28 +0200 Subject: [PATCH 104/105] remeasure for testing purposes --- cpp/examples/ode_secir.cpp | 4 ++-- cpp/examples/ode_secirvvs.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/examples/ode_secir.cpp b/cpp/examples/ode_secir.cpp index 896125d693..07d7ee05a5 100644 --- a/cpp/examples/ode_secir.cpp +++ b/cpp/examples/ode_secir.cpp @@ -85,7 +85,7 @@ int main() model.apply_constraints(); - auto integrator = std::make_shared(); + //auto integrator = std::make_shared(); //integrator->set_dt_min(0.3); //integrator->set_dt_max(1.0); //integrator->set_rel_tolerance(1e-4); @@ -95,7 +95,7 @@ int main() //LIKWID_MARKER_START("simulation"); - mio::TimeSeries secir = simulate(t0, tmax, dt, model, integrator); + mio::TimeSeries secir = simulate(t0, tmax, dt, model); //LIKWID_MARKER_STOP("simulation"); diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index 8668b4c161..7d78e269bb 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -112,7 +112,7 @@ int main() model.apply_constraints(); // use adaptive Runge-Kutta-Fehlberg45 scheme as integrator - auto integrator = std::make_shared(); + auto integrator = std::make_shared(); // integrator->set_dt_min(0.3); // integrator->set_dt_max(1.0); // integrator->set_rel_tolerance(1e-4); From b7795355a69c7fafe2edbc57ab5f76fefdf6272e Mon Sep 17 00:00:00 2001 From: BenoBurth <142229168+BenoBurth@users.noreply.github.com> Date: Thu, 5 Oct 2023 10:37:11 +0200 Subject: [PATCH 105/105] add final changes --- cpp/CMakeLists.txt | 1 + cpp/examples/CMakeLists.txt | 21 ++- cpp/examples/abm_minimal.cpp | 220 ++++++++++++++--------------- cpp/examples/ode_secirvvs.cpp | 238 ++++++++++++++++---------------- cpp/models/abm/CMakeLists.txt | 2 +- cpp/models/abm/world.cpp | 15 +- cpp/simulations/CMakeLists.txt | 17 +-- cpp/tests/test_save_results.cpp | 13 +- 8 files changed, 250 insertions(+), 277 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 4c6ead4144..9af3ba5760 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -23,6 +23,7 @@ mark_as_advanced(MEMILIO_USE_BUNDLED_SPDLOG MEMILIO_SANITIZE_ADDRESS MEMILIO_SAN set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# needed for likwid marker api find_package(LIKWID REQUIRED) if(NOT CMAKE_BUILD_TYPE) diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index b7d28cf99f..4ae9bdb77d 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -21,16 +21,16 @@ target_compile_options(ode_seir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNI target_compile_definitions(ode_seir_example PRIVATE "-DLIKWID_PERFMON") add_executable(ode_secir_example ode_secir.cpp) -target_link_libraries(ode_secir_example PRIVATE memilio ode_secir likwid) -target_compile_options(ode_secir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") - +target_include_directories(ode_secir_example PRIVATE ${LIKWID_INCLUDE_DIRS}) +target_link_libraries(ode_secir_example PRIVATE memilio ode_secir ${LIKWID_LIBRARIES}) +target_compile_options(ode_secir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_compile_definitions(ode_secir_example PRIVATE "-DLIKWID_PERFMON") add_executable(ode_secirvvs_example ode_secirvvs.cpp) -target_link_libraries(ode_secirvvs_example PRIVATE memilio ode_secirvvs likwid) -target_compile_options(ode_secirvvs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") - +target_include_directories(ode_secirvvs_example PRIVATE ${LIKWID_INCLUDE_DIRS}) +target_link_libraries(ode_secirvvs_example PRIVATE memilio ode_secirvvs ${LIKWID_LIBRARIES}) +target_compile_options(ode_secirvvs_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) +target_compile_definitions(ode_secirvvs_example PRIVATE "-DLIKWID_PERFMON") add_executable(ode_secir_ageres_example ode_secir_ageres.cpp) target_link_libraries(ode_secir_ageres_example PRIVATE memilio ode_secir) @@ -67,11 +67,6 @@ add_executable(ide_secir_example ide_secir.cpp) target_link_libraries(ide_secir_example PRIVATE memilio ide_secir) target_compile_options(ide_secir_example PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) -#parallelization cmake -#add_executable(parallelization_test_example parallelization_test.cpp) -#target_link_libraries(parallelization_test_example PRIVATE omp) -#target_compile_options(parallelization_test_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) diff --git a/cpp/examples/abm_minimal.cpp b/cpp/examples/abm_minimal.cpp index 1c74390b41..d3e5c78cec 100644 --- a/cpp/examples/abm_minimal.cpp +++ b/cpp/examples/abm_minimal.cpp @@ -54,120 +54,124 @@ int main() LIKWID_MARKER_INIT; - LIKWID_MARKER_START("main"); - - // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world - mio::abm::GlobalInfectionParameters infection_params; - - // Set same infection parameter for all age groups. For example, the incubation period is 4 days. - infection_params.get() = 4.; - - // Create the world with infection parameters. - auto world = mio::abm::World(infection_params); - - // There are 3 households for each household group. - int n_households = 3; - - // 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(); // A child is 50/50% 0-4 or 5-14. - child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); - child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); - - auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. - parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); - parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); - - // Two-person household with one parent and one child. - auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); - auto twoPersonHousehold_full = mio::abm::Household(); - twoPersonHousehold_full.add_members(child, 1); - twoPersonHousehold_full.add_members(parent, 1); - twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); - add_household_group_to_world(world, twoPersonHousehold_group); - - // Three-person household with two parent and one child. - auto threePersonHousehold_group = mio::abm::HouseholdGroup(); - auto threePersonHousehold_full = mio::abm::Household(); - threePersonHousehold_full.add_members(child, 1); - threePersonHousehold_full.add_members(parent, 2); - threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); - add_household_group_to_world(world, threePersonHousehold_group); - - // Add one social event with 5 maximum contacts. - // Maximum contacs limit the number of people that a person can infect while being at this location. - auto event = world.add_location(mio::abm::LocationType::SocialEvent); - world.get_individualized_location(event).get_infection_parameters().set(5); - // 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 one supermarket, maximum constacts are assumed to be 20. - auto shop = world.add_location(mio::abm::LocationType::BasicsShop); - world.get_individualized_location(shop).get_infection_parameters().set(20); - // At every school, the maximum contacts are 20. - auto school = world.add_location(mio::abm::LocationType::School); - world.get_individualized_location(school).get_infection_parameters().set(20); - // At every workplace, maximum contacts are 10. - auto work = world.add_location(mio::abm::LocationType::Work); - world.get_individualized_location(work).get_infection_parameters().set(10); - - // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. - auto testing_min_time = mio::abm::days(1); - auto probability = 0.5; - auto start_date = mio::abm::TimePoint(0); - auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); - auto test_type = mio::abm::AntigenTest(); - auto test_at_work = std::vector{mio::abm::LocationType::Work}; - auto testing_criteria_work = - std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; - 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); - - // Assign infection state to each person. - // The infection states are chosen randomly. - auto persons = world.get_persons(); - for (auto& person : persons) { - mio::abm::InfectionState infection_state = - (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); - auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); - if (infection_state != mio::abm::InfectionState::Susceptible) - person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), - world.get_global_infection_parameters(), start_date, - infection_state)); - } - - // Assign locations to the people - for (auto& person : persons) { - //assign shop and event - person.set_assigned_location(event); - person.set_assigned_location(shop); - //assign hospital and ICU - person.set_assigned_location(hospital); - person.set_assigned_location(icu); - //assign work/school to people depending on their age - if (person.get_age() == mio::abm::AgeGroup::Age5to14) { - person.set_assigned_location(school); + for (int iter = 0; iter < 100; iter++) { + + LIKWID_MARKER_START("main"); + + // Set global infection parameters (similar to infection parameters in SECIR model) and initialize the world + mio::abm::GlobalInfectionParameters infection_params; + + // Set same infection parameter for all age groups. For example, the incubation period is 4 days. + infection_params.get() = 4.; + + // Create the world with infection parameters. + auto world = mio::abm::World(infection_params); + + // There are 3 households for each household group. + int n_households = 3; + + // 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(); // A child is 50/50% 0-4 or 5-14. + child.set_age_weight(mio::abm::AgeGroup::Age0to4, 1); + child.set_age_weight(mio::abm::AgeGroup::Age5to14, 1); + + auto parent = mio::abm::HouseholdMember(); // A parent is 50/50% 15-34 or 35-59. + parent.set_age_weight(mio::abm::AgeGroup::Age15to34, 1); + parent.set_age_weight(mio::abm::AgeGroup::Age35to59, 1); + + // Two-person household with one parent and one child. + auto twoPersonHousehold_group = mio::abm::HouseholdGroup(); + auto twoPersonHousehold_full = mio::abm::Household(); + twoPersonHousehold_full.add_members(child, 1); + twoPersonHousehold_full.add_members(parent, 1); + twoPersonHousehold_group.add_households(twoPersonHousehold_full, n_households); + add_household_group_to_world(world, twoPersonHousehold_group); + + // Three-person household with two parent and one child. + auto threePersonHousehold_group = mio::abm::HouseholdGroup(); + auto threePersonHousehold_full = mio::abm::Household(); + threePersonHousehold_full.add_members(child, 1); + threePersonHousehold_full.add_members(parent, 2); + threePersonHousehold_group.add_households(threePersonHousehold_full, n_households); + add_household_group_to_world(world, threePersonHousehold_group); + + // Add one social event with 5 maximum contacts. + // Maximum contacs limit the number of people that a person can infect while being at this location. + auto event = world.add_location(mio::abm::LocationType::SocialEvent); + world.get_individualized_location(event).get_infection_parameters().set(5); + // 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 one supermarket, maximum constacts are assumed to be 20. + auto shop = world.add_location(mio::abm::LocationType::BasicsShop); + world.get_individualized_location(shop).get_infection_parameters().set(20); + // At every school, the maximum contacts are 20. + auto school = world.add_location(mio::abm::LocationType::School); + world.get_individualized_location(school).get_infection_parameters().set(20); + // At every workplace, maximum contacts are 10. + auto work = world.add_location(mio::abm::LocationType::Work); + world.get_individualized_location(work).get_infection_parameters().set(10); + + // People can get tested at work (and do this with 0.5 probability) from time point 0 to day 30. + auto testing_min_time = mio::abm::days(1); + auto probability = 0.5; + auto start_date = mio::abm::TimePoint(0); + auto end_date = mio::abm::TimePoint(0) + mio::abm::days(30); + auto test_type = mio::abm::AntigenTest(); + auto test_at_work = std::vector{mio::abm::LocationType::Work}; + auto testing_criteria_work = + std::vector{mio::abm::TestingCriteria({}, test_at_work, {})}; + 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); + + // Assign infection state to each person. + // The infection states are chosen randomly. + auto persons = world.get_persons(); + for (auto& person : persons) { + mio::abm::InfectionState infection_state = + (mio::abm::InfectionState)(rand() % ((uint32_t)mio::abm::InfectionState::Count - 1)); + auto rng = mio::abm::Person::RandomNumberGenerator(world.get_rng(), person); + if (infection_state != mio::abm::InfectionState::Susceptible) + person.add_new_infection(mio::abm::Infection(rng, mio::abm::VirusVariant::Wildtype, person.get_age(), + world.get_global_infection_parameters(), start_date, + infection_state)); } - if (person.get_age() == mio::abm::AgeGroup::Age15to34 || person.get_age() == mio::abm::AgeGroup::Age35to59) { - person.set_assigned_location(work); + + // Assign locations to the people + for (auto& person : persons) { + //assign shop and event + person.set_assigned_location(event); + person.set_assigned_location(shop); + //assign hospital and ICU + person.set_assigned_location(hospital); + person.set_assigned_location(icu); + //assign work/school to people depending on their age + if (person.get_age() == mio::abm::AgeGroup::Age5to14) { + person.set_assigned_location(school); + } + if (person.get_age() == mio::abm::AgeGroup::Age15to34 || + person.get_age() == mio::abm::AgeGroup::Age35to59) { + person.set_assigned_location(work); + } } - } - // During the lockdown, social events are closed for 90% of people. - auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); - mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); + // During the lockdown, social events are closed for 90% of people. + auto t_lockdown = mio::abm::TimePoint(0) + mio::abm::days(10); + mio::abm::close_social_events(t_lockdown, 0.9, world.get_migration_parameters()); - auto t0 = mio::abm::TimePoint(0); - auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); - auto sim = mio::abm::Simulation(t0, std::move(world)); + auto t0 = mio::abm::TimePoint(0); + auto tmax = mio::abm::TimePoint(0) + mio::abm::days(30); + auto sim = mio::abm::Simulation(t0, std::move(world)); - LIKWID_MARKER_START("simulation"); - sim.advance(tmax); - LIKWID_MARKER_STOP("simulation"); + LIKWID_MARKER_START("simulation"); + sim.advance(tmax); + LIKWID_MARKER_STOP("simulation"); - LIKWID_MARKER_STOP("main"); + LIKWID_MARKER_STOP("main"); + } LIKWID_MARKER_CLOSE; } diff --git a/cpp/examples/ode_secirvvs.cpp b/cpp/examples/ode_secirvvs.cpp index bfa4fb882a..90d740cde3 100644 --- a/cpp/examples/ode_secirvvs.cpp +++ b/cpp/examples/ode_secirvvs.cpp @@ -22,135 +22,129 @@ #include "memilio/utils/logging.h" #include - int main() { LIKWID_MARKER_INIT; LIKWID_MARKER_START("setup"); - for(int iter = 0; iter < 10000; iter++) { - - mio::set_log_level(mio::LogLevel::debug); - - double t0 = 0; - double tmax = 30; - double dt = 0.1; - - //mio::log_info("Simulating SECIRVVS; t={} ... {} with dt = {}.", t0, tmax, dt); - - mio::osecirvvs::Model model(1); - - for (mio::AgeGroup i = 0; i < model.parameters.get_num_groups(); i++) { - model.populations[{i, mio::osecirvvs::InfectionState::ExposedNaive}] = 10; - model.populations[{i, mio::osecirvvs::InfectionState::ExposedImprovedImmunity}] = 11; - model.populations[{i, mio::osecirvvs::InfectionState::ExposedPartialImmunity}] = 12; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsNaive}] = 13; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsNaiveConfirmed}] = 13; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsPartialImmunity}] = 14; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsPartialImmunityConfirmed}] = 14; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunity}] = 15; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed}] = 15; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsNaive}] = 5; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsNaiveConfirmed}] = 5; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsPartialImmunity}] = 6; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsPartialImmunityConfirmed}] = 6; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunity}] = 7; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunityConfirmed}] = 7; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSevereNaive}] = 8; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSevereImprovedImmunity}] = 1; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedSeverePartialImmunity}] = 2; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedCriticalNaive}] = 3; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedCriticalPartialImmunity}] = 4; - model.populations[{i, mio::osecirvvs::InfectionState::InfectedCriticalImprovedImmunity}] = 5; - model.populations[{i, mio::osecirvvs::InfectionState::SusceptibleImprovedImmunity}] = 6; - model.populations[{i, mio::osecirvvs::InfectionState::SusceptiblePartialImmunity}] = 7; - model.populations[{(mio::AgeGroup)0, mio::osecirvvs::InfectionState::DeadNaive}] = 0; - model.populations[{(mio::AgeGroup)0, mio::osecirvvs::InfectionState::DeadPartialImmunity}] = 0; - model.populations[{(mio::AgeGroup)0, mio::osecirvvs::InfectionState::DeadImprovedImmunity}] = 0; - model.populations.set_difference_from_group_total( - {i, mio::osecirvvs::InfectionState::SusceptibleNaive}, 1000); - } - - model.parameters.get() = 100; - model.parameters.get() = 0.0143; - model.parameters.get().resize(mio::SimulationDay(size_t(1000))); - model.parameters.get().array().setConstant(5); - model.parameters.get().resize(mio::SimulationDay(size_t(1000))); - model.parameters.get().array().setConstant(3); - - auto& contacts = model.parameters.get(); - auto& contact_matrix = contacts.get_cont_freq_mat(); - contact_matrix[0].get_baseline().setConstant(0.5); - contact_matrix[0].get_baseline().diagonal().setConstant(5.0); - contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); - - //times - model.parameters.get()[mio::AgeGroup(0)] = 5.2; - model.parameters.get()[mio::AgeGroup(0)] = 0.5 * 3.33 + 0.5 * 5.2; - model.parameters.get()[mio::AgeGroup(0)] = 7; - model.parameters.get()[mio::AgeGroup(0)] = 6; - model.parameters.get()[mio::AgeGroup(0)] = 7; - - //probabilities - model.parameters.get()[mio::AgeGroup(0)] = 0.15; - model.parameters.get()[mio::AgeGroup(0)] = 0.5; - // The precise value between Risk* (situation under control) and MaxRisk* (situation not under control) - // depends on incidence and test and trace capacity - model.parameters.get()[mio::AgeGroup(0)] = 0.0; - model.parameters.get()[mio::AgeGroup(0)] = 0.4; - model.parameters.get()[mio::AgeGroup(0)] = 0.2; - model.parameters.get()[mio::AgeGroup(0)] = 0.1; - model.parameters.get()[mio::AgeGroup(0)] = 0.1; - model.parameters.get()[mio::AgeGroup(0)] = 0.1; - - model.parameters.get()[mio::AgeGroup(0)] = 0.8; - model.parameters.get()[mio::AgeGroup(0)] = 0.331; - model.parameters.get()[mio::AgeGroup(0)] = 0.65; - model.parameters.get()[mio::AgeGroup(0)] = 0.243; - model.parameters.get()[mio::AgeGroup(0)] = 0.1; - model.parameters.get()[mio::AgeGroup(0)] = 0.091; - model.parameters.get()[mio::AgeGroup(0)] = 0.9; - - model.parameters.get() = 0.2; - - model.apply_constraints(); - - // use adaptive Runge-Kutta-Fehlberg45 scheme as integrator - // auto integrator = std::make_shared(); - // integrator->set_dt_min(0.3); - // integrator->set_dt_max(1.0); - // integrator->set_rel_tolerance(1e-4); - // integrator->set_abs_tolerance(1e-1); - // mio::TimeSeries secir = simulate(t0, tmax, dt, model, integrator); - - // use default Cash-Karp adaptive integrator - - } - - LIKWID_MARKER_STOP("setup"); - - LIKWID_MARKER_START("simulation"); - - for(int iter=0;iter<10000;iter++) { - - auto integrator = std::make_shared(); - - mio::TimeSeries result = simulate(t0, tmax, dt, model, integrator); - } - - LIKWID_MARKER_STOP("simulation"); - - LIKWID_MARKER_CLOSE; - - bool print_to_terminal = false; - - if (print_to_terminal) { - printf("\n%.14f ", result.get_last_time()); - for (size_t j = 0; j < (size_t)mio::osecirvvs::InfectionState::Count; j++) { - printf("compartment %d: %.14f\n", (int)j, result.get_last_value()[j]); + for (int iter = 0; iter < 10000; iter++) { + + mio::set_log_level(mio::LogLevel::debug); + + double t0 = 0; + double tmax = 30; + double dt = 0.1; + + //mio::log_info("Simulating SECIRVVS; t={} ... {} with dt = {}.", t0, tmax, dt); + + mio::osecirvvs::Model model(1); + + for (mio::AgeGroup i = 0; i < model.parameters.get_num_groups(); i++) { + model.populations[{i, mio::osecirvvs::InfectionState::ExposedNaive}] = 10; + model.populations[{i, mio::osecirvvs::InfectionState::ExposedImprovedImmunity}] = 11; + model.populations[{i, mio::osecirvvs::InfectionState::ExposedPartialImmunity}] = 12; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsNaive}] = 13; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsNaiveConfirmed}] = 13; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsPartialImmunity}] = 14; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsPartialImmunityConfirmed}] = 14; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunity}] = 15; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedNoSymptomsImprovedImmunityConfirmed}] = 15; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsNaive}] = 5; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsNaiveConfirmed}] = 5; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsPartialImmunity}] = 6; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsPartialImmunityConfirmed}] = 6; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunity}] = 7; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSymptomsImprovedImmunityConfirmed}] = 7; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSevereNaive}] = 8; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSevereImprovedImmunity}] = 1; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedSeverePartialImmunity}] = 2; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedCriticalNaive}] = 3; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedCriticalPartialImmunity}] = 4; + model.populations[{i, mio::osecirvvs::InfectionState::InfectedCriticalImprovedImmunity}] = 5; + model.populations[{i, mio::osecirvvs::InfectionState::SusceptibleImprovedImmunity}] = 6; + model.populations[{i, mio::osecirvvs::InfectionState::SusceptiblePartialImmunity}] = 7; + model.populations[{(mio::AgeGroup)0, mio::osecirvvs::InfectionState::DeadNaive}] = 0; + model.populations[{(mio::AgeGroup)0, mio::osecirvvs::InfectionState::DeadPartialImmunity}] = 0; + model.populations[{(mio::AgeGroup)0, mio::osecirvvs::InfectionState::DeadImprovedImmunity}] = 0; + model.populations.set_difference_from_group_total( + {i, mio::osecirvvs::InfectionState::SusceptibleNaive}, 1000); } - } + model.parameters.get() = 100; + model.parameters.get() = 0.0143; + model.parameters.get().resize(mio::SimulationDay(size_t(1000))); + model.parameters.get().array().setConstant(5); + model.parameters.get().resize(mio::SimulationDay(size_t(1000))); + model.parameters.get().array().setConstant(3); + + auto& contacts = model.parameters.get(); + auto& contact_matrix = contacts.get_cont_freq_mat(); + contact_matrix[0].get_baseline().setConstant(0.5); + contact_matrix[0].get_baseline().diagonal().setConstant(5.0); + contact_matrix[0].add_damping(0.3, mio::SimulationTime(5.0)); + + //times + model.parameters.get()[mio::AgeGroup(0)] = 5.2; + model.parameters.get()[mio::AgeGroup(0)] = 0.5 * 3.33 + 0.5 * 5.2; + model.parameters.get()[mio::AgeGroup(0)] = 7; + model.parameters.get()[mio::AgeGroup(0)] = 6; + model.parameters.get()[mio::AgeGroup(0)] = 7; + + //probabilities + model.parameters.get()[mio::AgeGroup(0)] = 0.15; + model.parameters.get()[mio::AgeGroup(0)] = 0.5; + // The precise value between Risk* (situation under control) and MaxRisk* (situation not under control) + // depends on incidence and test and trace capacity + model.parameters.get()[mio::AgeGroup(0)] = 0.0; + model.parameters.get()[mio::AgeGroup(0)] = 0.4; + model.parameters.get()[mio::AgeGroup(0)] = 0.2; + model.parameters.get()[mio::AgeGroup(0)] = 0.1; + model.parameters.get()[mio::AgeGroup(0)] = 0.1; + model.parameters.get()[mio::AgeGroup(0)] = 0.1; + + model.parameters.get()[mio::AgeGroup(0)] = 0.8; + model.parameters.get()[mio::AgeGroup(0)] = 0.331; + model.parameters.get()[mio::AgeGroup(0)] = 0.65; + model.parameters.get()[mio::AgeGroup(0)] = 0.243; + model.parameters.get()[mio::AgeGroup(0)] = 0.1; + model.parameters.get()[mio::AgeGroup(0)] = + 0.091; + model.parameters.get()[mio::AgeGroup(0)] = 0.9; + + model.parameters.get() = 0.2; + + model.apply_constraints(); + + // use adaptive Runge-Kutta-Fehlberg45 scheme as integrator + // auto integrator = std::make_shared(); + // integrator->set_dt_min(0.3); + // integrator->set_dt_max(1.0); + // integrator->set_rel_tolerance(1e-4); + // integrator->set_abs_tolerance(1e-1); + // mio::TimeSeries secir = simulate(t0, tmax, dt, model, integrator); + + // use default Cash-Karp adaptive integrator + + LIKWID_MARKER_STOP("setup"); + + LIKWID_MARKER_START("simulation"); + + auto integrator = std::make_shared(); + + mio::TimeSeries result = simulate(t0, tmax, dt, model, integrator); + + LIKWID_MARKER_STOP("simulation"); + + LIKWID_MARKER_CLOSE; + + bool print_to_terminal = false; + + if (print_to_terminal) { + printf("\n%.14f ", result.get_last_time()); + for (size_t j = 0; j < (size_t)mio::osecirvvs::InfectionState::Count; j++) { + printf("compartment %d: %.14f\n", (int)j, result.get_last_value()[j]); + } + } } } diff --git a/cpp/models/abm/CMakeLists.txt b/cpp/models/abm/CMakeLists.txt index eda7a3b75b..60a87291b5 100644 --- a/cpp/models/abm/CMakeLists.txt +++ b/cpp/models/abm/CMakeLists.txt @@ -29,7 +29,7 @@ add_library(abm mask.cpp ) -#find_package(LIKWID REQUIRED) + target_include_directories(abm PUBLIC $ $ diff --git a/cpp/models/abm/world.cpp b/cpp/models/abm/world.cpp index 610830e352..4dd8eef147 100755 --- a/cpp/models/abm/world.cpp +++ b/cpp/models/abm/world.cpp @@ -58,6 +58,7 @@ void World::evolve(TimePoint t, TimeSpan dt) { begin_step(t, dt); + //log_info("ABM World interaction."); #pragma omp parallel @@ -87,13 +88,6 @@ void World::evolve(TimePoint t, TimeSpan dt) void World::interaction(TimePoint t, TimeSpan dt) { - // #pragma omp parallel - // { - // LIKWID_MARKER_START("interaction"); - // //std::cout << "region interaction started" << std::endl; - // } - //LIKWID_MARKER_START("interaction"); - PRAGMA_OMP(parallel for schedule(dynamic, 50)) //dynamic 20 for (auto i = size_t(0); i < m_persons.size(); ++i) { auto&& person = m_persons[i]; @@ -117,13 +111,6 @@ void World::interaction(TimePoint t, TimeSpan dt) } } } - - // #pragma omp paralell - // { - // LIKWID_MARKER_STOP("interaction"); - // //std::cout << "region interaction ended" << std::endl; - // } - //LIKWID_MARKER_STOP("interaction"); } void World::prepare() diff --git a/cpp/simulations/CMakeLists.txt b/cpp/simulations/CMakeLists.txt index 51ee01c4d8..f9aa30ac8a 100644 --- a/cpp/simulations/CMakeLists.txt +++ b/cpp/simulations/CMakeLists.txt @@ -1,17 +1,18 @@ if(MEMILIO_HAS_JSONCPP AND MEMILIO_HAS_HDF5) add_executable(2020_npis_wildtype 2020_npis_sarscov2_wildtype_germany.cpp) - target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES} likwid) - target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") - + target_include_directories(2020_npis_wildtype PRIVATE ${LIKWID_INCLUDE_DIRS}) + target_link_libraries(2020_npis_wildtype PRIVATE memilio ode_secir Boost::filesystem ${HDF5_C_LIBRARIES} ${LIKWID_LIBRARIES}) + target_compile_options(2020_npis_wildtype PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_compile_definitions(2020_npis_wildtype PRIVATE "-DLIKWID_PERFMON") + add_executable(2021_vaccination_delta 2021_vaccination_sarscov2_delta_germany.cpp) - target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES} likwid) - target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS} - PRIVATE "-lstdc++;-DLIKWID_PERFMON;-llikwid") + target_include_directories(2021_vaccination_delta PRIVATE ${LIKWID_INCLUDE_DIRS}) + target_link_libraries(2021_vaccination_delta PRIVATE memilio ode_secirvvs Boost::filesystem ${HDF5_C_LIBRARIES} ${LIKWID_LIBRARIES}) + target_compile_options(2021_vaccination_delta PRIVATE ${MEMILIO_CXX_FLAGS_ENABLE_WARNING_ERRORS}) + target_compile_definitions(2021_vaccination_delta PRIVATE "-DLIKWID_PERFMON") - #find_package(LIKWID REQUIRED) add_executable(abm_simulation abm.cpp) target_include_directories(abm_simulation PRIVATE ${LIKWID_INCLUDE_DIRS}) target_link_libraries(abm_simulation PRIVATE memilio abm Boost::filesystem ${HDF5_C_LIBRARIES} ${LIKWID_LIBRARIES}) diff --git a/cpp/tests/test_save_results.cpp b/cpp/tests/test_save_results.cpp index 369cff6be0..f51e8a35e4 100644 --- a/cpp/tests/test_save_results.cpp +++ b/cpp/tests/test_save_results.cpp @@ -224,17 +224,8 @@ TEST(TestSaveResult, save_result_with_params) TEST(TestSaveResult, save_percentiles_and_sums) { - < < < < < < < < < Temporary merge branch 1 == == == == - = - //rng needs to be reseeded right before using parallel parameterstudies - //to keep the rest of the tests independent, we install a temporary RNG for this test - auto prev_rng = mio::thread_local_rng(); - mio::thread_local_rng() = mio::RandomNumberGenerator(); - mio::log_thread_local_rng_seeds(mio::LogLevel::warn); - - >>>>>>>>> Temporary merge branch 2 - // set up parameter study - double t0 = 0; + // set up parameter study + double t0 = 0; double tmax = 100; double cont_freq = 10; // see Polymod study double num_total_t0 = 10000, num_exp_t0 = 100, num_inf_t0 = 50, num_car_t0 = 50, num_hosp_t0 = 20, num_icu_t0 = 10,