Install
openclaw skills install swmm-uncertaintyParameter and forcing uncertainty propagation and sensitivity analysis for EPA SWMM. Use when an agent needs to (1) propagate parameter uncertainty through SWMM (fuzzy alpha-cut or Monte Carlo), (2) quantify hydrograph envelopes or output entropy without treating the run as calibration, (3) screen which parameters matter using OAT / Morris elementary-effects / Sobol' indices, (4) generate a rainfall ensemble (observed-series perturbation or IDF-curve design storms) and aggregate the resulting hydrograph envelope, or (5) build the integrated paper-reviewer-facing uncertainty source decomposition (`uncertainty_source_summary.md` + `uncertainty_source_decomposition.json`) over the raw outputs of the prior steps.
openclaw skills install swmm-uncertaintyPart of Agentic SWMM — install the project first for the executable toolchain (aiswmm CLI, SWMM solver, MCP servers).
scripts/sensitivity.py).This skill is intentionally separate from swmm-calibration.
swmm-calibration asks: which parameter set best matches observations?swmm-uncertainty asks: how much output uncertainty is induced by user-defined parameter uncertainty, and which parameters drive that uncertainty?Calibration requires observed data and performance metrics such as NSE, RMSE, or KGE. This skill can run without observed data when the task is prior uncertainty propagation. The sensitivity-analysis path does read an observed series (it scores trials by RMSE against observed flow), but it answers a different question from calibration: "which parameter spread matters?" rather than "which single set is best?". When calibration outputs exist, they can be used to narrow Monte Carlo ranges or define posterior-like parameter sets.
scripts/fuzzy_membership.py
baseline: "from_model" from the base INP through the patch mapscripts/sampling.py
lhs, random, and boundaryscripts/probabilistic_sampling.py
uniform, normal, truncnorm, and lognormalbind, greater_than, and less_thanscripts/parameter_recommender.py
scripts/monte_carlo_propagate.py
.out filesentropy_metrics.py to produce node entropy JSON recordsscripts/entropy_metrics.py
scripts/uncertainty_propagate.py
scripts/sensitivity.py
--method oat: one-at-a-time perturbation around a baseline (port of the legacy parameter_scout)--method morris: Morris elementary-effects via SALib; sample budget r * (k + 1); reports mu_star and sigma per parameter--method sobol: Sobol' indices via SALib (Saltelli sampling); sample budget N * (2k + 2); reports first-order S_i and total-effect S_T_isensitivity_indices.json summary (typically under runs/<case>/09_audit/)pyproject.toml)scripts/rainfall_ensemble.py
--method perturbation: noisy realisations of an observed rainfall timeseries (CSV or SWMM .dat). Models: gaussian_iid, multiplicative, autocorrelated (AR(1)), intensity_scaling. Flag preserve_total_volume rescales each realisation to match the observed total when set--method idf: synthesised hyetographs from IDF parameters (a, b, c) with confidence intervals. Storm types: chicago (Keifer-Chu), huff (4 quartiles), scs_type_ii (canonical 24-hr Type II)--base-inp is supplied, every realisation is patched into the base INP's [TIMESERIES] block and run through swmm5; peak flow + total outfall volume at --swmm-node are aggregated into swmm_ensemble_stats<run-root>/09_audit/rainfall_realisations/ and a v1 summary at <run-root>/09_audit/rainfall_ensemble_summary.jsonscripts/source_decomposition.py — integration deliverable (issue #55)
<run_dir>/09_audit/: reads whichever raw uncertainty outputs are present (Sobol' / Morris from sensitivity_indices.json, DREAM-ZS from posterior_samples.csv + chain_convergence.json, SCE-UA from candidate_calibration.json, rainfall ensemble from rainfall_ensemble_summary.json, MC propagation from uncertainty_summary.json)uncertainty_source_summary.md (paper-reviewer-facing) and uncertainty_source_decomposition.json (schema_version 1.0)python3 -m agentic_swmm.cli uncertainty source <run_dir>; exits 0 on a complete run, 0 with a stderr warning on a partial run, and 1 when no uncertainty raw outputs exist at allskills/swmm-experiment-audit/scripts/audit_run.py whenever any of the raw artefacts is present in 09_audit/, so the integrated report always lives next to the audit noteThe three modes share the patch-map workflow and the --observed series so that trials can be scored by RMSE against the same target flow node.
| Sub-method | Config input | Sample budget | Output indices |
|---|---|---|---|
oat | base_params.json + scan_spec.json (parameter -> list of trial values) | sum_i len(scan_spec[i]) | importance, recommended_direction, suggested_next_range |
morris | parameter_space.json (parameter -> {min, max}) | r * (k + 1), r = --morris-r | mu, mu_star, sigma, mu_star_conf |
sobol | parameter_space.json (parameter -> {min, max}) | N * (2k + 2), N = --sobol-n, calc_second_order=True | S_i (first-order), S_T_i (total-effect), 95% conf |
OAT is the cheapest, Morris is the standard screening method, and Sobol' decomposes variance into first-order and total-effect contributions (more expensive but more informative).
patch_map.json.fuzzy_space.json.uncertainty_config.json.uncertainty_propagate.py.uncertainty_summary.json, alpha_intervals.json, and generated trial directories.patch_map.json.monte_carlo_space.json with parameter distributions.probabilistic_sampling.py.node,OUT_0,Total_inflow.entropy_metrics.py.If observed data are available, first run swmm-calibration and use its best, acceptable, or narrowed parameter ranges as a calibration-informed Monte Carlo input. If observed data are not available, report the analysis as prior uncertainty propagation.
For a triangular membership function, the preferred compact form is:
{
"parameters": {
"pct_imperv_s1": {
"type": "triangular",
"lower": 15.0,
"upper": 40.0,
"baseline": "from_model"
}
}
}
The resolved triangle is:
triangular(a=lower, b=current model value, c=upper)
The baseline must lie inside [lower, upper]; otherwise the configuration is invalid.
A trapezoidal function can be fully specified:
{
"parameters": {
"n_imperv_s1": {
"type": "trapezoidal",
"lower": 0.010,
"core_lower": 0.013,
"core_upper": 0.018,
"upper": 0.025
}
}
}
Or centered around the baseline:
{
"parameters": {
"n_imperv_s1": {
"type": "trapezoidal",
"lower": 0.010,
"upper": 0.025,
"core_width": 0.004,
"baseline": "from_model"
}
}
}
python3 skills/swmm-uncertainty/scripts/uncertainty_propagate.py \
--base-inp examples/todcreek/model_chicago5min.inp \
--patch-map examples/calibration/patch_map.json \
--fuzzy-space skills/swmm-uncertainty/examples/fuzzy_space.json \
--config skills/swmm-uncertainty/examples/uncertainty_config.json \
--run-root runs/uncertainty-demo \
--summary-json runs/uncertainty-demo/uncertainty_summary.json \
--dry-run
Remove --dry-run to execute SWMM for every generated trial.
python3 skills/swmm-uncertainty/scripts/probabilistic_sampling.py \
--parameter-space skills/swmm-uncertainty/examples/monte_carlo_space.json \
--samples 100 \
--seed 42 \
--out runs/uncertainty-mc/parameter_sets.json
python3 skills/swmm-uncertainty/scripts/entropy_metrics.py \
--ensemble-json skills/swmm-uncertainty/examples/entropy_ensemble.json \
--bins 10 \
--out runs/uncertainty-mc/entropy_summary.json
python3 scripts/benchmarks/run_tecnopolo_mc_uncertainty_smoke.py \
--samples 20 \
--seed 42 \
--node OUT_0 \
--scan-nodes \
--entropy-nodes J6 OUT_0
This is a prior uncertainty smoke test, not calibration. It identifies perturbable parameters in the Tecnopolo HORTON prepared INP, applies small Monte Carlo perturbations, runs SWMM, optionally ranks all junction/outfall nodes by peak-flow spread, and writes summary.json, parameter_recommendations.json, trial outputs, a rainfall-plus-flow envelope figure, J6/OUT_0 entropy JSON files, and an entropy curve figure under runs/benchmarks/tecnopolo-mc-uncertainty-smoke/.
OAT (port of the legacy parameter_scout):
python3 skills/swmm-uncertainty/scripts/sensitivity.py \
--method oat \
--base-inp examples/todcreek/model_chicago5min.inp \
--patch-map examples/calibration/patch_map.json \
--base-params examples/calibration/base_params.json \
--scan-spec examples/calibration/scan_spec.json \
--observed examples/calibration/observed_flow.csv \
--run-root runs/sensitivity-oat \
--summary-json runs/sensitivity-oat/09_audit/sensitivity_indices.json \
--swmm-node O1
Morris elementary-effects (r=10 trajectories on a 4-parameter space gives 50 swmm5 calls):
python3 skills/swmm-uncertainty/scripts/sensitivity.py \
--method morris \
--base-inp examples/todcreek/model_chicago5min.inp \
--patch-map examples/calibration/patch_map.json \
--parameter-space examples/calibration/search_space.json \
--observed examples/calibration/observed_flow.csv \
--run-root runs/sensitivity-morris \
--summary-json runs/sensitivity-morris/09_audit/sensitivity_indices.json \
--morris-r 10 \
--seed 42
Sobol' indices (N=64 on a 4-parameter space gives 640 swmm5 calls; budget is N*(2k+2)):
python3 skills/swmm-uncertainty/scripts/sensitivity.py \
--method sobol \
--base-inp examples/todcreek/model_chicago5min.inp \
--patch-map examples/calibration/patch_map.json \
--parameter-space examples/calibration/search_space.json \
--observed examples/calibration/observed_flow.csv \
--run-root runs/sensitivity-sobol \
--summary-json runs/sensitivity-sobol/09_audit/sensitivity_indices.json \
--sobol-n 64 \
--seed 42
All three modes share the same --summary-json schema header (method, parameters, sample_budget, indices). Per-parameter shapes differ by method (see the "Sensitivity-analysis sub-modes" table above).
Time-series perturbation (200 noisy realisations of an observed rainfall CSV, all run through swmm5):
python3 skills/swmm-uncertainty/scripts/rainfall_ensemble.py \
--method perturbation \
--config skills/swmm-uncertainty/examples/rainfall_perturbation_config.json \
--run-root runs/rainfall-ensemble-perturbation \
--base-inp examples/todcreek/model_chicago5min.inp \
--series-name TS_RAIN \
--swmm-node O1 \
--seed 42
IDF-curve design storm (200 hyetographs from sampled Chicago IDF params):
python3 skills/swmm-uncertainty/scripts/rainfall_ensemble.py \
--method idf \
--config skills/swmm-uncertainty/examples/rainfall_idf_config.json \
--run-root runs/rainfall-ensemble-idf \
--base-inp examples/todcreek/model_chicago5min.inp \
--series-name TS_RAIN \
--swmm-node O1 \
--seed 42
Use --dry-run to skip the SWMM execution layer and write only the realisation CSVs + rainfall-only summary statistics.
| Method | Input | Models | Output |
|---|---|---|---|
perturbation | One observed rainfall CSV / SWMM .dat | gaussian_iid, multiplicative, autocorrelated, intensity_scaling | N realisations of the observed pattern |
idf | IDF (a, b, c) with CIs + storm type | chicago, huff (4 quartiles), scs_type_ii | N synthesised hyetographs |
gaussian_iid adds zero-mean Gaussian noise (mean residual ≈ 0). multiplicative preserves the shape — Pearson correlation between observed and any realisation stays near 1. autocorrelated produces noise with lag-1 autocorrelation ≈ ar1_coefficient. intensity_scaling scales noise variance with intensity, so peaks fluctuate more than troughs.
When preserve_total_volume=true, every realisation is rescaled so its integrated rainfall depth matches the observed total. When false, totals vary across the ensemble — that variance is itself part of the propagated uncertainty.
After at least one of the prior uncertainty steps has run (sensitivity, DREAM-ZS posterior, SCE-UA calibration, rainfall ensemble, or MC propagation) the integration layer collects the raw outputs and writes a single paper-reviewer-facing report:
python3 -m agentic_swmm.cli uncertainty source runs/<case>
# writes:
# runs/<case>/09_audit/uncertainty_source_summary.md
# runs/<case>/09_audit/uncertainty_source_decomposition.json (schema_version 1.0)
The markdown body has five fixed sections per the PRD template:
The top of the markdown carries an Evidence Boundary code block that lists every potential method as ✓ or ✗:
Evidence boundary:
Sobol' SA : ✓ ran (sensitivity_indices.json)
Morris SA : ✗ not run
DREAM-ZS : ✓ ran (posterior_samples.csv)
SCE-UA : ✓ ran (candidate_calibration.json)
Rainfall ensemble: ✓ method A only (method B not run)
MC propagation : ✓ ran (uncertainty_summary.json)
The audit pipeline (skills/swmm-experiment-audit/scripts/audit_run.py) auto-runs the decomposition at audit-end whenever any uncertainty raw artefact is present in 09_audit/, so the integrated report stays in sync with the audit note.
Exit codes for the CLI:
00 with a warning: line on stderr naming the absent methods1The run directory contains:
fuzzy_space.resolved.jsonalpha_intervals.jsonparameter_sets.jsontrials/<trial>/model.inptrials/<trial>/manifest.json when SWMM execution is enableduncertainty_summary.jsonThe summary answers:
swmm5 being installed.swmm-calibration; this skill can be extended later to call that observed-flow path.