Scenario and sensitivity analysis

Learning objectives:

  • Recognise the importance of sharing your scenario and sensitivity analysis code.
  • Learn how to run a combination of scenarios.
  • Understand differences between scenario and sensitivity analysis.

Relevant reproducibility guidelines:

  • STARS Reproducibility Recommendations (⭐): Provide code for all scenarios and sensitivity analyses.

Pre-reading:

This page continues on from: Parallel processing.

Entity generation → Entity processing → Initialisation bias → Performance measures → Replications → Parallel processing → Scenario and sensitivity analysis

Required packages:

These should be available from environment setup in the “Test yourself” section of Environments.

import itertools

from IPython.display import HTML
from itables import to_html_datatable
import numpy as np
import pandas as pd
import scipy.stats as st
import simpy
from sim_tools.distributions import Exponential
library(dplyr)
library(kableExtra)
library(knitr)
library(simmer)
library(tidyr)

Running and sharing scenarios

When creating a DES, you won’t just run one model - you run scenarios, which are different parameter set-ups. These are used to test how outcomes change under varying conditions.

When running scenarios, there are two important things to remember:

1. Run scenarios programmatically.

Don’t duplicate whole scripts just to test different set-ups. The idea is to build the model with functions and classes so you only need to change the parameters and re-run. That’s why we guide you through building the model this way in this book.

2. Share your scenario code.

You should share code and data for every scenario - not just the base case.

In Heather et al. (2025), six out of seven studies with scenarios lacked scenario code. This made it challenging and time-consuming to reproduce their results, as descriptions of the scenarios in the articles were often ambiguous and key parameters unclear or missing.

= 18

Simple scenarios

This code runs the simulation for each value of number_of_doctors from 3 to 6.

The returned dataframe contains the results from each of the five runs from the four scenarios.

results = []
# Loop through doctor counts 3-6, running the simulation
for i in range(3, 7):
    param = Parameters(number_of_doctors=i)
    runner = Runner(param=param)
    result = runner.run_reps()["run"]
    # Add scenario information to the results, then save to list
    result["number_of_doctors"] = i
    results.append(result)

# Combine results into a single dataframe
scenario_results = pd.concat(results, ignore_index=True)

# Show as interactive table
HTML(to_html_datatable(scenario_results))
Loading ITables v2.5.2 from the internet... (need help?)
results <- list()
# Loop through doctor counts 3-6, running the simulation
for (i in 3L:6L) {
  param <- create_params(number_of_doctors = i)
  result <- runner(param = param)[["run_results"]]
  # Add scenario information to the results, then save to list
  result["number_of_doctors"] <- i
  results[[length(results) + 1L]] <- result
}

# Combine results into a single dataframe
scenario_results <- do.call(rbind, results)

# Show as interactive table
kable(scenario_results) |> scroll_box(height = "400px")
replication arrivals mean_wait_time_doctor mean_time_with_doctor utilisation_doctor mean_queue_length_doctor mean_time_in_system mean_patients_in_system number_of_doctors
1 9 1.1561321 12.600360 0.7276328 0.0867587 6.972457 2.3250216 3
2 10 0.2185845 15.462225 0.7846974 0.3030654 7.019465 2.3551596 3
3 8 1.6612341 11.704709 0.8021880 0.2856074 10.033307 1.7861155 3
4 4 1.3862671 8.268402 0.5661071 0.9366037 8.714268 0.8466787 3
5 7 1.1383003 8.211288 0.8055934 0.1965982 8.159880 1.5215678 3
1 9 0.0000000 14.361497 0.5548891 0.0000000 5.909446 2.0617147 4
2 8 0.0000000 10.902873 0.5048249 0.0000000 5.040122 1.5551330 4
3 8 0.1002196 12.491221 0.6609534 0.0000000 8.233837 1.6932483 4
4 5 0.0000000 7.220270 0.3307908 0.0000000 8.605687 0.8960278 4
5 5 0.0000000 7.764737 0.2861765 0.0000000 7.764737 1.0613887 4
1 9 0.0000000 14.361497 0.4439113 0.0000000 5.909446 2.0617147 5
2 8 0.0000000 10.902873 0.4038599 0.0000000 5.040122 1.5551330 5
3 8 0.0000000 13.200172 0.5337198 0.0000000 8.233837 1.6932483 5
4 5 0.0000000 7.220270 0.2646327 0.0000000 8.605687 0.8960278 5
5 5 0.0000000 7.764737 0.2289412 0.0000000 7.764737 1.0613887 5
1 9 0.0000000 14.361497 0.3699261 0.0000000 5.909446 2.0617147 6
2 8 0.0000000 10.902873 0.3365499 0.0000000 5.040122 1.5551330 6
3 8 0.0000000 13.200172 0.4447665 0.0000000 8.233837 1.6932483 6
4 5 0.0000000 7.220270 0.2205272 0.0000000 8.605687 0.8960278 6
5 5 0.0000000 7.764737 0.1907843 0.0000000 7.764737 1.0613887 6

Running several scenario combinations

If you are running several scenarios, it can be useful to define a helper function like the one shown below. This example is adapted from:

HSMA - little book of DES” from Sammi Rosser, Dan Chalk and Amy Heather 2025 (MIT Licence).

def run_scenarios(scenarios, param_factory=None):
    """
    Execute a set of scenarios and return the results from each run.

    Parameters
    ----------
    scenarios : dict
        Dictionary where key is name of parameter and value is a list with
        different values to run in scenarios.
    param_factory : callable or None, optional
        A callable that returns a new Parameters object for each scenario run.
        This can be a class (e.g., `Parameters`) or a factory function/lambda
        with preset arguments (e.g., `lambda: Parameters(number_of_runs=4)`).
        If not provided, defaults to using `Parameters()` with no arguments.

    Returns
    -------
    pandas.DataFrame
        DataFrame with results from each run of each scenario.

    Notes
    -----
    Function adapted from Rosser, Chalk and Heather 2025.
    """
    # If none provided, use Parameters
    if param_factory is None:
        param_factory = Parameters

    # Find every possible permutation of the scenarios
    all_scenarios_tuples = list(itertools.product(*scenarios.values()))

    # Convert back into dictionaries
    all_scenarios_dicts = [
        dict(zip(scenarios.keys(), p)) for p in all_scenarios_tuples
    ]

    # Preview some of the scenarios
    print(f"There are {len(all_scenarios_dicts)} scenarios. Running:")

    # Run the scenarios...
    results = []
    for index, scenario_to_run in enumerate(all_scenarios_dicts):
        print(scenario_to_run)

        # Create fresh instance of parameter class for each scenario
        param = param_factory()

        # Update parameter list with the scenario parameters
        param.scenario_name = index
        for key in scenario_to_run:
            setattr(param, key, scenario_to_run[key])

        # Perform replications
        scenario_exp = Runner(param)
        scenario_res = scenario_exp.run_reps()["run"]

        # Add scenario number and values to the results dataframe
        scenario_res["scenario"] = index
        for key in scenario_to_run:
            scenario_res[key] = scenario_to_run[key]

        # Add results from scenario to list
        results.append(scenario_res)
    return pd.concat(results)

If you are running several scenarios, it can be useful to define a helper function like the one shown below.

#' Run a set of scenarios
#'
#' @param scenarios List where key is name of parameter and value is a list of
#' different values to run in scenarios
#' @param base_list List of parameters to use as base for scenarios, which can
#' be partial (as will input to create_params() function).
#' @param verbose Boolean, whether to print messages about scenarios as run.
#'
#' @return Tibble with results from each replication for each scenario.
#' @export

run_scenarios <- function(scenarios, base_list, verbose = TRUE) {
  # Generate all permutations of the scenarios
  all_scenarios <- expand.grid(scenarios)

  # Preview the number of scenarios
  if (isTRUE(verbose)) {
    message(sprintf("There are %d scenarios.", nrow(all_scenarios)))
    message("Base parameters:")
    print(base_list)
  }

  results <- list()

  # Iterate through each scenario
  for (index in seq_len(nrow(all_scenarios))) {

    # Filter to one of the scenarios
    scenario_to_run <- all_scenarios[index, , drop = FALSE]

    # Print the scenario parameters
    formatted_scenario <- toString(
      paste0(names(scenario_to_run), " = ", scenario_to_run)
    )

    # Print the scenario currently running
    if (isTRUE(verbose)) {
      message("Scenario: ", formatted_scenario)
    }

    # Create parameter list with scenario-specific values
    s_args <- c(scenario_to_run, list(scenario_name = index))

    # Create instance of parameter class with specified base parameters
    s_param <- do.call(create_params, base_list)

    # Update parameter list with the scenario parameters
    for (name in names(s_args)) {
      s_param[[name]] <- s_args[[name]]
    }

    # Run replications for the current scenario and get processed results
    scenario_result <- runner(s_param)[["run_results"]]

    # Append scenario parameters to the results
    scenario_result[["scenario"]] <- index
    for (key in names(scenario_to_run)) {
      scenario_result[[key]] <- scenario_to_run[[key]]
    }

    # Append to results list
    results[[index]] <- scenario_result
  }
  do.call(rbind, results)
}

The function generates all possible scenario combinations from the values supplied in scenarios.

It then iterates through these, creating a fresh set of parameters for each scenario run. This ensures every scenario gets an independent set of parameters and avoids carrying state from one scenario to another.

The replication results from each scenario are saved in a list and finally combined into a single table.

Let’s run it!

# Run scenarios
scenario_results = run_scenarios(
    scenarios={"interarrival_time": [4, 5, 6, 7, 8],
               "number_of_doctors": [3, 4, 5]},
    param_factory=Parameters
)
There are 15 scenarios. Running:
{'interarrival_time': 4, 'number_of_doctors': 3}
{'interarrival_time': 4, 'number_of_doctors': 4}
{'interarrival_time': 4, 'number_of_doctors': 5}
{'interarrival_time': 5, 'number_of_doctors': 3}
{'interarrival_time': 5, 'number_of_doctors': 4}
{'interarrival_time': 5, 'number_of_doctors': 5}
{'interarrival_time': 6, 'number_of_doctors': 3}
{'interarrival_time': 6, 'number_of_doctors': 4}
{'interarrival_time': 6, 'number_of_doctors': 5}
{'interarrival_time': 7, 'number_of_doctors': 3}
{'interarrival_time': 7, 'number_of_doctors': 4}
{'interarrival_time': 7, 'number_of_doctors': 5}
{'interarrival_time': 8, 'number_of_doctors': 3}
{'interarrival_time': 8, 'number_of_doctors': 4}
{'interarrival_time': 8, 'number_of_doctors': 5}
# Save to CSV
scenario_results.to_csv(
    "scenarios_resources/python_scenario_results.csv", index=False
)

# View scenario results
HTML(to_html_datatable(scenario_results.head(200)))
Loading ITables v2.5.2 from the internet... (need help?)
# Run scenarios
scenarios <- list(
  interarrival_time = c(4L, 5L, 6L, 7L, 8L),
  number_of_doctors = c(3L, 4L, 5L)
)
scenario_results <- run_scenarios(
  scenarios = scenarios, base_list = create_params()
)
There are 15 scenarios.
Base parameters:
$interarrival_time
[1] 5

$consultation_time
[1] 10

$number_of_doctors
[1] 3

$warm_up_period
[1] 30

$data_collection_period
[1] 40

$number_of_runs
[1] 5

$verbose
[1] FALSE
Scenario: interarrival_time = 4, number_of_doctors = 3
Scenario: interarrival_time = 5, number_of_doctors = 3
Scenario: interarrival_time = 6, number_of_doctors = 3
Scenario: interarrival_time = 7, number_of_doctors = 3
Scenario: interarrival_time = 8, number_of_doctors = 3
Scenario: interarrival_time = 4, number_of_doctors = 4
Scenario: interarrival_time = 5, number_of_doctors = 4
Scenario: interarrival_time = 6, number_of_doctors = 4
Scenario: interarrival_time = 7, number_of_doctors = 4
Scenario: interarrival_time = 8, number_of_doctors = 4
Scenario: interarrival_time = 4, number_of_doctors = 5
Scenario: interarrival_time = 5, number_of_doctors = 5
Scenario: interarrival_time = 6, number_of_doctors = 5
Scenario: interarrival_time = 7, number_of_doctors = 5
Scenario: interarrival_time = 8, number_of_doctors = 5
# Save to CSV
write.csv(scenario_results,
          file.path("scenarios_resources", "r_scenario_results.csv"))

# View scenario results
kable(scenario_results) |> scroll_box(height = "400px")
replication arrivals mean_wait_time_doctor mean_time_with_doctor utilisation_doctor mean_queue_length_doctor mean_time_in_system mean_patients_in_system scenario interarrival_time number_of_doctors
1 8 0.0000000 10.626767 0.4537486 0.0000000 5.909446 1.4915195 1 4 3
2 14 1.3489715 16.474475 0.8289468 1.0647613 6.529895 3.5927361 1 4 3
3 8 3.3185568 12.576986 0.9191876 0.6619987 14.293369 2.4039172 1 4 3
4 9 7.0789803 8.229096 1.0000000 1.7013415 14.810041 3.2499288 1 4 3
5 11 11.8431217 8.866916 1.0000000 2.7677553 16.007073 4.0062616 1 4 3
1 9 1.1561321 12.600360 0.7276328 0.0867587 6.972457 2.3250216 2 5 3
2 10 0.2185845 15.462225 0.7846974 0.3030654 7.019465 2.3551596 2 5 3
3 8 1.6612341 11.704709 0.8021880 0.2856074 10.033307 1.7861155 2 5 3
4 4 1.3862671 8.268402 0.5661071 0.9366037 8.714268 0.8466787 2 5 3
5 7 1.1383003 8.211288 0.8055934 0.1965982 8.159880 1.5215678 2 5 3
1 4 0.0000000 16.756011 0.6414842 0.0000000 7.594901 1.8938729 3 6 3
2 7 0.0000000 13.951263 0.6246311 0.0000000 9.878982 1.8342062 3 6 3
3 11 0.0000000 10.642901 0.6635152 0.0000000 5.471170 2.1379821 3 6 3
4 5 2.1255795 7.962361 0.6226607 0.7752625 10.087940 1.8885379 3 6 3
5 6 0.1698502 7.931855 0.6517116 0.0000000 7.985399 0.8057795 3 6 3
1 5 0.0000000 14.928869 0.4778604 0.0000000 8.986369 1.3699936 4 7 3
2 5 0.0000000 16.557442 0.5346736 0.0000000 9.466947 1.3707882 4 7 3
3 8 0.0000000 10.066787 0.5240666 0.0000000 3.468666 2.0591952 4 7 3
4 5 0.9021887 7.962361 0.4926338 0.7752625 8.864549 1.5240774 4 7 3
5 4 0.0000000 8.753199 0.3178118 0.0000000 7.926292 0.7389692 4 7 3
1 3 0.0000000 20.737360 0.4070758 0.0000000 8.986369 1.2813530 5 8 3
2 5 0.0000000 16.557442 0.4604853 0.0000000 9.466947 1.1994397 5 8 3
3 7 0.0000000 11.304930 0.4236727 0.0000000 3.882443 1.8788845 5 8 3
4 6 0.0000000 6.932281 0.3316804 0.0000000 4.751341 0.9071163 5 8 3
5 3 0.0000000 9.563759 0.2875069 0.0000000 7.926292 0.5634397 5 8 3
1 11 0.2873017 8.979263 0.7385688 0.0000000 5.072421 2.0138738 6 4 4
2 7 0.0000000 11.322431 0.6053971 0.0000000 5.722932 2.1066223 6 4 4
3 10 0.3279443 13.891435 0.8138707 0.0000000 11.073710 2.2892632 6 4 4
4 6 0.0000000 7.155858 0.3158917 0.0000000 6.212797 0.7887158 6 4 4
5 8 0.0000000 9.299128 0.3310623 0.0000000 7.062117 1.5393844 6 4 4
1 9 0.0000000 14.361497 0.5548891 0.0000000 5.909446 2.0617147 7 5 4
2 8 0.0000000 10.902873 0.5048249 0.0000000 5.040122 1.5551330 7 5 4
3 8 0.1002196 12.491221 0.6609534 0.0000000 8.233837 1.6932483 7 5 4
4 5 0.0000000 7.220270 0.3307908 0.0000000 8.605687 0.8960278 7 5 4
5 5 0.0000000 7.764737 0.2861765 0.0000000 7.764737 1.0613887 7 5 4
1 4 0.0000000 16.756011 0.4811132 0.0000000 7.594901 1.8938729 8 6 4
2 7 0.0000000 13.951263 0.4684733 0.0000000 9.878982 1.8342062 8 6 4
3 11 0.0000000 10.921073 0.4976364 0.0000000 5.471170 2.1379821 8 6 4
4 6 0.0000000 6.932281 0.4399759 0.0000000 6.932281 1.3959298 8 6 4
5 4 0.0000000 8.753199 0.2213847 0.0000000 7.926292 0.8176458 8 6 4
1 5 0.0000000 14.928869 0.3583953 0.0000000 8.986369 1.3699936 9 7 4
2 5 0.0000000 16.557442 0.4010052 0.0000000 9.466947 1.3707882 9 7 4
3 8 0.0000000 10.066787 0.3930499 0.0000000 3.468666 2.0591952 9 7 4
4 6 0.0000000 6.932281 0.3505276 0.0000000 6.932281 1.2725838 9 7 4
5 4 0.0000000 7.539044 0.1964017 0.0000000 5.772494 0.4947610 9 7 4
1 3 0.0000000 20.737360 0.3053068 0.0000000 8.986369 1.2813530 10 8 4
2 5 0.0000000 16.557442 0.3453640 0.0000000 9.466947 1.1994397 10 8 4
3 7 0.0000000 11.304930 0.3177545 0.0000000 3.882443 1.8788845 10 8 4
4 6 0.0000000 6.932281 0.2487603 0.0000000 4.751341 0.9071163 10 8 4
5 4 0.0000000 7.437496 0.2778820 0.0000000 7.437496 1.2817552 10 8 4
1 13 0.1711051 9.688685 0.6178914 0.0000000 5.063579 2.1269053 11 4 5
2 7 0.0000000 11.322431 0.4843177 0.0000000 5.722932 2.1066223 11 4 5
3 11 0.1017516 14.993140 0.7501085 0.0000000 11.329369 2.7785658 11 4 5
4 6 0.0000000 7.155858 0.2527133 0.0000000 6.212797 0.7887158 11 4 5
5 8 0.0000000 9.299128 0.2648499 0.0000000 7.062117 1.5393844 11 4 5
1 9 0.0000000 14.361497 0.4439113 0.0000000 5.909446 2.0617147 12 5 5
2 8 0.0000000 10.902873 0.4038599 0.0000000 5.040122 1.5551330 12 5 5
3 8 0.0000000 13.200172 0.5337198 0.0000000 8.233837 1.6932483 12 5 5
4 5 0.0000000 7.220270 0.2646327 0.0000000 8.605687 0.8960278 12 5 5
5 5 0.0000000 7.764737 0.2289412 0.0000000 7.764737 1.0613887 12 5 5
1 4 0.0000000 16.756011 0.3848905 0.0000000 7.594901 1.8938729 13 6 5
2 7 0.0000000 13.951263 0.3747787 0.0000000 9.878982 1.8342062 13 6 5
3 11 0.0000000 10.921073 0.3981091 0.0000000 5.471170 2.1379821 13 6 5
4 6 0.0000000 6.932281 0.3519807 0.0000000 6.932281 1.3959298 13 6 5
5 4 0.0000000 8.753199 0.1771077 0.0000000 7.926292 0.8176458 13 6 5
1 5 0.0000000 14.928869 0.2867162 0.0000000 8.986369 1.3699936 14 7 5
2 5 0.0000000 16.557442 0.3208042 0.0000000 9.466947 1.3707882 14 7 5
3 8 0.0000000 10.066787 0.3144400 0.0000000 3.468666 2.0591952 14 7 5
4 6 0.0000000 6.932281 0.2804221 0.0000000 6.932281 1.2725838 14 7 5
5 4 0.0000000 7.539044 0.1571214 0.0000000 5.772494 0.4947610 14 7 5
1 3 0.0000000 20.737360 0.2442455 0.0000000 8.986369 1.2813530 15 8 5
2 5 0.0000000 16.557442 0.2762912 0.0000000 9.466947 1.1994397 15 8 5
3 7 0.0000000 11.304930 0.2542036 0.0000000 3.882443 1.8788845 15 8 5
4 6 0.0000000 6.932281 0.1990082 0.0000000 4.751341 0.9071163 15 8 5
5 4 0.0000000 7.437496 0.2223056 0.0000000 7.437496 1.2817552 15 8 5

Sensitivity analysis

How does sensitivity analysis differ from scenario analysis?

Scenario analysis focuses on a set of pre-defined situations which are plausible and relevant to the problem being studied. It can often involve varying multiple parameters simultaneously. The purpose is to understand how the system operates under different hypothetical scenarios.

Sensitivity analysis varies one parameter (or a small group of parameters) and assess the impact of small changes in that parameter on outcomes. The purpose is to understand how uncertainty in the inputs affects the model, and how robust the model is to variation in those inputs.

Running sensitivity analyses

Sensitivity analyses should be run programmatically, just like scenario analyses, and the code should be shared for reproducibility.

They can be run using similar code. Below is an example using the run_scenarios function to vary the interarrival_time parameter.

# Run sensitivity analysis
sensitivity_results = run_scenarios(
    scenarios={"interarrival_time": [4, 4.5, 5, 5.5, 6, 6.5, 7]},
    param_factory=Parameters
)
There are 7 scenarios. Running:
{'interarrival_time': 4}
{'interarrival_time': 4.5}
{'interarrival_time': 5}
{'interarrival_time': 5.5}
{'interarrival_time': 6}
{'interarrival_time': 6.5}
{'interarrival_time': 7}
# Save to CSV
sensitivity_results.to_csv(
    "scenarios_resources/python_sensitivity_results.csv", index=False
)

# View sensitivity results
HTML(to_html_datatable(sensitivity_results.head(200)))
Loading ITables v2.5.2 from the internet... (need help?)
# Run sensitivity analysis
sensitivity_results <- run_scenarios(
  scenarios = list(interarrival_time = c(4L, 4.5, 5L, 5.5, 6L, 6.5, 7L)),
  base_list = create_params()
)
There are 7 scenarios.
Base parameters:
$interarrival_time
[1] 5

$consultation_time
[1] 10

$number_of_doctors
[1] 3

$warm_up_period
[1] 30

$data_collection_period
[1] 40

$number_of_runs
[1] 5

$verbose
[1] FALSE
Scenario: interarrival_time = 4
Scenario: interarrival_time = 4.5
Scenario: interarrival_time = 5
Scenario: interarrival_time = 5.5
Scenario: interarrival_time = 6
Scenario: interarrival_time = 6.5
Scenario: interarrival_time = 7
# Save to CSV
write.csv(sensitivity_results,
          file.path("scenarios_resources", "r_sensitivity_results.csv"))

# View sensitivity results
kable(sensitivity_results) |> scroll_box(height = "400px")
replication arrivals mean_wait_time_doctor mean_time_with_doctor utilisation_doctor mean_queue_length_doctor mean_time_in_system mean_patients_in_system scenario interarrival_time
1 8 0.0000000 10.626767 0.4537486 0.0000000 5.909446 1.4915195 1 4.0
2 14 1.3489715 16.474475 0.8289468 1.0647613 6.529895 3.5927361 1 4.0
3 8 3.3185568 12.576986 0.9191876 0.6619987 14.293369 2.4039172 1 4.0
4 9 7.0789803 8.229096 1.0000000 1.7013415 14.810041 3.2499288 1 4.0
5 11 11.8431217 8.866916 1.0000000 2.7677553 16.007073 4.0062616 1 4.0
1 11 4.4136200 10.968638 0.9298199 0.9756128 10.970224 3.1920586 2 4.5
2 10 4.2952348 11.628385 0.8730010 0.3030654 10.737537 3.5451830 2 4.5
3 8 6.3476572 13.246809 0.9533261 0.8691611 16.012763 3.1993868 2 4.5
4 4 1.6258165 9.999398 0.6284609 2.2429504 8.013161 0.7329990 2 4.5
5 7 1.4030508 7.662403 0.8019001 0.1902909 8.671934 1.8121060 2 4.5
1 9 1.1561321 12.600360 0.7276328 0.0867587 6.972457 2.3250216 3 5.0
2 10 0.2185845 15.462225 0.7846974 0.3030654 7.019465 2.3551596 3 5.0
3 8 1.6612341 11.704709 0.8021880 0.2856074 10.033307 1.7861155 3 5.0
4 4 1.3862671 8.268402 0.5661071 0.9366037 8.714268 0.8466787 3 5.0
5 7 1.1383003 8.211288 0.8055934 0.1965982 8.159880 1.5215678 3 5.0
1 8 0.0000000 13.183604 0.6309778 0.0000000 7.594901 1.7924370 4 5.5
2 6 0.0000000 14.204232 0.6859391 0.0000000 8.061193 1.7964074 4 5.5
3 9 1.0701721 10.484981 0.8439595 0.3755197 7.720035 2.8520302 4 5.5
4 8 1.6097542 12.349744 0.6932288 0.3067946 7.074266 2.0253767 4 5.5
5 7 0.6407407 8.647519 0.7276208 0.0000000 7.929976 1.1557192 4 5.5
1 4 0.0000000 16.756011 0.6414842 0.0000000 7.594901 1.8938729 5 6.0
2 7 0.0000000 13.951263 0.6246311 0.0000000 9.878982 1.8342062 5 6.0
3 11 0.0000000 10.642901 0.6635152 0.0000000 5.471170 2.1379821 5 6.0
4 5 2.1255795 7.962361 0.6226607 0.7752625 10.087940 1.8885379 5 6.0
5 6 0.1698502 7.931855 0.6517116 0.0000000 7.985399 0.8057795 5 6.0
1 5 0.0000000 14.928869 0.5853969 0.0000000 7.601250 1.6358209 6 6.5
2 6 0.0000000 15.719055 0.5136415 0.0000000 12.100828 1.5208300 6 6.5
3 8 0.0000000 10.066787 0.5979488 0.0000000 5.471170 2.0850030 6 6.5
4 5 1.5138841 7.962361 0.5499657 0.7752625 9.476245 1.6985554 6 6.5
5 5 0.0046527 13.128797 0.6701869 0.0000000 13.133450 1.8736129 6 6.5
1 5 0.0000000 14.928869 0.4778604 0.0000000 8.986369 1.3699936 7 7.0
2 5 0.0000000 16.557442 0.5346736 0.0000000 9.466947 1.3707882 7 7.0
3 8 0.0000000 10.066787 0.5240666 0.0000000 3.468666 2.0591952 7 7.0
4 5 0.9021887 7.962361 0.4926338 0.7752625 8.864549 1.5240774 7 7.0
5 4 0.0000000 8.753199 0.3178118 0.0000000 7.926292 0.7389692 7 7.0

Explore the example models

Nurse visit simulation

GitHub Click to visit pydesrap_mms repository

Key files simulation/run_scenarios.py
notebooks/analysis.ipynb
What to look for? Similar implementation to above.

GitHub Click to visit rdesrap_mms repository

Key files R/scenarios.R
rmarkdown/analysis.md
What to look for? Similar implementation to above.

Stroke pathway simulation

GitHub Click to visit pydesrap_stroke repository

Key files notebooks/analysis.ipynb
What to look for? These scenarios are run one-by-one in the Jupyter notebook. They involve altering arrivals before running the simulation (5% increase in admissions), or changes in calculations (pooling beds).

GitHub Click to visit rdesrap_stroke repository

Key files rmarkdown/analysis.md
What to look for? These scenarios are run one-by-one in the Rmarkdown. They involve altering arrivals before running the simulation (5% increase in admissions), or changes in calculations (pooling beds).

Test yourself

Try running your own scenario and sensitivity analyses.

You could build your own simple loop to test different values - just make sure you use a fresh set of parameters for each run.

Alternatively, use the run_scenarios function.

References

Heather, Amy, Thomas Monks, Alison Harper, Navonil Mustafee, and Andrew Mayne. 2025. “On the Reproducibility of Discrete-Event Simulation Studies in Health Research: An Empirical Study Using Open Models.” Journal of Simulation 0 (0): 1–25. https://doi.org/10.1080/17477778.2025.2552177.