From Prompt to Biennial Endemic (This One Trick Could Save Your Calibration) - laser-base/laser-measles GitHub Wiki
From Prompt to Biennial Endemic: The Case Against Premature Calibration
The Goal
Produce classic biennial endemic measles waves in a large metropolitan node using laser-measles, calibrated from scratch. Constraints: CBR ~30-33/1000/yr, no importation to reseed, no spelunking the source code -- MCP tools only.
Chapter 1 -- The Landscape Before Calibration
The first instinct in any calibration problem is to reach for an optimizer. Here we did the opposite: we mapped the territory first.
A 6x10 grid sweep across R0 in {6, 8, 10, 12, 14, 16} and seasonality in {0.05, ..., 0.50} revealed a stark picture. Most of the parameter space falls into one of two failure modes:
- Extinction zone (upper-right): seasonality too strong for the given R0. The epidemic fires intensely, depletes susceptibles, and the integer-count compartmental model drives I to exactly zero -- permanently. No importation means no recovery.
- Annual / irregular zone (lower-left): R0 too low. Susceptibles replenish fast enough to sustain near-annual cycles, but there is no subharmonic resonance with the seasonal forcing.
Sandwiched between them is a narrow biennial corridor: R0 ~12-16, seasonality ~0.10-0.25. This is where the 2-year subharmonic locks in -- births at CBR=32 replenish susceptibles on a timescale that resonates with twice the annual forcing period.
The lesson: without this map, an optimizer handed a mostly-flat, mostly-zero objective landscape would waste the majority of its trials on dead zones. It might converge on something mediocre, or not converge at all. The sweep costs nothing -- 60 runs x 2.5 s = 2.5 minutes -- and makes the problem legible.

Chapter 2 -- The Ratio Landscape and What It Reveals
The right panel of the sweep goes further: it plots log(1 + epidemic/off-year peak ratio) for every surviving cell. The brighter the cell, the more dramatically the model alternates between epidemic and off-years.
The standout result: R0=16, seasonality=0.15 produces a 33x ratio -- a near-complete biennial saw-tooth. The R0=14-16, seasonality=0.10-0.15 band is unambiguously the sweet spot.
Chapter 2b -- Calibration as Finishing Touch
With the corridor identified, Optuna was given a narrow search box (R0 in [10, 18], seasonality in [0.01, 0.25]) and warm-started from the sweep's best grid point (R0=14, seas=0.10, score=0.621).
The optimizer converged in roughly 15 trials to R0=13.845, seasonality=0.112, score=0.628 -- a marginal improvement over the grid point. The optimizer did not discover the corridor; the sweep did. Optuna merely sharpened the pencil.
This is the correct role for calibration: precision work inside a region that science has already identified as viable.

Chapter 3 -- The Self-Sustaining Biennial Endemic
The final 30-year run with the calibrated parameters tells the story plainly.
After a 5-year burn-in, the model settles into a stable, self-perpetuating biennial rhythm:
| Metric | Value |
|---|---|
| Dominant period (annual-peak FFT) | 2.08 years |
| Biennial peak ratio | 12.3x |
| Epidemic year peak I | ~33,000 - 59,000 |
| Off-year peak I | ~2,000 - 5,000 |
| Off-year minimum I | never zero |
| Importation used | none |
The mechanism is purely endogenous: CBR=32 births replenish the susceptible pool on a ~2-year cycle, seasonal forcing synchronises the epidemic timing, and high R0 creates deep enough troughs to prevent annual re-ignition -- but not so deep that the infection extinguishes.

Chapter 3b -- The Annual Peak Fingerprint
The bar chart says it in one glance: perfectly alternating red (epidemic) and blue (off-year) bars, spanning 25 post-burnin years without deviation, growing slowly as the population accumulates net births. This is the canonical biennial fingerprint.
Chapter 3c -- Spectral Confirmation
The FFT of the annual-peak series places the dominant spectral peak at 2.08 years -- squarely in the biennial window (2.0 +/- 0.3 yr), with no competing annual or triennial peak. The spectrum is the mathematician's way of saying what the eye already sees in the time series.
The Full Story in One Figure

The Moral
| Step | Role | Time |
|---|---|---|
| Identifiability sweep | Map the viable region; expose extinction and annual zones | ~2.5 min |
| Optuna calibration | Sharpen within the known-good corridor | ~2.5 min |
| 30-year validation | Confirm stability and measure the result | ~4 s |
Premature calibration is not a speed shortcut -- it is a way to optimise the wrong thing efficiently. The sweep is not a preliminary step; it is the step that makes calibration meaningful.
Model & Parameters
| Property | Value |
|---|---|
| Model | laser-measles CompartmentalModel (SEIR, daily ticks) |
| Population | 10,000,000 (large metropolitan node) |
| CBR | 32 /1000/yr |
| CDR | 10 /1000/yr |
| MCV1 coverage | 0% |
| Importation | None |
| R0 (calibrated) | 13.845 |
| Seasonality (calibrated) | 0.112 |
| Infectious period | 8 days |
| Latent period | 6 days |
Prompt & Skills
Prompt (cleaned up)
OK, big cool project. I want you to create a scenario in which a large metropolitan node (patch) exhibits the classic biennial measles endemic waves. CBR should be about 30-33. Do not make CBR massive to get this to work. Use calibration to find parameters which work. Use clever objective functions as necessary. Remember to use parameter identifiabilty sweeps against objective functions before trying calibrations that might never work. Do not rely on importation to reseed. Pick the best base model for this. You might have to work hard.
Skills
Using just the jenner-measles-mcp-beta server to learn about laser-measles and using just the jenner-mcp server to learn about laser-core (INCLUDING CALIBRATION WITH LASER, see Chapter 17). Use Python3.11 (global). You do not need to go inspecting the laser-measles codebase. Use the MCP services. Do not go spellunking on my disk for help.
Note
The "use parameter identifiability first" was a key part of this whole experiment and arguably should be moved into the skills, but in this case it was not.
Code (Coming Soon)
Here: biennial_metro.py