Skip to content

New example: Calibration to empirical data #58

Description

@smjenness

Motivation

Every gallery example uses illustrative parameter values with no connection to empirical data. Users building real models need to calibrate — adjust parameters so that simulation output matches observed epidemiological targets (prevalence, incidence, demographic structure). This is typically the hardest and most time-consuming part of building an EpiModel application, and there's no gallery template for it. Even a simplified example showing the workflow would fill a major gap.

Suggested Design

Disease Model: SIS with Vital Dynamics

Use SIS with vital dynamics — simple enough to focus on calibration methodology, but with enough parameters to make calibration non-trivial. The model has both network parameters (ERGM terms) and epidemic parameters that need calibration.

Calibration Approach: Grid Search + Manual Refinement

Rather than implementing full Bayesian calibration (which would require MCMC infrastructure beyond EpiModel's scope), demonstrate a practical three-stage workflow:

  1. Define targets: Specify empirical quantities the model must match
  2. Coarse grid search: Sweep over a parameter grid, run simulations, identify the region of parameter space that produces target-consistent output
  3. Fine-tuning: Narrow the grid and refine, or manually adjust based on diagnostic plots

This is the workflow most EpiModel users actually follow, and formalizing it as a template is valuable.

Calibration Targets

Use synthetic "observed data" (generated from a known true model) so the example is self-contained and has a known ground truth:

Target Observed Value Source (simulated)
HIV/STI prevalence at steady state 12% Generated from true model
Mean partnership duration ~40 weeks From survey/behavioral data
Mean degree ~0.8 From survey/behavioral data
Population size (stable) ~500 Demographic data
Incidence rate (new infections/person-year) 0.05 Surveillance data

Parameters to Calibrate

Parameter Role Calibration Approach
inf.prob Transmission probability Calibrate to match prevalence and incidence targets
Target stats for ~edges Mean degree Calibrate to match behavioral survey data
Dissolution coefficient Partnership duration Calibrate to match partnership duration data
departure.rate Background mortality Set from demographic data (known)
arrival.rate Set equal to departure.rate Derived (not calibrated)

ERGM Parameterization

Demonstrate the two-phase calibration:

Phase 1 — Network calibration:

  • ~edges: Target from behavioral survey (mean degree = 0.8 → target edges = 200 for n = 500)
  • ~nodematch("risk_group"): Target from mixing data (e.g., 70% of partnerships are within-group)
  • ~concurrent: Target from concurrency data (e.g., 10% of population has concurrent partners)

Demonstrate using `netdx()` to verify the ERGM reproduces the network targets before proceeding to epidemic calibration.

Phase 2 — Epidemic calibration:
With the network fixed, sweep over `inf.prob` to match prevalence and incidence targets.

Workflow Steps

  1. Set known parameters: Demographics (arrival/departure rates), partnership duration from surveys
  2. Estimate ERGM: Fit the network model to behavioral targets using `netest()`
  3. Diagnose network fit: Use `netdx()` to verify — demonstrate what good vs. bad fit looks like
  4. Define epidemic parameter grid: Range of `inf.prob` values to sweep
  5. Run batch simulations: For each `inf.prob`, run multiple simulations
  6. Extract calibration diagnostics: For each parameter set, compute mean prevalence, incidence, etc. at steady state
  7. Compare to targets: Plot simulated vs. observed for each target
  8. Select best-fitting parameters: Identify the parameter set(s) that minimize distance to all targets simultaneously
  9. Validation run: Run the calibrated model with more simulations and verify targets are met

Analyses

  • Calibration diagnostic plot: Simulated prevalence vs. `inf.prob` with horizontal line at target — visually identify the calibrated value
  • Multi-target calibration: 2D heat map of (prevalence error + incidence error) over a parameter grid
  • Network diagnostics: `netdx()` plots showing edge count and dissolution over time — demonstrate what good ERGM fit looks like
  • Posterior predictive check: Run the calibrated model and overlay simulated trajectories on "observed" data — do they match?
  • Sensitivity of calibration: Show that small changes in the prevalence target lead to different calibrated `inf.prob` — illustrates identifiability

Implementation Notes

The example generates "observed data" at the top from a known true model, then proceeds to calibrate as if the true parameters were unknown. This means:

  • The ground truth is available for validation
  • No external data files needed
  • The example is fully self-contained and reproducible

The calibration loop should use a clean pattern:

# Define targets
targets <- list(prevalence = 0.12, incidence = 0.05)

# Parameter grid
inf_probs <- seq(0.05, 0.30, by = 0.025)

# Calibration sweep
calib_results <- lapply(inf_probs, function(ip) {
  param <- param.net(inf.prob = ip, ...)
  sim <- netsim(est, param, init, control)
  df <- as.data.frame(sim)
  # Extract steady-state statistics
  ...
})

# Find best fit
distances <- sapply(calib_results, function(r) {
  (r$prevalence - targets$prevalence)^2 +
  (r$incidence - targets$incidence)^2
})
best_fit <- inf_probs[which.min(distances)]

Relationship to Existing Examples

  • Uses a simple SIS model (like TestAndTreatIntervention) but focuses on the calibration workflow
  • Demonstrates `netdx()` more thoroughly than any other example (most examples include it briefly)
  • Complements the Sensitivity Analysis example (New example: Parameter sensitivity analysis and scenario sweeps #55) — calibration finds the right parameters, sensitivity analysis explores around them
  • Provides the methodological foundation that every real-world EpiModel application needs

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions