LCOV - code coverage report
Current view: top level - models/ide_secir - model.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 237 237 100.0 %
Date: 2024-11-18 12:45:26 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /* 
       2             : * Copyright (C) 2020-2024 MEmilio
       3             : *
       4             : * Authors: Anna Wendler, Lena Ploetzke, Martin J. Kuehn
       5             : *
       6             : * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
       7             : *
       8             : * Licensed under the Apache License, Version 2.0 (the "License");
       9             : * you may not use this file except in compliance with the License.
      10             : * You may obtain a copy of the License at
      11             : *
      12             : *     http://www.apache.org/licenses/LICENSE-2.0
      13             : *
      14             : * Unless required by applicable law or agreed to in writing, software
      15             : * distributed under the License is distributed on an "AS IS" BASIS,
      16             : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      17             : * See the License for the specific language governing permissions and
      18             : * limitations under the License.
      19             : */
      20             : #include "ide_secir/model.h"
      21             : #include "ide_secir/parameters.h"
      22             : #include "infection_state.h"
      23             : #include "memilio/config.h"
      24             : #include "memilio/utils/logging.h"
      25             : #include "memilio/math/eigen.h"
      26             : 
      27             : #include "vector"
      28             : 
      29             : namespace mio
      30             : {
      31             : namespace isecir
      32             : {
      33             : 
      34          20 : Model::Model(TimeSeries<ScalarType>&& init, ScalarType N_init, ScalarType deaths, ScalarType total_confirmed_cases,
      35          20 :              const ParameterSet& Parameterset_init)
      36          20 :     : parameters{Parameterset_init}
      37          20 :     , m_transitions{std::move(init)}
      38          20 :     , m_populations{TimeSeries<ScalarType>(Eigen::Index(InfectionState::Count))}
      39          20 :     , m_total_confirmed_cases{total_confirmed_cases}
      40          20 :     , m_N{N_init}
      41             : {
      42          20 :     if (m_transitions.get_num_time_points() > 0) {
      43             :         // Add first time point in m_populations according to last time point in m_transitions which is where we start
      44             :         // the simulation.
      45          57 :         m_populations.add_time_point<Eigen::VectorXd>(
      46          57 :             m_transitions.get_last_time(), TimeSeries<ScalarType>::Vector::Constant((int)InfectionState::Count, 0.));
      47             :     }
      48             :     else {
      49             :         // Initialize m_populations with zero as the first point of time if no data is provided for the transitions.
      50             :         // This can happen for example in the case of initialization with real data.
      51           2 :         m_populations.add_time_point<Eigen::VectorXd>(
      52           2 :             0, TimeSeries<ScalarType>::Vector::Constant((int)InfectionState::Count, 0));
      53             :     }
      54             : 
      55             :     // Set deaths at simulation start time t0.
      56          20 :     m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)] = deaths;
      57          20 : }
      58             : 
      59             : // ---- Functionality to calculate the sizes of the compartments for time t0. ----
      60          70 : void Model::compute_compartment_from_flows(ScalarType dt, Eigen::Index idx_InfectionState,
      61             :                                            Eigen::Index idx_IncomingFlow, int idx_TransitionDistribution1,
      62             :                                            int idx_TransitionDistribution2)
      63             : {
      64          70 :     ScalarType sum       = 0;
      65          70 :     ScalarType calc_time = 0;
      66             :     // Determine relevant calculation area and corresponding index.
      67          70 :     if ((1 - parameters.get<TransitionProbabilities>()[idx_TransitionDistribution1]) > 0) {
      68          47 :         calc_time = std::max(m_transitiondistributions_support_max[idx_TransitionDistribution1],
      69          47 :                              m_transitiondistributions_support_max[idx_TransitionDistribution2]);
      70             :     }
      71             :     else {
      72          23 :         calc_time = m_transitiondistributions_support_max[idx_TransitionDistribution1];
      73             :     }
      74             : 
      75          70 :     Eigen::Index calc_time_index = (Eigen::Index)std::ceil(calc_time / dt) - 1;
      76             : 
      77          70 :     Eigen::Index num_time_points = m_transitions.get_num_time_points();
      78             : 
      79         167 :     for (Eigen::Index i = num_time_points - 1 - calc_time_index; i < num_time_points - 1; i++) {
      80             : 
      81          97 :         ScalarType state_age = (num_time_points - 1 - i) * dt;
      82             : 
      83          97 :         sum += (parameters.get<TransitionProbabilities>()[idx_TransitionDistribution1] *
      84          97 :                     parameters.get<TransitionDistributions>()[idx_TransitionDistribution1].eval(state_age) +
      85          97 :                 (1 - parameters.get<TransitionProbabilities>()[idx_TransitionDistribution1]) *
      86          97 :                     parameters.get<TransitionDistributions>()[idx_TransitionDistribution2].eval(state_age)) *
      87          97 :                m_transitions[i + 1][idx_IncomingFlow];
      88             :     }
      89             : 
      90          70 :     m_populations.get_last_value()[idx_InfectionState] = sum;
      91          70 : }
      92             : 
      93          14 : void Model::initial_compute_compartments_infection(ScalarType dt)
      94             : {
      95             :     // Exposed
      96          14 :     compute_compartment_from_flows(dt, Eigen::Index(InfectionState::Exposed),
      97             :                                    Eigen::Index(InfectionTransition::SusceptibleToExposed),
      98             :                                    (int)InfectionTransition::ExposedToInfectedNoSymptoms);
      99             :     // InfectedNoSymptoms
     100          14 :     compute_compartment_from_flows(dt, Eigen::Index(InfectionState::InfectedNoSymptoms),
     101             :                                    Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms),
     102             :                                    (int)InfectionTransition::InfectedNoSymptomsToInfectedSymptoms,
     103             :                                    (int)InfectionTransition::InfectedNoSymptomsToRecovered);
     104             :     // InfectedSymptoms
     105          14 :     compute_compartment_from_flows(dt, Eigen::Index(InfectionState::InfectedSymptoms),
     106             :                                    Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms),
     107             :                                    (int)InfectionTransition::InfectedSymptomsToInfectedSevere,
     108             :                                    (int)InfectionTransition::InfectedSymptomsToRecovered);
     109             :     // InfectedSevere
     110          14 :     compute_compartment_from_flows(dt, Eigen::Index(InfectionState::InfectedSevere),
     111             :                                    Eigen::Index(InfectionTransition::InfectedSymptomsToInfectedSevere),
     112             :                                    (int)InfectionTransition::InfectedSevereToInfectedCritical,
     113             :                                    (int)InfectionTransition::InfectedSevereToRecovered);
     114             :     // InfectedCritical
     115          14 :     compute_compartment_from_flows(dt, Eigen::Index(InfectionState::InfectedCritical),
     116             :                                    Eigen::Index(InfectionTransition::InfectedSevereToInfectedCritical),
     117             :                                    (int)InfectionTransition::InfectedCriticalToDead,
     118             :                                    (int)InfectionTransition::InfectedCriticalToRecovered);
     119          14 : }
     120             : 
     121          14 : void Model::initial_compute_compartments(ScalarType dt)
     122             : {
     123             :     // The initialization method only affects the Susceptible and Recovered compartments.
     124             :     // It is possible to calculate the sizes of the other compartments in advance because only the initial values of
     125             :     // the flows are used.
     126          14 :     initial_compute_compartments_infection(dt);
     127             : 
     128          14 :     if (m_total_confirmed_cases > 1e-12) {
     129           1 :         m_initialization_method = 1;
     130             : 
     131             :         // The scheme of the ODE model for initialization is applied here.
     132           1 :         m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] =
     133           3 :             m_total_confirmed_cases - m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSymptoms)] -
     134           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSevere)] -
     135           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedCritical)] -
     136           2 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)];
     137             : 
     138           1 :         m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] =
     139           3 :             m_N - m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Exposed)] -
     140           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedNoSymptoms)] -
     141           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSymptoms)] -
     142           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSevere)] -
     143           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedCritical)] -
     144           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] -
     145           2 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)];
     146             :     }
     147          13 :     else if (m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] > 1e-12) {
     148             :         // Take initialized value for Susceptibles if value can't be calculated via the standard formula.
     149           1 :         m_initialization_method = 2;
     150             : 
     151             :         // R; need an initial value for R, therefore do not calculate via compute_recovered()
     152           1 :         m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] =
     153           3 :             m_N - m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] -
     154           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Exposed)] -
     155           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedNoSymptoms)] -
     156           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSymptoms)] -
     157           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSevere)] -
     158           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedCritical)] -
     159           2 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)];
     160             :     }
     161          12 :     else if (m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] > 1e-12) {
     162             :         // If value for Recovered is initialized and standard method is not applicable (i.e., no value for total
     163             :         // infections or Susceptibles given directly), calculate Susceptibles via other compartments.
     164           1 :         m_initialization_method = 3;
     165             : 
     166           1 :         m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] =
     167           3 :             m_N - m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Exposed)] -
     168           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedNoSymptoms)] -
     169           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSymptoms)] -
     170           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSevere)] -
     171           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedCritical)] -
     172           3 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] -
     173           2 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)];
     174             :     }
     175             :     else {
     176             :         // Compute Susceptibles at t0 and m_forceofinfection at time t0-dt as initial values for discretization scheme.
     177             :         // Use m_forceofinfection at t0-dt to be consistent with further calculations of S (see compute_susceptibles()),
     178             :         // where also the value of m_forceofinfection for the previous timestep is used.
     179          11 :         compute_forceofinfection(dt, true);
     180          11 :         if (m_forceofinfection > 1e-12) {
     181           9 :             m_initialization_method = 4;
     182             : 
     183             :             /* Attention: With an inappropriate combination of parameters and initial conditions, it can happen that S 
     184             :             becomes greater than N when this formula is used. In this case, at least one compartment is initialized 
     185             :             with a number less than zero, so that a log_error is thrown.
     186             :             However, this initialization method is consistent with the numerical solver of the model equations,
     187             :             so it may sometimes make sense to use this method. */
     188          18 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] =
     189          18 :                 m_transitions.get_last_value()[Eigen::Index(InfectionTransition::SusceptibleToExposed)] /
     190           9 :                 (dt * m_forceofinfection);
     191             : 
     192             :             // Recovered; calculated as the difference between the total population and the sum of the other
     193             :             // compartment sizes.
     194           9 :             m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] =
     195          27 :                 m_N - m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] -
     196          27 :                 m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Exposed)] -
     197          27 :                 m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedNoSymptoms)] -
     198          27 :                 m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSymptoms)] -
     199          27 :                 m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedSevere)] -
     200          27 :                 m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::InfectedCritical)] -
     201          18 :                 m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)];
     202             :         }
     203             :         else {
     204           2 :             m_initialization_method = -1;
     205           4 :             log_error("Error occured while initializing compartments: Force of infection is evaluated to 0 and neither "
     206             :                       "Susceptibles nor Recovered or total confirmed cases for time 0 were set. One of them should be "
     207             :                       "larger 0.");
     208             :         }
     209             :     }
     210             : 
     211             :     // Verify that the initialization produces an appropriate result.
     212             :     // Another check would be if the sum of the compartments is equal to N, but in all initialization methods one of
     213             :     // the compartments is initialized via N - the others.
     214             :     // This also means that if a compartment is greater than N, we will always have one or more compartments less than
     215             :     // zero.
     216             :     // Check if all compartments are non negative.
     217         126 :     for (int i = 0; i < (int)InfectionState::Count; i++) {
     218         112 :         if (m_populations[0][i] < 0) {
     219           1 :             m_initialization_method = -2;
     220           2 :             log_error("Initialization failed. One or more initial values for populations are less than zero. It may "
     221             :                       "help to use a different method for initialization.");
     222             :         }
     223             :     }
     224             : 
     225             :     // Compute m_forceofinfection at time t0 needed for further simulation.
     226          14 :     compute_forceofinfection(dt);
     227          14 : }
     228             : 
     229             : // ---- Functionality for the iterations of a simulation. ----
     230         103 : void Model::compute_susceptibles(ScalarType dt)
     231             : {
     232         103 :     Eigen::Index num_time_points = m_populations.get_num_time_points();
     233             :     // Using number of Susceptibles from previous time step and force of infection from previous time step:
     234             :     // Compute current number of Susceptibles and store Susceptibles in m_populations.
     235         206 :     m_populations.get_last_value()[Eigen::Index(InfectionState::Susceptible)] =
     236         206 :         m_populations[num_time_points - 2][Eigen::Index(InfectionState::Susceptible)] / (1 + dt * m_forceofinfection);
     237         103 : }
     238             : 
     239        1021 : void Model::compute_flow(Eigen::Index idx_InfectionTransitions, Eigen::Index idx_IncomingFlow, ScalarType dt,
     240             :                          Eigen::Index current_time_index)
     241             : {
     242        1021 :     ScalarType sum = 0;
     243             :     /* In order to satisfy TransitionDistribution(dt*i) = 0 for all i >= k, k is determined by the maximum support of 
     244             :     the distribution.
     245             :     Since we are using a backwards difference scheme to compute the derivative, we have that the
     246             :     derivative of TransitionDistribution(dt*i) = 0 for all i >= k+1.
     247             : 
     248             :     Hence calc_time_index goes until std::ceil(support_max/dt) since for std::ceil(support_max/dt)+1 all terms are 
     249             :     already zero. 
     250             :     This needs to be adjusted if we are changing the finite difference scheme */
     251             : 
     252             :     Eigen::Index calc_time_index =
     253        1021 :         (Eigen::Index)std::ceil(m_transitiondistributions_support_max[idx_InfectionTransitions] / dt);
     254             : 
     255        4491 :     for (Eigen::Index i = current_time_index - calc_time_index; i < current_time_index; i++) {
     256             :         // (current_time_index - i)  is the index corresponding to time the individuals have already spent in this state.
     257        3470 :         sum += m_transitiondistributions_derivative[idx_InfectionTransitions][current_time_index - i] *
     258        3470 :                m_transitions[i + 1][idx_IncomingFlow];
     259             :     }
     260             : 
     261        1021 :     m_transitions.get_value(current_time_index)[Eigen::Index(idx_InfectionTransitions)] =
     262        1021 :         (-dt) * parameters.get<TransitionProbabilities>()[idx_InfectionTransitions] * sum;
     263        1021 : }
     264             : 
     265         927 : void Model::compute_flow(Eigen::Index idx_InfectionTransitions, Eigen::Index idx_IncomingFlow, ScalarType dt)
     266             : {
     267         927 :     Eigen::Index current_time_index = m_transitions.get_num_time_points() - 1;
     268         927 :     compute_flow(idx_InfectionTransitions, idx_IncomingFlow, dt, current_time_index);
     269         927 : }
     270             : 
     271         103 : void Model::flows_current_timestep(ScalarType dt)
     272             : {
     273             :     // Calculate flow SusceptibleToExposed with force of infection from previous time step and Susceptibles from
     274             :     // current time step.
     275         103 :     m_transitions.get_last_value()[Eigen::Index(InfectionTransition::SusceptibleToExposed)] =
     276         206 :         dt * m_forceofinfection * m_populations.get_last_value()[Eigen::Index(InfectionState::Susceptible)];
     277             : 
     278             :     // Calculate all other flows with compute_flow.
     279             :     // Flow from Exposed to InfectedNoSymptoms
     280         103 :     compute_flow(Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms),
     281             :                  Eigen::Index(InfectionTransition::SusceptibleToExposed), dt);
     282             :     // Flow from InfectedNoSymptoms to InfectedSymptoms
     283         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms),
     284             :                  Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms), dt);
     285             :     // Flow from InfectedNoSymptoms to Recovered
     286         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedNoSymptomsToRecovered),
     287             :                  Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms), dt);
     288             :     // Flow from InfectedSymptoms to InfectedSevere
     289         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedSymptomsToInfectedSevere),
     290             :                  Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms), dt);
     291             :     // Flow from InfectedSymptoms to Recovered
     292         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedSymptomsToRecovered),
     293             :                  Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms), dt);
     294             :     // Flow from InfectedSevere to InfectedCritical
     295         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedSevereToInfectedCritical),
     296             :                  Eigen::Index(InfectionTransition::InfectedSymptomsToInfectedSevere), dt);
     297             :     // Flow from InfectedSevere to Recovered
     298         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedSevereToRecovered),
     299             :                  Eigen::Index(InfectionTransition::InfectedSymptomsToInfectedSevere), dt);
     300             :     // Flow from InfectedCritical to Dead
     301         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedCriticalToDead),
     302             :                  Eigen::Index(InfectionTransition::InfectedSevereToInfectedCritical), dt);
     303             :     // Flow from InfectedCritical to Recovered
     304         103 :     compute_flow(Eigen::Index(InfectionTransition::InfectedCriticalToRecovered),
     305             :                  Eigen::Index(InfectionTransition::InfectedSevereToInfectedCritical), dt);
     306         103 : }
     307             : 
     308         103 : void Model::update_compartments()
     309             : {
     310             :     // Exposed
     311         103 :     update_compartment_from_flow(InfectionState::Exposed, {InfectionTransition::SusceptibleToExposed},
     312             :                                  {InfectionTransition::ExposedToInfectedNoSymptoms});
     313             : 
     314             :     // InfectedNoSymptoms
     315         103 :     update_compartment_from_flow(InfectionState::InfectedNoSymptoms, {InfectionTransition::ExposedToInfectedNoSymptoms},
     316             :                                  {InfectionTransition::InfectedNoSymptomsToInfectedSymptoms,
     317             :                                   InfectionTransition::InfectedNoSymptomsToRecovered});
     318             : 
     319             :     // InfectedSymptoms
     320         103 :     update_compartment_from_flow(
     321             :         InfectionState::InfectedSymptoms, {InfectionTransition::InfectedNoSymptomsToInfectedSymptoms},
     322             :         {InfectionTransition::InfectedSymptomsToInfectedSevere, InfectionTransition::InfectedSymptomsToRecovered});
     323             : 
     324             :     // InfectedSevere
     325         103 :     update_compartment_from_flow(
     326             :         InfectionState::InfectedSevere, {InfectionTransition::InfectedSymptomsToInfectedSevere},
     327             :         {InfectionTransition::InfectedSevereToInfectedCritical, InfectionTransition::InfectedSevereToRecovered});
     328             : 
     329             :     // InfectedCritical
     330         103 :     update_compartment_from_flow(
     331             :         InfectionState::InfectedCritical, {InfectionTransition::InfectedSevereToInfectedCritical},
     332             :         {InfectionTransition::InfectedCriticalToDead, InfectionTransition::InfectedCriticalToRecovered});
     333             : 
     334             :     // Recovered
     335         206 :     update_compartment_from_flow(
     336             :         InfectionState::Recovered,
     337             :         {InfectionTransition::InfectedNoSymptomsToRecovered, InfectionTransition::InfectedSymptomsToRecovered,
     338             :          InfectionTransition::InfectedSevereToRecovered, InfectionTransition::InfectedCriticalToRecovered},
     339         206 :         std::vector<InfectionTransition>());
     340             : 
     341             :     // Dead
     342         206 :     update_compartment_from_flow(InfectionState::Dead, {InfectionTransition::InfectedCriticalToDead},
     343         206 :                                  std::vector<InfectionTransition>());
     344         103 : }
     345             : 
     346         721 : void Model::update_compartment_from_flow(InfectionState infectionState,
     347             :                                          std::vector<InfectionTransition> const& IncomingFlows,
     348             :                                          std::vector<InfectionTransition> const& OutgoingFlows)
     349             : {
     350         721 :     Eigen::Index num_time_points   = m_populations.get_num_time_points();
     351         721 :     ScalarType updated_compartment = m_populations[num_time_points - 2][Eigen::Index(infectionState)];
     352        1751 :     for (const InfectionTransition& inflow : IncomingFlows) {
     353        1030 :         updated_compartment += m_transitions.get_last_value()[Eigen::Index(inflow)];
     354             :     }
     355        1648 :     for (const InfectionTransition& outflow : OutgoingFlows) {
     356         927 :         updated_compartment -= m_transitions.get_last_value()[Eigen::Index(outflow)];
     357             :     }
     358         721 :     m_populations.get_last_value()[Eigen::Index(infectionState)] = updated_compartment;
     359         721 : }
     360             : 
     361         128 : void Model::compute_forceofinfection(ScalarType dt, bool initialization)
     362             : {
     363         128 :     m_forceofinfection = 0;
     364             : 
     365             :     // Determine the relevant calculation area = union of the supports of the relevant transition distributions.
     366             :     ScalarType calc_time =
     367         512 :         std::max({m_transitiondistributions_support_max[(int)InfectionTransition::InfectedNoSymptomsToInfectedSymptoms],
     368         128 :                   m_transitiondistributions_support_max[(int)InfectionTransition::InfectedNoSymptomsToRecovered],
     369         128 :                   m_transitiondistributions_support_max[(int)InfectionTransition::InfectedSymptomsToInfectedSevere],
     370         128 :                   m_transitiondistributions_support_max[(int)InfectionTransition::InfectedSymptomsToRecovered]});
     371             : 
     372             :     // Corresponding index.
     373             :     // Need calc_time_index timesteps in sum,
     374             :     // subtract 1 because in the last summand all TransitionDistributions evaluate to 0 (by definition of support_max).
     375         128 :     Eigen::Index calc_time_index = (Eigen::Index)std::ceil(calc_time / dt) - 1;
     376             : 
     377             :     Eigen::Index num_time_points;
     378             :     ScalarType current_time;
     379             :     ScalarType deaths;
     380             : 
     381         128 :     if (initialization) {
     382             :         // Determine m_forceofinfection at time t0-dt which is the penultimate timepoint in m_transitions.
     383          11 :         num_time_points = m_transitions.get_num_time_points() - 1;
     384             :         // Get time of penultimate timepoint in m_transitions.
     385          11 :         current_time = m_transitions.get_time(num_time_points - 1);
     386             : 
     387             :         // Determine the number of individuals in Dead compartment at time t0-dt.
     388          22 :         deaths = m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)] -
     389          11 :                  m_transitions.get_last_value()[Eigen::Index(InfectionTransition::InfectedCriticalToDead)];
     390             :     }
     391             :     else {
     392             :         // Determine m_forceofinfection for current last time in m_transitions.
     393         117 :         num_time_points = m_transitions.get_num_time_points();
     394         117 :         current_time    = m_transitions.get_last_time();
     395         117 :         deaths          = m_populations.get_last_value()[Eigen::Index(InfectionState::Dead)];
     396             :     }
     397             : 
     398         384 :     for (Eigen::Index i = num_time_points - 1 - calc_time_index; i < num_time_points - 1; i++) {
     399         256 :         Eigen::Index state_age_index = num_time_points - 1 - i;
     400         256 :         ScalarType state_age         = state_age_index * dt;
     401             :         ScalarType season_val =
     402         256 :             1 + parameters.get<Seasonality>() *
     403         256 :                     sin(3.141592653589793 * ((parameters.get<StartDay>() + current_time) / 182.5 + 0.5));
     404             : 
     405         256 :         m_forceofinfection +=
     406         256 :             season_val * parameters.get<TransmissionProbabilityOnContact>().eval(state_age) *
     407         512 :             parameters.get<ContactPatterns>().get_cont_freq_mat().get_matrix_at(current_time)(0, 0) *
     408         256 :             (m_transitiondistributions_in_forceofinfection[0][num_time_points - i - 1] *
     409         512 :                  m_transitions[i + 1][Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms)] *
     410         256 :                  parameters.get<RelativeTransmissionNoSymptoms>().eval(state_age) +
     411         256 :              m_transitiondistributions_in_forceofinfection[1][num_time_points - i - 1] *
     412         512 :                  m_transitions[i + 1][Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms)] *
     413         256 :                  parameters.get<RiskOfInfectionFromSymptomatic>().eval(state_age));
     414             :     }
     415         128 :     m_forceofinfection = 1 / (m_N - deaths) * m_forceofinfection;
     416         128 : }
     417             : 
     418          16 : void Model::set_transitiondistributions_support_max(ScalarType dt)
     419             : {
     420             :     // We do not consider the transition SusceptibleToExposed as it is not needed in the computations.
     421         160 :     for (int transition = 1; transition < (int)InfectionTransition::Count; transition++) {
     422         144 :         m_transitiondistributions_support_max[transition] =
     423         144 :             parameters.get<TransitionDistributions>()[transition].get_support_max(dt, m_tol);
     424             :     }
     425          16 : }
     426             : 
     427          16 : void Model::set_transitiondistributions_derivative(ScalarType dt)
     428             : {
     429             :     // We do not consider the transition SusceptibleToExposed as it is not needed in the computations.
     430         160 :     for (int transition = 1; transition < (int)InfectionTransition::Count; transition++) {
     431             :         Eigen::Index support_max_index =
     432         144 :             (Eigen::Index)std::ceil(m_transitiondistributions_support_max[transition] / dt);
     433             :         // Create vec_derivative that contains the value of the approximated derivative for all necessary time points.
     434             :         // Here, we evaluate the derivative at time points t_0, ..., t_{support_max_index}.
     435         288 :         std::vector<ScalarType> vec_derivative(support_max_index + 1, 0.);
     436             : 
     437         655 :         for (Eigen::Index i = 0; i <= support_max_index; i++) {
     438             :             // Compute state_age for considered index.
     439         511 :             ScalarType state_age = (ScalarType)i * dt;
     440             :             // Compute derivative.
     441        1022 :             vec_derivative[i] = (parameters.get<TransitionDistributions>()[transition].eval(state_age) -
     442         511 :                                  parameters.get<TransitionDistributions>()[transition].eval(state_age - dt)) /
     443         511 :                                 dt;
     444             :         }
     445         144 :         m_transitiondistributions_derivative[transition] = vec_derivative;
     446         144 :     }
     447          16 : }
     448             : 
     449          14 : void Model::set_transitiondistributions_in_forceofinfection(ScalarType dt)
     450             : {
     451             :     // Relevant transitions for force of infection term.
     452          14 :     std::vector<std::vector<int>> relevant_transitions = {
     453             :         {(int)InfectionTransition::InfectedNoSymptomsToInfectedSymptoms,
     454             :          (int)InfectionTransition::InfectedNoSymptomsToRecovered},
     455             :         {(int)InfectionTransition::InfectedSymptomsToInfectedSevere,
     456          98 :          (int)InfectionTransition::InfectedSymptomsToRecovered}};
     457             : 
     458             :     // Determine the relevant calculation area = union of the supports of the relevant transition distributions.
     459          56 :     ScalarType calc_time = std::max({m_transitiondistributions_support_max[relevant_transitions[0][0]],
     460          14 :                                      m_transitiondistributions_support_max[relevant_transitions[0][1]],
     461          14 :                                      m_transitiondistributions_support_max[relevant_transitions[1][0]],
     462          14 :                                      m_transitiondistributions_support_max[relevant_transitions[1][1]]});
     463             : 
     464             :     // Corresponding index.
     465             :     // Need to evaluate survival functions at t_0, ..., t_{calc_time_index} for computation of force of infection,
     466             :     // subtract 1 because in the last summand all TransitionDistributions evaluate to 0 (by definition of support_max).
     467          14 :     Eigen::Index calc_time_index = (Eigen::Index)std::ceil(calc_time / dt) - 1;
     468             : 
     469             :     // Compute contributions from survival functions and transition probabilities starting in InfectedNoSymptoms and
     470             :     // InfectedSymptoms, respectively, on force of infection term.
     471          42 :     for (int contribution = 0; contribution < 2; contribution++) {
     472          56 :         std::vector<ScalarType> vec_contribution_to_foi(calc_time_index + 1, 0.);
     473          92 :         for (Eigen::Index i = 0; i <= calc_time_index; i++) {
     474             :             // Compute state_age for all indices from t_0, ..., t_{calc_time_index}.
     475          64 :             ScalarType state_age = i * dt;
     476          64 :             vec_contribution_to_foi[i] =
     477          64 :                 parameters.get<TransitionProbabilities>()[relevant_transitions[contribution][0]] *
     478          64 :                     parameters.get<TransitionDistributions>()[relevant_transitions[contribution][0]].eval(state_age) +
     479          64 :                 parameters.get<TransitionProbabilities>()[relevant_transitions[contribution][1]] *
     480          64 :                     parameters.get<TransitionDistributions>()[relevant_transitions[contribution][1]].eval(state_age);
     481             :         }
     482          28 :         m_transitiondistributions_in_forceofinfection[contribution] = vec_contribution_to_foi;
     483          28 :     }
     484          28 : }
     485             : 
     486          10 : ScalarType Model::get_global_support_max(ScalarType dt) const
     487             : {
     488             :     // Note that this function computes the global_support_max via the get_support_max() method and does not make use
     489             :     // of the vector m_transitiondistributions_support_max. This is because the global_support_max is already used in
     490             :     // check_constraints and we cannot ensure that the vector has already been computed when checking for constraints
     491             :     // (which usually happens before setting the initial flows and simulating).
     492          20 :     return std::max(
     493          10 :         {parameters.get<TransitionDistributions>()[(int)InfectionTransition::ExposedToInfectedNoSymptoms]
     494          10 :              .get_support_max(dt, m_tol),
     495          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedNoSymptomsToInfectedSymptoms]
     496          10 :              .get_support_max(dt, m_tol),
     497          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedNoSymptomsToRecovered]
     498          10 :              .get_support_max(dt, m_tol),
     499          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedSymptomsToInfectedSevere]
     500          10 :              .get_support_max(dt, m_tol),
     501          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedSymptomsToRecovered]
     502          10 :              .get_support_max(dt, m_tol),
     503          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedSevereToInfectedCritical]
     504          10 :              .get_support_max(dt, m_tol),
     505          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedSevereToRecovered].get_support_max(
     506          10 :              dt, m_tol),
     507          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedCriticalToDead].get_support_max(
     508          10 :              dt, m_tol),
     509          10 :          parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedCriticalToRecovered]
     510          20 :              .get_support_max(dt, m_tol)});
     511             : }
     512             : 
     513             : } // namespace isecir
     514             : } // namespace mio

Generated by: LCOV version 1.14