Swmm Uncertainty
/install swmm-uncertainty
SWMM Uncertainty
Part of Agentic SWMM — install the project first for the executable toolchain (aiswmm CLI, SWMM solver, MCP servers).
What this skill provides
- User-defined fuzzy membership functions for SWMM parameters.
- Baseline-aware triangular fuzzy numbers, where the current model value is the default triangle peak.
- Alpha-cut transformation from fuzzy membership functions to parameter intervals.
- LHS, random, or boundary sampling inside each alpha-cut interval.
- Monte Carlo parameter sampling for prior or calibration-informed probability distributions.
- Normal/lognormal/truncated-normal/uniform sampling with simple physical constraints such as bound parameters and greater-than rules.
- Batch propagation through SWMM by reusing the existing calibration patch-map convention.
- Normalized Shannon entropy metrics for output ensembles, such as hydrograph entropy over time.
- Machine-readable uncertainty summaries for output envelopes, entropy records, and failed/invalid samples.
- Sensitivity-analysis screening with three sub-methods (OAT / Morris / Sobol') sharing one entry point (
scripts/sensitivity.py). - Rainfall-forcing ensembles: time-series perturbation of an observed rainfall record (gaussian, multiplicative, AR(1), intensity_scaling) or IDF-curve sampling of design storms (Chicago / Huff / SCS Type II), with optional per-realisation SWMM runs and ensemble envelope aggregation.
This skill is intentionally separate from swmm-calibration.
swmm-calibrationasks: which parameter set best matches observations?swmm-uncertaintyasks: 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
scripts/fuzzy_membership.py- parses and validates crisp, interval, triangular, and trapezoidal fuzzy parameter specs
- resolves
baseline: "from_model"from the base INP through the patch map - computes alpha-cut intervals
scripts/sampling.py- generates parameter sets from alpha-cut intervals
- supports
lhs,random, andboundary
scripts/probabilistic_sampling.py- generates Monte Carlo parameter sets from probability distributions
- supports
uniform,normal,truncnorm, andlognormal - supports simple constraints such as
bind,greater_than, andless_than
scripts/parameter_recommender.py- inspects an INP and recommends prior Monte Carlo parameters that are actually present in the model
- reports the evidence boundary so prior ranges are not mistaken for calibrated posterior ranges
scripts/monte_carlo_propagate.py- extracts node-flow ensembles from Monte Carlo trial
.outfiles - calls
entropy_metrics.pyto produce node entropy JSON records - plots normalized output entropy curves for selected nodes
- extracts node-flow ensembles from Monte Carlo trial
scripts/entropy_metrics.py- calculates normalized discrete Shannon entropy for output ensembles
- summarizes ensemble p05/p50/p95/min/max time series
scripts/uncertainty_propagate.py- main CLI entry point
- writes resolved fuzzy space, alpha intervals, parameter sets, trial INPs, and summary JSON
- optionally executes SWMM and aggregates peak/continuity envelopes
scripts/sensitivity.py- unified sensitivity-analysis entry point with three sub-methods
--method oat: one-at-a-time perturbation around a baseline (port of the legacyparameter_scout)--method morris: Morris elementary-effects via SALib; sample budgetr * (k + 1); reportsmu_starandsigmaper parameter--method sobol: Sobol' indices via SALib (Saltelli sampling); sample budgetN * (2k + 2); reports first-orderS_iand total-effectS_T_i
- writes a
sensitivity_indices.jsonsummary (typically underruns/\x3Ccase>/09_audit/) - the Morris and Sobol' paths require SALib (declared in
pyproject.toml)
- unified sensitivity-analysis entry point with three sub-methods
scripts/rainfall_ensemble.py- rainfall ensemble generator with two methods
--method perturbation: noisy realisations of an observed rainfall timeseries (CSV or SWMM.dat). Models:gaussian_iid,multiplicative,autocorrelated(AR(1)),intensity_scaling. Flagpreserve_total_volumerescales 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)
- if
--base-inpis supplied, every realisation is patched into the base INP's[TIMESERIES]block and run through swmm5; peak flow + total outfall volume at--swmm-nodeare aggregated intoswmm_ensemble_stats - writes per-realisation CSVs under
\x3Crun-root>/09_audit/rainfall_realisations/and a v1 summary at\x3Crun-root>/09_audit/rainfall_ensemble_summary.json
- rainfall ensemble generator with two methods
scripts/source_decomposition.py— integration deliverable (issue #55)- pure-function over
\x3Crun_dir>/09_audit/: reads whichever raw uncertainty outputs are present (Sobol' / Morris fromsensitivity_indices.json, DREAM-ZS fromposterior_samples.csv+chain_convergence.json, SCE-UA fromcandidate_calibration.json, rainfall ensemble fromrainfall_ensemble_summary.json, MC propagation fromuncertainty_summary.json) - emits
uncertainty_source_summary.md(paper-reviewer-facing) anduncertainty_source_decomposition.json(schema_version 1.0) - the markdown body contains the five required sections: Output uncertainty envelope, Parameter contribution (Sobol' total-effect, sorted), Input contribution (rainfall ensemble vs parameter), Structural assumptions (not quantified), Cross-references
- top of the markdown carries an Evidence Boundary code block that lists every potential method as ✓ ran or ✗ not run — partial runs are still reported, just with the absent methods flagged so no method is silently dropped
- regenerate on demand with
python3 -m agentic_swmm.cli uncertainty source \x3Crun_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 all - automatically re-invoked by
skills/swmm-experiment-audit/scripts/audit_run.pywhenever any of the raw artefacts is present in09_audit/, so the integrated report always lives next to the audit note
- pure-function over
Sensitivity-analysis sub-modes
The 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).
Expected fuzzy workflow
- Prepare a base SWMM INP.
- Prepare a calibration-style
patch_map.json. - Define a
fuzzy_space.json. - Define an
uncertainty_config.json. - Run
uncertainty_propagate.py. - Inspect
uncertainty_summary.json,alpha_intervals.json, and generated trial directories.
Expected Monte Carlo / entropy workflow
- Prepare a base SWMM INP.
- Prepare a calibration-style
patch_map.json. - Define a
monte_carlo_space.jsonwith parameter distributions. - Generate parameter sets with
probabilistic_sampling.py. - Propagate the generated parameter sets through SWMM using the uncertainty runner path.
- Extract an output ensemble, such as
node,OUT_0,Total_inflow. - Calculate normalized output entropy with
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.
Fuzzy Space
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"
}
}
}
CLI Example
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.
Monte Carlo sampling example
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
Entropy metric example
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
Tecnopolo Monte Carlo smoke example
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/.
Sensitivity-analysis examples
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).
Rainfall ensemble examples
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.
Rainfall ensemble — methods at a glance
| 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.
Uncertainty source decomposition — integration deliverable
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/\x3Ccase>
# writes:
# runs/\x3Ccase>/09_audit/uncertainty_source_summary.md
# runs/\x3Ccase>/09_audit/uncertainty_source_decomposition.json (schema_version 1.0)
The markdown body has five fixed sections per the PRD template:
- Output uncertainty envelope — MC propagation peak-flow envelope + rainfall-driven peak-flow envelope when both exist.
- Parameter contribution (Sobol' total-effect, sorted) — Sobol' S_T_i ranking; falls back to Morris mu_star with an explicit "screening only" note when Morris ran instead.
- Input contribution (rainfall ensemble vs parameter) — side-by-side rainfall stats and top-3 parameter contributions plus a textual comparison.
- Structural assumptions (not quantified) — model-structural, boundary-condition, observation-noise.
- Cross-references — relative paths to every raw artefact + posterior plots.
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:
- complete run (every method ran) →
0 - partial run (some methods absent) →
0with awarning:line on stderr naming the absent methods - no uncertainty raw outputs anywhere →
1
Outputs
The run directory contains:
fuzzy_space.resolved.jsonalpha_intervals.jsonparameter_sets.jsontrials/\x3Ctrial>/model.inptrials/\x3Ctrial>/manifest.jsonwhen SWMM execution is enableduncertainty_summary.json
The summary answers:
- What parameter interval was used at each alpha level?
- What samples were propagated?
- How many trials succeeded, failed, or were only dry-run trials?
- What peak-flow and continuity envelopes were induced by each alpha level?
- What output entropy curve was induced by the propagated ensemble?
MVP Assumptions
- Fuzzy analysis focuses on epistemic parameter uncertainty through membership functions.
- Monte Carlo analysis supports prior or calibration-informed probability distributions.
- The current model value is treated as the most plausible value for compact triangular specs.
- Entropy is calculated from SWMM output ensembles; it is parameter-induced output entropy, not parameter entropy and not calibration performance.
- Real SWMM propagation depends on
swmm5being installed. - Hydrograph goodness-of-fit metrics remain in
swmm-calibration; this skill can be extended later to call that observed-flow path.
- Make sure OpenClaw is installed (local or Docker)
- Run the install command in chat:
/install swmm-uncertainty - After installation, invoke the skill by name or use
/swmm-uncertainty - Provide required inputs per the skill's parameter spec and get structured output
What is Swmm Uncertainty?
Parameter and forcing uncertainty propagation and sensitivity analysis for EPA SWMM. Use when an agent needs to (1) propagate parameter uncertainty through S... It is an AI Agent Skill for Claude Code / OpenClaw, with 43 downloads so far.
How do I install Swmm Uncertainty?
Run "/install swmm-uncertainty" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.
Is Swmm Uncertainty free?
Yes, Swmm Uncertainty is completely free, licensed under MIT-0. You can download, install and use it at no cost.
Which platforms does Swmm Uncertainty support?
Swmm Uncertainty is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).
Who created Swmm Uncertainty?
It is built and maintained by Zhonghao Zhang (@zhonghao1995); the current version is v0.7.3.