Skip to content

Commit

Permalink
936 implement the class infectionstate of the lct model more efficien…
Browse files Browse the repository at this point in the history
…tly (#941)

- LctInfectionState is now a class template and has only static members (variables and functions). More computation is now done at compile time.
- LctInfectionState can be used by various LCT models (not just SECIR models).

Co-authored-by: reneSchm <[email protected]>
  • Loading branch information
lenaploetzke and reneSchm authored Mar 1, 2024
1 parent c484b89 commit afd4982
Show file tree
Hide file tree
Showing 11 changed files with 476 additions and 625 deletions.
95 changes: 69 additions & 26 deletions cpp/examples/lct_secir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,42 +25,85 @@
#include "memilio/utils/time_series.h"
#include "memilio/epidemiology/uncertain_matrix.h"
#include "memilio/math/eigen.h"
#include "memilio/utils/logging.h"

#include <vector>

int main()
{
/** Simple example to demonstrate how to run a simulation using an LCT SECIR model.
Parameters, initial values and subcompartments are not meant to represent a realistic scenario. */
// Simple example to demonstrate how to run a simulation using an LCT SECIR model.
// Parameters, initial values and the number of subcompartments are not meant to represent a realistic scenario.

// Set vector that specifies the number of subcompartments.
std::vector<int> num_subcompartments((int)mio::lsecir::InfectionStateBase::Count, 1);
num_subcompartments[(int)mio::lsecir::InfectionStateBase::Exposed] = 2;
num_subcompartments[(int)mio::lsecir::InfectionStateBase::InfectedNoSymptoms] = 3;
num_subcompartments[(int)mio::lsecir::InfectionStateBase::InfectedCritical] = 5;
mio::lsecir::InfectionState infection_state(num_subcompartments);
using Model = mio::lsecir::Model<2, 3, 1, 1, 5>;
using LctState = Model::LctState;

ScalarType tmax = 20;

// Define initial distribution of the population in the subcompartments.
Eigen::VectorXd init(infection_state.get_count());
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::Susceptible)] = 750;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::Exposed)] = 30;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::Exposed) + 1] = 20;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedNoSymptoms)] = 20;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedNoSymptoms) + 1] = 10;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedNoSymptoms) + 2] = 10;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedSymptoms)] = 50;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedSevere)] = 50;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedCritical)] = 10;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedCritical) + 1] = 10;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedCritical) + 2] = 5;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedCritical) + 3] = 3;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::InfectedCritical) + 4] = 2;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::Recovered)] = 20;
init[infection_state.get_firstindex(mio::lsecir::InfectionStateBase::Dead)] = 10;
// Define the initial value vector init with the distribution of the population into subcompartments.
// This method of defining the vector using a vector of vectors is a bit of overhead, but should remind you how
// the entries of the initial value vector relate to the defined template parameters of the model or the number of subcompartments.
// It is also possible to define the initial value vector directly.
std::vector<std::vector<ScalarType>> initial_populations = {{750}, {30, 20}, {20, 10, 10}, {50},
{50}, {10, 10, 5, 3, 2}, {20}, {10}};

// Assert that initial_populations has the right shape.
if (initial_populations.size() != (int)LctState::InfectionState::Count) {
mio::log_error("The number of vectors in initial_populations does not match the number of InfectionStates.");
return 1;
}
if ((initial_populations[(int)LctState::InfectionState::Susceptible].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::Susceptible>()) ||
(initial_populations[(int)LctState::InfectionState::Exposed].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::Exposed>()) ||
(initial_populations[(int)LctState::InfectionState::InfectedNoSymptoms].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::InfectedNoSymptoms>()) ||
(initial_populations[(int)LctState::InfectionState::InfectedSymptoms].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::InfectedSymptoms>()) ||
(initial_populations[(int)LctState::InfectionState::InfectedSevere].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::InfectedSevere>()) ||
(initial_populations[(int)LctState::InfectionState::InfectedCritical].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::InfectedCritical>()) ||
(initial_populations[(int)LctState::InfectionState::Recovered].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::Recovered>()) ||
(initial_populations[(int)LctState::InfectionState::Dead].size() !=
LctState::get_num_subcompartments<LctState::InfectionState::Dead>())) {
mio::log_error("The length of at least one vector in initial_populations does not match the related number of "
"subcompartments.");
return 1;
}

// Transfer the initial values in initial_populations to the vector init.
Eigen::VectorXd init = Eigen::VectorXd::Zero(LctState::Count);
init[(int)LctState::get_first_index<LctState::InfectionState::Susceptible>()] =
initial_populations[(int)LctState::InfectionState::Susceptible][0];
for (unsigned int i = 0; i < LctState::get_num_subcompartments<LctState::InfectionState::Exposed>(); i++) {
init[(int)LctState::get_first_index<LctState::InfectionState::Exposed>() + i] =
initial_populations[(int)LctState::InfectionState::Exposed][i];
}
for (unsigned int i = 0; i < LctState::get_num_subcompartments<LctState::InfectionState::InfectedNoSymptoms>();
i++) {
init[(int)LctState::get_first_index<LctState::InfectionState::InfectedNoSymptoms>() + i] =
initial_populations[(int)LctState::InfectionState::InfectedNoSymptoms][i];
}
for (unsigned int i = 0; i < LctState::get_num_subcompartments<LctState::InfectionState::InfectedSymptoms>(); i++) {
init[(int)LctState::get_first_index<LctState::InfectionState::InfectedSymptoms>() + i] =
initial_populations[(int)LctState::InfectionState::InfectedSymptoms][i];
}
for (unsigned int i = 0; i < LctState::get_num_subcompartments<LctState::InfectionState::InfectedSevere>(); i++) {
init[(int)LctState::get_first_index<LctState::InfectionState::InfectedSevere>() + i] =
initial_populations[(int)LctState::InfectionState::InfectedSevere][i];
}
for (unsigned int i = 0; i < LctState::get_num_subcompartments<LctState::InfectionState::InfectedCritical>(); i++) {
init[(int)LctState::get_first_index<LctState::InfectionState::InfectedCritical>() + i] =
initial_populations[(int)LctState::InfectionState::InfectedCritical][i];
}
init[(int)LctState::get_first_index<LctState::InfectionState::Recovered>()] =
initial_populations[(int)LctState::InfectionState::Recovered][0];
init[(int)LctState::get_first_index<LctState::InfectionState::Dead>()] =
initial_populations[(int)LctState::InfectionState::Dead][0];

// Initialize model.
mio::lsecir::Model model(std::move(init), infection_state);
Model model(std::move(init));

// Set Parameters.
model.parameters.get<mio::lsecir::TimeExposed>() = 3.2;
Expand Down
1 change: 1 addition & 0 deletions cpp/memilio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_library(memilio
epidemiology/damping_sampling.cpp
epidemiology/dynamic_npis.h
epidemiology/dynamic_npis.cpp
epidemiology/lct_infection_state.h
geography/regions.h
geography/regions.cpp
epidemiology/simulation_day.h
Expand Down
86 changes: 86 additions & 0 deletions cpp/memilio/epidemiology/lct_infection_state.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2020-2024 MEmilio
*
* Authors: Lena Ploetzke
*
* Contact: Martin J. Kuehn <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MIO_EPI_LCT_INFECTION_STATE_H
#define MIO_EPI_LCT_INFECTION_STATE_H

#include <array>

namespace mio
{
/**
* @brief Provides the functionality to be able to work with subcompartments in an LCT model.
*
* @tparam InfectionStates An enum class that defines the basic infection states.
* @tparam Ns Number of subcompartments for each infection state defined in InfectionState.
* The number of given template arguments must be equal to the entry Count from InfectionState.
*/
template <class InfectionStates, unsigned int... Ns>
class LctInfectionState
{
public:
using InfectionState = InfectionStates;
static_assert((unsigned int)InfectionState::Count == sizeof...(Ns),
"The number of integers provided as template parameters must be "
"the same as the entry Count of InfectionState.");

static_assert(((Ns > 0) && ...), "The number of subcompartments must be at least 1.");

/**
* @brief Gets the number of subcompartments in an infection state.
*
* @tparam State: Infection state for which the number of subcompartments should be returned.
* @return Number of subcompartments for State.
*/
template <InfectionState State>
static constexpr unsigned int get_num_subcompartments()
{
static_assert(State < InfectionState::Count, "State must be a a valid InfectionState.");
return m_subcompartment_numbers[(int)State];
}

/**
* @brief Gets the index of the first subcompartment of an infection state.
*
* In a simulation, the number of individuals in the subcompartments are stored in vectors.
* Accordingly, the index of the first subcompartment of State in such a vector is returned.
* @tparam State: Infection state for which the index should be returned.
* @return Index of the first subcompartment for a vector with one entry per subcompartment.
*/
template <InfectionState State>
static constexpr unsigned int get_first_index()
{
static_assert(State < InfectionState::Count, "State must be a a valid InfectionState.");
unsigned int index = 0;
for (int i = 0; i < (int)(State); i++) {
index = index + m_subcompartment_numbers[i];
}
return index;
}

static constexpr unsigned int Count{(... + Ns)};

private:
static constexpr const std::array<unsigned int, sizeof...(Ns)> m_subcompartment_numbers{
Ns...}; ///< Vector which defines the number of subcompartments for each infection state of InfectionState.
};

} // namespace mio

#endif
3 changes: 1 addition & 2 deletions cpp/models/lct_secir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ add_library(lct_secir
infection_state.h
model.h
model.cpp
simulation.h
simulation.cpp
simulation.h
parameters.h
)
target_link_libraries(lct_secir PUBLIC memilio)
Expand Down
10 changes: 5 additions & 5 deletions cpp/models/lct_secir/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ Below is an overview of the model architecture and its compartments.
| $\xi_{I_{Sy}}$ | `RiskOfInfectionFromSymptomatic` | Proportion of infected people with symptomps who are not isolated. |
| $N$ | `m_N0` | Total population. |
| $D$ | `D` | Number of death people. |
| $n_E$ | Defined via `InfectionState` | Number of subcompartments of the Exposed compartment. |
| $n_{NS}$ | Defined via `InfectionState` | Number of subcompartments of the InfectedNoSymptoms compartment. |
| $n_{Sy}$ | Defined via `InfectionState` | Number of subcompartments of the InfectedSymptoms compartment. |
| $n_{Sev}$ | Defined via `InfectionState` | Number of subcompartments of the InfectedSevere compartment.|
| $n_{Cr}$ | Defined via `InfectionState` | Number of subcompartments of the InfectedCritical compartment. |
| $n_E$ | `NumExposed` | Number of subcompartments of the Exposed compartment. |
| $n_{NS}$ | `NumInfectedNoSymptoms` | Number of subcompartments of the InfectedNoSymptoms compartment. |
| $n_{Sy}$ | `NumInfectedSymptoms` | Number of subcompartments of the InfectedSymptoms compartment. |
| $n_{Sev}$ |`NumInfectedSevere` | Number of subcompartments of the InfectedSevere compartment.|
| $n_{Cr}$ | `NumInfectedCritical` | Number of subcompartments of the InfectedCritical compartment. |
| $T_E$ | `TimeExposed` | Average time in days an individual stays in the Exposed compartment. |
| $T_{I_{NS}}$ | `TimeInfectedNoSymptoms` | Average time in days an individual stays in the InfectedNoSymptoms compartment. |
| $T_{I_{Sy}}$ | `TimeInfectedSymptoms` | Average time in days an individual stays in the InfectedSymptoms compartment. |
Expand Down
Loading

0 comments on commit afd4982

Please sign in to comment.