Skip to content

Commit 44980e8

Browse files
committed
Merge branch 'main' into 1522-improve-dynamicnpis-structure
2 parents b919146 + a0c1752 commit 44980e8

22 files changed

Lines changed: 1294 additions & 77 deletions

File tree

cpp/memilio/epidemiology/age_group.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,16 @@ namespace mio
2626
{
2727

2828
/**
29-
* @brief The AgeGroup struct is used as a dynamically
30-
* sized tag for all age dependent categories
29+
* @brief Typesafe index representing an age group.
30+
* Used as a tag for all age dependent categories in a model.
31+
* The number of age groups is determined at runtime.
32+
* The underlying size_t value (i.e., the age group index) can be obtained via the get() member function:
33+
* @code
34+
* AgeGroup g(2);
35+
* size_t idx = g.get();
36+
* @endcode
37+
* @see Index
38+
* @see TypeSafe
3139
*/
3240
struct AgeGroup : public Index<AgeGroup> {
3341
AgeGroup(size_t val)

cpp/memilio/epidemiology/damping.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,37 @@ namespace mio
3838
{
3939

4040
/**
41-
* integer damping level.
41+
* @brief Typesafe integer representing the level of a damping.
42+
* The underlying int value can be obtained via the get() member function:
43+
* @code
44+
* DampingLevel l(2);
45+
* int v = l.get();
46+
* @endcode
47+
* @see TypeSafe
4248
*/
4349
DECL_TYPESAFE(int, DampingLevel);
4450

4551
/**
46-
* integer damping type.
52+
* @brief Typesafe integer representing the type of a damping.
53+
* The underlying int value can be obtained via the get() member function:
54+
* @code
55+
* DampingType t(1);
56+
* int v = t.get();
57+
* @endcode
58+
* @see TypeSafe
4759
*/
4860
DECL_TYPESAFE(int, DampingType);
4961

5062
/**
51-
* double simulation time.
63+
* @brief Typesafe wrapper for a floating-point simulation time value (in days).
64+
* The underlying value can be obtained via the get() member function:
65+
* @code
66+
* SimulationTime<double> t(3.5);
67+
* double days = t.get();
68+
* @endcode
69+
* Supports arithmetic (+, -, *, /) and comparison operators.
70+
* @tparam FP Floating point type, e.g., double.
71+
* @see TypeSafe
5272
*/
5373
template <typename FP>
5474
class MEMILIO_ENABLE_EBO SimulationTime : public TypeSafe<FP, SimulationTime<FP>>,

cpp/memilio/geography/regions.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ struct Region : public mio::Index<Region> {
4242
};
4343

4444
/**
45-
* Id of a state.
45+
* @brief Typesafe id of a German state.
4646
* For Germany the Ids are:
4747
* 1 = Schleswig-Holstein
4848
* 2 = Hamburg
@@ -60,16 +60,37 @@ struct Region : public mio::Index<Region> {
6060
* 14 = Sachsen
6161
* 15 = Sachsen-Anhalt
6262
* 16 = Thüringen
63+
* The underlying int value can be obtained via the get() member function:
64+
* @code
65+
* StateId s(3);
66+
* int v = s.get();
67+
* @endcode
68+
* @see TypeSafe
6369
*/
6470
DECL_TYPESAFE(int, StateId);
6571

6672
/**
67-
* Id of a county.
73+
* @brief Typesafe id of a county.
6874
* Format ssxxx where ss is the id of the state that the county is in (first s may be 0) and xxx are other digits.
6975
* Ids are generally not consecutive, even within one state.
76+
* The underlying int value can be obtained via the get() member function:
77+
* @code
78+
* CountyId c(5315);
79+
* int v = c.get();
80+
* @endcode
81+
* @see TypeSafe
7082
*/
7183
DECL_TYPESAFE(int, CountyId);
7284

85+
/**
86+
* @brief Typesafe id of a district.
87+
* The underlying int value can be obtained via the get() member function:
88+
* @code
89+
* DistrictId d(9162);
90+
* int v = d.get();
91+
* @endcode
92+
* @see TypeSafe
93+
*/
7394
DECL_TYPESAFE(int, DistrictId);
7495

7596
/**

cpp/memilio/utils/index.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,20 @@ Index<T...> tuple_to_index(std::tuple<Index<T>...>);
9090
} // namespace details
9191

9292
/**
93-
* @brief An Index with a single template parameter is a typesafe wrapper for size_t
94-
* that is associated with a Tag. It is used to index into a CustomIndexArray
93+
* @brief Typesafe wrapper for a size_t index associated with a Tag type.
94+
* Used to index into a CustomIndexArray:
9595
*
96+
* @code
9697
* CustomIndexArray<Tag1, Tag2> a;
9798
* a[{Index<Tag1>(0), Index<Tag2>(14)}]
99+
* @endcode
98100
*
99101
* Will retrieve the element associated with the indices 0 and 14 for the Tags Tag1
100102
* and Tag2 respectively.
101103
*
102104
* Optionally, the tag can be derived from Index to shorten the notation:
103105
*
106+
* @code
104107
* struct Tag1 : public Index<Tag1>{
105108
* Tag1(size_t val) : Index<Tag1>(val) {}
106109
* };
@@ -109,9 +112,16 @@ Index<T...> tuple_to_index(std::tuple<Index<T>...>);
109112
* };
110113
* CustomIndexArray<Tag1, Tag2> a;
111114
* a[{Tag1(0), Tag2(14)}]
115+
* @endcode
112116
*
113-
* @tparam CategoryTag A tag for the typesafe index
117+
* The underlying size_t value can be obtained via the get() member function:
118+
* @code
119+
* Index<Tag1> i(3);
120+
* size_t v = i.get();
121+
* @endcode
114122
*
123+
* @tparam CategoryTag A tag type for the typesafe index.
124+
* @see TypeSafe
115125
*/
116126
template <typename CategoryTag>
117127
class MEMILIO_ENABLE_EBO Index<CategoryTag> : public TypeSafe<size_t, Index<CategoryTag>>,

cpp/memilio/utils/type_safe.h

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,32 @@ namespace mio
2727
{
2828

2929
/**
30-
* typesafe wrapper around any type to make function arguments, tuple elements, etc. easily distinguishable.
30+
* @brief Typesafe wrapper around any type to make function arguments, tuple elements, etc. easily distinguishable.
3131
* e.g.
32-
* class Foo : public Type<int, Foo>() {};
33-
* class Bar : public Type<int, Bar>() {};
32+
* @code
33+
* class Foo : public TypeSafe<int, Foo> { using TypeSafe::TypeSafe; };
34+
* class Bar : public TypeSafe<int, Bar> { using TypeSafe::TypeSafe; };
3435
* void work(Foo f, Bar b);
36+
* @endcode
3537
* instead of
38+
* @code
3639
* void work(int f, int b);
37-
* @tparam T type of the underlying value
38-
* @tparam Derived Concrete type derived from this type for CRTP.
40+
* @endcode
41+
*
42+
* The underlying value can be accessed via the get() member function or via an explicit cast:
43+
* @code
44+
* Foo f(5);
45+
* int v = f.get(); // preferred
46+
* int w = int(f); // also valid
47+
* @endcode
48+
*
49+
* Additional arithmetic and comparison operators can be mixed in by deriving from the
50+
* operator helper classes OperatorAdditionSubtraction, OperatorScalarMultiplicationDivision,
51+
* OperatorComparison, and OperatorIncrementDecrement.
52+
* Alternatively, use the DECL_TYPESAFE macro for a simple one-line declaration.
53+
*
54+
* @tparam T Type of the underlying value.
55+
* @tparam Derived Concrete type derived from this class (CRTP).
3956
*/
4057
template <class T, class Derived>
4158
class TypeSafe
@@ -57,12 +74,18 @@ class TypeSafe
5774
}
5875

5976
/**
60-
* conversion to underlying type.
77+
* Explicit conversion to the underlying type.
78+
* Prefer get() for clarity.
6179
*/
6280
explicit operator T() const
6381
{
6482
return m_t;
6583
}
84+
85+
/**
86+
* Returns the underlying value.
87+
* This is the preferred way to access the raw value of a TypeSafe wrapper.
88+
*/
6689
T get() const
6790
{
6891
return m_t;
@@ -223,7 +246,9 @@ class OperatorComparison
223246
};
224247

225248
/**
226-
* helper macro to declare a class that derives from TypeSafe.
249+
* @brief Helper macro to declare a simple typesafe wrapper struct around a given type.
250+
* The resulting struct derives from TypeSafe and provides access to the underlying value via get().
251+
* @see TypeSafe
227252
*/
228253
#define DECL_TYPESAFE(T, Name) \
229254
struct Name : public ::mio::TypeSafe<T, Name> { \

cpp/models/ode_secir/model.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,12 @@ class Model : public FlowModel<FP, InfectionState, Populations<FP, AgeGroup, Inf
196196
flows[this->template get_flat_flow_index<InfectionState::InfectedSevere, InfectionState::InfectedCritical>(
197197
{i})] = criticalPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevi];
198198
flows[this->template get_flat_flow_index<InfectionState::InfectedSevere, InfectionState::Recovered>({i})] =
199-
(1.0 - params.template get<CriticalPerSevere<FP>>()[i]) /
199+
(1.0 - params.template get<CriticalPerSevere<FP>>()[i] -
200+
params.template get<DeathsPerSevere<FP>>()[i]) /
200201
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevi];
201202
flows[this->template get_flat_flow_index<InfectionState::InfectedSevere, InfectionState::Dead>({i})] =
202-
deathsPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevi];
203+
(params.template get<DeathsPerSevere<FP>>()[i] + deathsPerSevereAdjusted) /
204+
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevi];
203205

204206
// InfectedCritical -> Dead / Recovered
205207
flows[this->template get_flat_flow_index<InfectionState::InfectedCritical, InfectionState::Dead>({i})] =

cpp/models/ode_secir/parameters.h

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,24 @@ struct CriticalPerSevere {
285285
}
286286
};
287287

288+
/**
289+
* @brief the percentage of dead patients per hospitalized patients.
290+
* This is a direct mortality probability from the InfectedSevere compartment,
291+
* independent of ICU capacity.
292+
*/
293+
template <typename FP>
294+
struct DeathsPerSevere {
295+
using Type = CustomIndexArray<UncertainValue<FP>, AgeGroup>;
296+
static Type get_default(AgeGroup size)
297+
{
298+
return Type(size, 0.);
299+
}
300+
static std::string name()
301+
{
302+
return "DeathsPerSevere";
303+
}
304+
};
305+
288306
/**
289307
* @brief the percentage of dead patients per ICU patients in the SECIR model
290308
*/
@@ -366,12 +384,14 @@ struct TestAndTraceCapacityMaxRisk {
366384
};
367385

368386
template <typename FP>
369-
using ParametersBase = ParameterSet<
370-
StartDay<FP>, Seasonality<FP>, ICUCapacity<FP>, TestAndTraceCapacity<FP>, TestAndTraceCapacityMaxRisk<FP>,
371-
ContactPatterns<FP>, DynamicNPIsInfectedSymptoms<FP>, TimeExposed<FP>, TimeInfectedNoSymptoms<FP>,
372-
TimeInfectedSymptoms<FP>, TimeInfectedSevere<FP>, TimeInfectedCritical<FP>, TransmissionProbabilityOnContact<FP>,
373-
RelativeTransmissionNoSymptoms<FP>, RecoveredPerInfectedNoSymptoms<FP>, RiskOfInfectionFromSymptomatic<FP>,
374-
MaxRiskOfInfectionFromSymptomatic<FP>, SeverePerInfectedSymptoms<FP>, CriticalPerSevere<FP>, DeathsPerCritical<FP>>;
387+
using ParametersBase =
388+
ParameterSet<StartDay<FP>, Seasonality<FP>, ICUCapacity<FP>, TestAndTraceCapacity<FP>,
389+
TestAndTraceCapacityMaxRisk<FP>, ContactPatterns<FP>, DynamicNPIsInfectedSymptoms<FP>, TimeExposed<FP>,
390+
TimeInfectedNoSymptoms<FP>, TimeInfectedSymptoms<FP>, TimeInfectedSevere<FP>, TimeInfectedCritical<FP>,
391+
TransmissionProbabilityOnContact<FP>, RelativeTransmissionNoSymptoms<FP>,
392+
RecoveredPerInfectedNoSymptoms<FP>, RiskOfInfectionFromSymptomatic<FP>,
393+
MaxRiskOfInfectionFromSymptomatic<FP>, SeverePerInfectedSymptoms<FP>, CriticalPerSevere<FP>,
394+
DeathsPerSevere<FP>, DeathsPerCritical<FP>>;
375395

376396
/**
377397
* @brief Parameters of an age-resolved SECIR/SECIHURD model.
@@ -568,6 +588,22 @@ class Parameters : public ParametersBase<FP>
568588
corrected = true;
569589
}
570590

591+
if (this->template get<DeathsPerSevere<FP>>()[i] < 0.0 ||
592+
this->template get<DeathsPerSevere<FP>>()[i] > 1.0) {
593+
log_warning("Constraint check: Parameter DeathsPerSevere changed from {} to {}",
594+
this->template get<DeathsPerSevere<FP>>()[i], 0);
595+
this->template get<DeathsPerSevere<FP>>()[i] = 0;
596+
corrected = true;
597+
}
598+
599+
if (this->template get<CriticalPerSevere<FP>>()[i] + this->template get<DeathsPerSevere<FP>>()[i] > 1.0) {
600+
log_warning("Constraint check: CriticalPerSevere + DeathsPerSevere exceed 1.0 for age group {}. "
601+
"DeathsPerSevere changed from {} to 0.",
602+
static_cast<size_t>(i), this->template get<DeathsPerSevere<FP>>()[i]);
603+
this->template get<DeathsPerSevere<FP>>()[i] = 0;
604+
corrected = true;
605+
}
606+
571607
if (this->template get<DeathsPerCritical<FP>>()[i] < 0.0 ||
572608
this->template get<DeathsPerCritical<FP>>()[i] > 1.0) {
573609
log_warning("Constraint check: Parameter DeathsPerCritical changed from {} to {}",
@@ -684,6 +720,18 @@ class Parameters : public ParametersBase<FP>
684720
return true;
685721
}
686722

723+
if (this->template get<DeathsPerSevere<FP>>()[i] < 0.0 ||
724+
this->template get<DeathsPerSevere<FP>>()[i] > 1.0) {
725+
log_error("Constraint check: Parameter DeathsPerSevere smaller {} or larger {}", 0, 1);
726+
return true;
727+
}
728+
729+
if (this->template get<CriticalPerSevere<FP>>()[i] + this->template get<DeathsPerSevere<FP>>()[i] > 1.0) {
730+
log_error("Constraint check: CriticalPerSevere + DeathsPerSevere exceed 1.0 for age group {}.",
731+
static_cast<size_t>(i));
732+
return true;
733+
}
734+
687735
if (this->template get<DeathsPerCritical<FP>>()[i] < 0.0 ||
688736
this->template get<DeathsPerCritical<FP>>()[i] > 1.0) {
689737
log_error("Constraint check: Parameter DeathsPerCritical smaller {} or larger {}", 0, 1);

cpp/models/ode_secirts/model.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -386,11 +386,12 @@ class Model
386386

387387
flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive,
388388
InfectionState::TemporaryImmunePartialImmunity>({i})] =
389-
(1 - params.template get<CriticalPerSevere<FP>>()[i]) /
389+
(1 - params.template get<CriticalPerSevere<FP>>()[i] - params.template get<DeathsPerSevere<FP>>()[i]) /
390390
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
391391

392392
flows[this->template get_flat_flow_index<InfectionState::InfectedSevereNaive, InfectionState::DeadNaive>(
393-
{i})] = deathsPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
393+
{i})] = (params.template get<DeathsPerSevere<FP>>()[i] + deathsPerSevereAdjusted) /
394+
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevNi];
394395
// InfectedCritical
395396
flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalNaive, InfectionState::DeadNaive>(
396397
{i})] = params.template get<DeathsPerCritical<FP>>()[i] /
@@ -472,13 +473,15 @@ class Model
472473
flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
473474
InfectionState::TemporaryImmuneImprovedImmunity>({i})] =
474475
(1 - (reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
475-
params.template get<CriticalPerSevere<FP>>()[i]) /
476+
(params.template get<CriticalPerSevere<FP>>()[i] +
477+
params.template get<DeathsPerSevere<FP>>()[i])) /
476478
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
477479

478480
flows[this->template get_flat_flow_index<InfectionState::InfectedSeverePartialImmunity,
479481
InfectionState::DeadPartialImmunity>({i})] =
480482
(reducInfectedSevereCriticalDeadPartialImmunity / reducInfectedSevereCriticalDeadPartialImmunity) *
481-
deathsPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
483+
(params.template get<DeathsPerSevere<FP>>()[i] + deathsPerSevereAdjusted) /
484+
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevPIi];
482485
// InfectedCritical
483486
flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalPartialImmunity,
484487
InfectionState::DeadPartialImmunity>({i})] =
@@ -555,13 +558,15 @@ class Model
555558
InfectionState::TemporaryImmuneImprovedImmunity>({i})] =
556559
(1 -
557560
(reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
558-
params.template get<CriticalPerSevere<FP>>()[i]) /
561+
(params.template get<CriticalPerSevere<FP>>()[i] +
562+
params.template get<DeathsPerSevere<FP>>()[i])) /
559563
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
560564

561565
flows[this->template get_flat_flow_index<InfectionState::InfectedSevereImprovedImmunity,
562566
InfectionState::DeadImprovedImmunity>({i})] =
563567
(reducInfectedSevereCriticalDeadImprovedImmunity / reducInfectedSevereCriticalDeadImprovedImmunity) *
564-
deathsPerSevereAdjusted / params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
568+
(params.template get<DeathsPerSevere<FP>>()[i] + deathsPerSevereAdjusted) /
569+
params.template get<TimeInfectedSevere<FP>>()[i] * y[ISevIIi];
565570

566571
// InfectedCritical
567572
flows[this->template get_flat_flow_index<InfectionState::InfectedCriticalImprovedImmunity,

0 commit comments

Comments
 (0)