# Quickstart ## Set up a dimensionless ODE model To set up a simple Susceptible-Infectious-Removed (SIR) disease model, schematically represented as follows, and governed by the following equations, ```{math} \begin{eqnarray} N &=& S + I + R, \\ \frac{dS}{dt} &=& - \beta S (I/N), \\ \frac{dI}{dt} &=& \beta S (I/N) - (1/\gamma)I, \\ \frac{dR}{dt} &=& (1/\gamma)I. \end{eqnarray} ``` Load pySODM's [`ODE`](models.md) class, and then define your model class--inheriting pySODM's `ODE` class--to define your model. Minimally, you must provide: 1) A list containing the names of your model's states, 2) a list containing the names of your model's parameters, 3) an `integrate` function integrating your model's states. To learn more about the `ODE` class' formatting, check out the [`ODE`](models.md) class description. ```python # Import the ODE class from pySODM.models.base import ODE # Define the model equations class SIR(ODE): states = ['S','I','R'] parameters = ['beta','gamma'] @staticmethod def integrate(t, S, I, R, beta, gamma): # Calculate total population N = S+I+R # Calculate differentials dS = -beta*S*I/N dI = beta*S*I/N - 1/gamma*I dR = 1/gamma*I return dS, dI, dR ``` To initialize the model, provide a dictionary containing the initial condition and a dictionary containing all model parameters. Undefined initial states are automatically filled with zeros. ```python model = SIR(initial_states={'S': 1000, 'I': 1}, parameters={'beta': 0.35, 'gamma': 5}) ``` Alternatively, use an *initial condition function* to define the initial states, ```python # can have arguments def initial_condition_function(S0): return {'S': S0, 'I': 1} # that become part of the model's parameters and can be optimised.. model = SIR(initial_states=initial_condition_function, parameters={'beta': 0.35, 'gamma': 5, 'S0': 1000}) ``` Simulate the model using its `sim()` method. pySODM supports the use of dates to index simulations, string representations of dates with the format `'yyyy-mm-dd'` as well as `datetime.datetime()` can be used to define the start- and enddate of a simulation. By default, pySODM assumes the unit of time is days, but you can change this using the `time_unit` input. ```python # Timesteps out = model.sim(121) # String representation of dates: # 'yyyy-mm-dd' only (no hours, min, seconds -> use datetime) out = model.sim(['2022-12-01', '2023-05-01']) # Datetime representation of time + date # A timestep of length 1 represents one week from datetime import datetime as datetime out = model.sim([datetime(2022, 12, 1), datetime(2023, 5, 1)], time_unit='W') # Tailor method and tolerance of integrator out = model.sim(121, method='RK45', rtol='1e-4') ``` In some situations, the use of a discrete timestep with a fixed length may be preferred, ```python out = model.sim(121, tau=1) # Use a discrete timestepper with step size 1 ``` Simulations are sent to an `xarray.Dataset`, for more information on indexing and selecting data using `xarray`, [see](https://docs.xarray.dev/en/stable/user-guide/indexing.html). ```bash Dimensions: (time: 122) Coordinates: * time (time) int64 0 1 2 3 4 5 6 7 8 ... 114 115 116 117 118 119 120 121 Data variables: S (time) float64 1e+03 999.6 999.2 998.7 ... 287.2 287.2 287.2 287.2 I (time) float64 1.0 1.161 1.348 1.565 ... 0.1455 0.1316 0.1192 R (time) float64 0.0 0.2157 0.4662 0.7569 ... 713.6 713.7 713.7 713.7 ``` The simulation above results in the following trajectories for the number of susceptibles, infected and recovered individuals. ![SIR_time](/_static/figs/quickstart/quickstart_SIR.png) ## Set up an ODE model with a labeled dimension To transform all our SIR model's states in 1D vectors, referred to as a *dimension* throughout the documentation, add the `dimensions` keyword to the model declaration. In the example below, we add a dimension representing the age groups individuals belong to. This turns all model states in the `integrate` function into 1D vectors. ```python # Import the ODE class from pySODM.models.base import ODE # Define the model equations class stratified_SIR(ODE): states = ['S','I','R'] parameters = ['beta', 'gamma'] dimensions = ['age_groups'] @staticmethod def integrate(t, S, I, R, beta, gamma): # Calculate total population N = S+I+R # Calculate differentials dS = -beta*S*I/N dI = beta*S*I/N - 1/gamma*I dR = 1/gamma*I return dS, dI, dR ``` When initializing your model, provide a dictionary containing coordinates for every dimension declared previously. In the example below, we'll declare four age groups: 0-5, 5-15, 15-65 and 65-120 year olds. **All** model states are now 1D vectors of shape `(4,)`. ```python model = stratified_SIR(initial_states={'S': 1000*np.ones(4), 'I': np.ones(4)}, parameters={'beta': 0.35, 'gamma': 5}, coordinates={'age_groups': ['0-5','5-15', '15-65','65-120']}) out = model.sim(121) print(out) ``` The dimension `'age_groups'` with coordinates `['0-5','5-15', '15-65','65-120']` is automatically added to the model output. ```bash Dimensions: (time: 122, age_groups: 4) Coordinates: * time (time) int64 0 1 2 3 4 5 6 7 ... 114 115 116 117 118 119 120 121 * age_groups (age_groups) In the example below, the states S, I and R will be 1D vectors in the `integrate` function, while the states S_v and I_v will be scalars. ```python class ODE_SIR_SI(ODE): """ An age-stratified SIR model for humans, an unstratified SI model for a disease vector (f.i. mosquito) """ states = ['S', 'I', 'R', 'S_v', 'I_v'] parameters = ['beta', 'gamma'] stratified_parameters = ['alpha'] dimensions = ['age_group'] dimensions_per_state = [['age_group'],['age_group'],['age_group'],[],[]] @staticmethod def integrate(t, S, I, R, S_v, I_v, alpha, beta, gamma): # Calculate total mosquito population N = S + I + R N_v = S_v + I_v # Calculate human differentials dS = -alpha*(I_v/N_v)*S dI = alpha*(I_v/N_v)*S - 1/gamma*I dR = 1/gamma*I # Calculate mosquito differentials dS_v = -np.sum(alpha*(I/N)*S_v) + (1/beta)*N_v - (1/beta)*S_v dI_v = np.sum(alpha*(I/N)*S_v) - (1/beta)*I_v return dS, dI, dR, dS_v, dI_v ``` Setting up and simulating the model, ```python # Define parameters, initial states and coordinates params={'alpha': np.array([0.05, 0.1, 0.2, 0.15]), 'gamma': 5, 'beta': 7} init_states = {'S': [606938, 1328733, 7352492, 2204478], 'S_v': 1e6, 'I_v': 2} coordinates={'age_group': ['0-5','5-15', '15-65','65-120']} # Initialize model model = ODE_SIR_SI(initial_states=init_states, parameters=params, coordinates=coordinates) # Simulate the model out = model.sim(120) print(out) ``` results in the following `xarray.Dataset`, ```bash Dimensions: (age_group: 4, time: 121) Coordinates: * age_group (age_groups) Dimensions: (time: 122, age_groups: 4, draws: 10) Coordinates: * time (time) int64 0 1 2 3 4 5 6 7 ... 114 115 116 117 118 119 120 121 * age_groups (age_groups)