Skip to content

laser.measles.scenarios.synthetic

laser.measles.scenarios.synthetic

Synthetic scenario builders for laser-measles models.

Overview

Every laser-measles model (ABM, Biweekly, Compartmental) requires a scenario: a Polars DataFrame with one row per geographic patch. This module provides ready-made scenario builders for testing and development. For production use, supply your own DataFrame following the schema below.

Correct import paths

Import the module::

1
2
from laser.measles.scenarios import synthetic
scenario = synthetic.single_patch_scenario(population=50_000, mcv1_coverage=0.8)

Or import individual functions directly::

1
2
from laser.measles.scenarios.synthetic import single_patch_scenario, two_patch_scenario
from laser.measles.scenarios import single_patch_scenario   # also re-exported here

NEVER write import laser_measles or from laser_measles import .... The package is always laser.measles (dot, not underscore).

Scenario DataFrame schema

All scenario DataFrames must contain at least these five columns:

+--------+----------+--------------------------------------------------+ | Column | Dtype | Description | +========+==========+==================================================+ | id | Utf8/str | Unique string identifier for the patch. | | | | Examples: "patch_0", "cluster_1:node_3" | +--------+----------+--------------------------------------------------+ | pop | Int64 | Total population of the patch (integer). | +--------+----------+--------------------------------------------------+ | lat | Float64 | Latitude of the patch centroid (degrees). | +--------+----------+--------------------------------------------------+ | lon | Float64 | Longitude of the patch centroid (degrees). | +--------+----------+--------------------------------------------------+ | mcv1 | Float64 | Routine MCV1 vaccination coverage, 0.0-1.0. | +--------+----------+--------------------------------------------------+

Critical schema notes:

  • id must be a string (pl.Utf8). Integer IDs will fail validation.
  • pop must be named pop, not population.
  • mcv1 must be present even when vaccination is irrelevant to your analysis (use 0.0 as a placeholder).
  • Extra columns are allowed and are ignored by the model.

Building a scenario by hand (no helper function needed)

::

1
2
3
4
5
6
7
8
9
import polars as pl

scenario = pl.DataFrame({
    "id":   ["patch_0", "patch_1", "patch_2"],
    "pop":  [200_000,   150_000,   80_000],
    "lat":  [40.0,      38.5,      36.0],
    "lon":  [4.0,       6.0,       8.0],
    "mcv1": [0.85,      0.70,      0.60],
})

Available helper functions

single_patch_scenario(population, mcv1_coverage) One patch. Useful for the simplest possible "hello world" run::

1
2
    from laser.measles.scenarios.synthetic import single_patch_scenario
    scenario = single_patch_scenario(population=10_000, mcv1_coverage=0.0)

two_patch_scenario(population, mcv1_coverage) Two patches; the second patch has half the population of the first::

1
2
    from laser.measles.scenarios.synthetic import two_patch_scenario
    scenario = two_patch_scenario(population=100_000, mcv1_coverage=0.8)

two_cluster_scenario(seed, n_nodes_per_cluster, ...) 100 patches arranged in two geographic clusters, with randomised populations and MCV1 coverage in a configurable range. Good for spatial spread studies::

1
2
    from laser.measles.scenarios.synthetic import two_cluster_scenario
    scenario = two_cluster_scenario(seed=42, n_nodes_per_cluster=50)

satellites_scenario(core_population, satellite_population, n_towns, ...) One large central city surrounded by smaller satellite towns — a classic city-and-hinterland structure::

1
2
    from laser.measles.scenarios.synthetic import satellites_scenario
    scenario = satellites_scenario(core_population=500_000, n_towns=30, mcv1=0.5)

Connecting a scenario to a model

Pass the DataFrame directly as the first argument to any model constructor::

1
2
3
4
5
6
from laser.measles.abm import ABMModel, ABMParams
from laser.measles.scenarios.synthetic import single_patch_scenario

scenario = single_patch_scenario(population=50_000, mcv1_coverage=0.85)
params   = ABMParams(num_ticks=365, seed=42, start_time="2000-01")
model    = ABMModel(scenario, params)

The same pattern applies to BiweeklyModel and CompartmentalModel.

laser.measles.scenarios.synthetic.satellites_scenario(core_population=500000, satellite_population=100000, n_towns=30, max_distance=200, mcv1=0.5, seed=52, population_std=0.3)

Create a cluster of nodes with a single large node in the center (core) surrounded by smaller nodes (satellites).

Parameters:

Name Type Description Default
core_population int

Population of the core city

500000
satellite_population int

Population of the satellite cities

100000
n_towns int

Number of towns to create

30
max_distance float

Maximum distance from center (km)

200
mcv1 float

MCV1 coverage

0.5
seed int

Random seed for reproducibility

52

Returns:

Type Description

pl.DataFrame: Scenario DataFrame.

laser.measles.scenarios.synthetic.single_patch_scenario(population=100000, mcv1_coverage=0.0)

Generate a synthetic scenario with a single patch.

Parameters:

Name Type Description Default
population int

Population of the patch.

100000
mcv1_coverage float

MCV1 coverage of the patch.

0.0

Returns:

Type Description
DataFrame

pl.DataFrame: Scenario DataFrame.

laser.measles.scenarios.synthetic.two_cluster_scenario(seed=42, n_nodes_per_cluster=50, cluster_centers=None, cluster_size_std=0.3, mcv1_coverage_range=None)

Generate a synthetic scenario with two clusters of nodes.

Parameters:

Name Type Description Default
seed int

Random seed for reproducibility.

42
n_nodes_per_cluster int

Number of nodes per cluster.

50
cluster_centers list[tuple[float, float]]

List of tuples representing the centers of the clusters.

None
cluster_size_std float

Standard deviation of the Gaussian distribution for cluster size.

0.3
mcv1_coverage_range tuple[float, float]

Range of MCV1 coverage percentages.

None

Returns:

Type Description
DataFrame

pl.DataFrame: Scenario DataFrame.

laser.measles.scenarios.synthetic.two_patch_scenario(population=100000, mcv1_coverage=0.0)

Generate a synthetic scenario with two patches where one is half the size of the other.

Parameters:

Name Type Description Default
population int

Population of the largest patch.

100000
mcv1_coverage float

MCV1 coverage of the patches.

0.0

Returns:

Type Description
DataFrame

pl.DataFrame: Scenario DataFrame.