Skip to content

Conversation

an-jung
Copy link
Collaborator

@an-jung an-jung commented Aug 7, 2025

Changes and Information

Please briefly list the changes (main added features, changed items, or corrected bugs) made:

  • added LCT SECIR Model for 2 DISEASES on the basis of the LCT SECIR model

If need be, add additional information and what the reviewer should look out for in particular:

Merge Request - Guideline Checklist

Please check our git workflow. Use the draft feature if the Pull Request is not yet ready to review.

Checks by code author

  • Every addressed issue is linked (use the "Closes #ISSUE" keyword below)
  • New code adheres to coding guidelines
  • No large data files have been added (files should in sum not exceed 100 KB, avoid PDFs, Word docs, etc.)
  • Tests are added for new functionality and a local test run was successful (with and without OpenMP)
  • Appropriate documentation within the code (Doxygen) for new functionality has been added in the code
  • Appropriate external documentation (ReadTheDocs) for new functionality has been added to the online documentation
  • Proper attention to licenses, especially no new third-party software with conflicting license has been added
  • (For ABM development) Checked benchmark results and ran and posted a local test above from before and after development to ensure performance is monitored.

Checks by code reviewer(s)

  • Corresponding issue(s) is/are linked and addressed
  • Code is clean of development artifacts (no deactivated or commented code lines, no debugging printouts, etc.)
  • Appropriate unit tests have been added, CI passes, code coverage and performance is acceptable (did not decrease)
  • No large data files added in the whole history of commits(files should in sum not exceed 100 KB, avoid PDFs, Word docs, etc.)
  • On merge, add 2-5 lines with the changes (main added features, changed items, or corrected bugs) to the merge-commit-message. This can be taken from the briefly-list-the-changes above (best case) or the separate commit messages (worst case).

Closes #1294

@an-jung an-jung linked an issue Aug 7, 2025 that may be closed by this pull request
2 tasks
@an-jung an-jung added loc::backend This issue concerns the C++ backend implementation. model::lct Models using the linear chain trick labels Aug 7, 2025
@an-jung an-jung marked this pull request as ready for review August 7, 2025 12:00
Copy link

codecov bot commented Aug 7, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.36%. Comparing base (0b61820) to head (ab75b17).
⚠️ Report is 10 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1340      +/-   ##
==========================================
+ Coverage   97.29%   97.36%   +0.06%     
==========================================
  Files         174      176       +2     
  Lines       15105    15675     +570     
==========================================
+ Hits        14697    15262     +565     
- Misses        408      413       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.


int main()
{
// Simple example to demonstrate how to run a simulation using an LCT-SECIR model.
Copy link
Member

Choose a reason for hiding this comment

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

If the changes in this file have don't have a specific reason, I would revert them such that the example corresponds to the main.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i have done this

NumInfectedSevere_2b = 2, NumInfectedCritical_2b = 1;
using InfState = mio::lsecir2d::InfectionState;
using LctState = mio::LctInfectionState<
InfState, 1, NumExposed_1a, NumInfectedNoSymptoms_1a, NumInfectedSymptoms_1a, NumInfectedSevere_1a,
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a comment on what the numbers in the templates are or define them as variables with corresponding names before?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

and i have done this

ScalarType tmax = 10;

// Set Parameters.
model.parameters.get<mio::lsecir2d::TimeExposed_a>()[0] = 3.;
Copy link
Member

@jubicker jubicker Aug 28, 2025

Choose a reason for hiding this comment

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

I'm not sure whether the naming of the parameters and also of infection states is the best choice. Maybe we should use longer but more self-speaking names, like TimeExposedDisease1,... and for infection states something like: InfectedNoSymptomsDisease1Naive and InfectedNoSymptomsDisease1RecoveredDisease2? Or is that too long? @annawendler what do you think?

EXPECT_NEAR(result.get_last_time(), tmax, 1e-10);
ScalarType sum_pop = init.sum();
for (Eigen::Index i = 0; i < result.get_num_time_points(); i++) {
EXPECT_NEAR(sum_pop, result[i].sum(), 1e-5); // check that total pop. is constant
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
EXPECT_NEAR(sum_pop, result[i].sum(), 1e-5); // check that total pop. is constant
EXPECT_NEAR(sum_pop, result[i].sum(), 1e-5); // check that total pop is constant


// Initialization vector for lct2d model.
Eigen::VectorX<ScalarType> init_lct2d = Eigen::VectorX<ScalarType>::Constant((Eigen::Index)InfState2d::Count, 0);
init_lct2d[0] = 200; // lct and lct2d use different infection states
Copy link
Member

Choose a reason for hiding this comment

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

This comment is a bit confusing because below you initialize the exactly same values for the normal LCT model


// Run the model with more than one subcompartment for states E,I,C,H,U
// and calculate the TimeSeries with no subcompartments from the result
TEST(TestLCTSecir2d, testSubcompartments)
Copy link
Member

Choose a reason for hiding this comment

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

The only thing this test does is checking whether the simulate function advances until tmax which is tested already in simulateDefault. What was the intention of this test? If it was to check whether the calculate_compartment method works, then test whether the Exposed value of this timeseries at t=0 corresponds to the sum of the Exposed compartments of the other timeseries at t=0.

| The LCT-SECIR-2-DISEASES model is an extension of the :doc:`model with one disease <lsecir>`.
| The model is ODE-based and uses the Linear Chain Trick to allow for more general Erlang distributed stay times in each compartment instead of just exponentially distributed stay times induced by basic ODE-based models.
| With the SECIR structure the model is particularly suited for pathogens with pre- or asymptomatic infection states and when severe or critical states are possible.
| For the two diseases :math:`a` and :math:`b` the model assumes no co-infection, a certain independence in the sense that prior infection with one disease does not affect the infection with the other disease (e.g. probability to get infected, time spend in each state, chances of recovery etc.), and perfect immunity after recovery for both diseases.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
| For the two diseases :math:`a` and :math:`b` the model assumes no co-infection, a certain independence in the sense that prior infection with one disease does not affect the infection with the other disease (e.g. probability to get infected, time spend in each state, chances of recovery etc.), and perfect immunity after recovery for both diseases.
| For the two diseases or variants of one disease :math:`a` and :math:`b` the model assumes no co-infection, a certain independence in the sense that prior infection with one disease does not affect the infection with the other disease (e.g. probability to get infected, time spend in each state, chances of recovery etc.), and perfect immunity after recovery for both diseases.

Each infection is simulated using a LCT-SECIR model. The states are labeled according to infection (first or second) and disease (:math:`a` or :math:`b`),
so the full model is given by the combination of infections :math:`1a`, :math:`2a`, :math:`1b`, and :math:`2b`.

Below is a visualization of the infection states split into LCT-states and transitions without a stratification according to socisdemographic groups.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Below is a visualization of the infection states split into LCT-states and transitions without a stratification according to socisdemographic groups.
Below is a visualization of the infection states split into LCT-states and transitions without a stratification according to sociodemographic groups.

Copy link
Member

@annawendler annawendler left a comment

Choose a reason for hiding this comment

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

First part of review


## Examples

A simple example can be found at [LCT2D minimal example](../../examples/lct_secir_2_diseases.cpp).
Copy link
Member

Choose a reason for hiding this comment

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

In a recent commit to the main we updated the docu of memilio. In this commit we changed the structure of the README files of the models. Now, we only refer to the docu and to an example, see https://github.com/SciCompMod/memilio/blob/main/cpp/models/lct_secir/README.md. Can you adapt this? Can you also capitalize Linear Chain Trick in the README file of the LCT SECIR model?

InfectedCritical_2bToDead_b = 38,
InfectedCritical_2bToRecovered_ab = 39,
Count = 40
};
Copy link
Member

Choose a reason for hiding this comment

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

Do you use the transitions somewhere? I think in the LCT SECIR model they are only used in the initializer_flows file for the initialization. If the transitions are not currently used, I would remove them to avoid confusion.

**********************************************************/

/**
* @brief Average time spent in the Exposed compartment for disease a in day unit.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* @brief Average time spent in the Exposed compartment for disease a in day unit.
* @brief Average time spent in the Exposed compartment for disease a in day unit for each group.

Same for the other parameters.


/**
* @brief Average time spent in the TimeInfectedNoSymptoms before developing
* symptoms or recover for disease b in day unit.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* symptoms or recover for disease b in day unit.
* symptoms or recovering for disease b in day unit.

Same for other parameters.

};

/**
* @brief The start day in the LCT-SECIR-2-DISEASES model.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* @brief The start day in the LCT-SECIR-2-DISEASES model.
* @brief The start day in the LCT-SECIR-2-DISEASES model.

*/
struct TimeExposed_a {
using Type = Eigen::VectorX<UncertainValue<ScalarType>>;
static Type get_default(size_t size = 1)
Copy link
Member

Choose a reason for hiding this comment

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

Is the default ever used? Below when constructing a Parameters object, you have num_groups as input argument. Isn't the default always overridden then?

Copy link
Member

@annawendler annawendler left a comment

Choose a reason for hiding this comment

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

In a recent commit the use of floating point types was reworked to allow for automatic differentiation. This also affected the LCT-SECIR model, e.g. we now have an additional template parameter FP for the model class. Can you adapt your model accordingly so that the models are consistent? If you have any questions just let us know :)

/*
* Copyright (C) 2020-2025 MEmilio
*
* Authors: Annika Jungkalus, Lena Ploetzke
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* Authors: Annika Jungkalus, Lena Ploetzke
* Authors: Annika Jungklaus, Lena Ploetzke

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
loc::backend This issue concerns the C++ backend implementation. model::lct Models using the linear chain trick
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add LCT SECIR model with two disease strains
3 participants