Skip to content

laser.measles.scenarios.synthetic

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.

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
DataFrame

pl.DataFrame: Scenario DataFrame.

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.

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.

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.