An eWaterCycle plugin for the EPA Storm Water Management Model (SWMM).
This plugin wraps the pySWMM Simulation object in
the Basic Model Interface (BMI) and runs it inside
a grpc4bmi container, so SWMM can be driven
step-by-step from Python and coupled to other models in the eWaterCycle
framework.
The containerized BMI model lives in a separate repository:
eWaterCycle/swmm-bmi. This plugin only
provides the thin eWaterCycle wrapper around the published container image
(ghcr.io/ewatercycle/swmm-grpc4bmi).
SWMM is the EPA's dynamic rainfall-runoff simulation model, used for single-event or long-term simulation of runoff quantity and quality, primarily in urban areas. A SWMM model represents the drainage network as subcatchments, nodes (junctions, outfalls, storage), links (conduits, pumps, weirs) and rain gages.
pySWMM is an open-source tool that provides "Pythonic"
access to the SWMM data model. Its Simulation object lets you step through a
running simulation, read and modify network state at each step, and post-process
results without manipulating SWMM input files directly. eWaterCycle has wrapped
this Simulation object in BMI — so for the most part you can use the model as
if it were a pySWMM Simulation, but through the standardized BMI calls
(initialize, update, get_value, set_value, finalize).
Cite pySWMM: McDonnell, B., Ratliff, K., Tryby, M., Wu, J. X., & Mullapudi, A. (2020). PySWMM: The Python Interface to Stormwater Management Model (SWMM). https://doi.org/10.21105/joss.02292
Install this package alongside your eWaterCycle installation:
pip install ewatercycle-swmmSWMM then becomes available as one of the eWaterCycle models:
from ewatercycle.models import SWMMThe container image is pulled automatically the first time you run the model, so a working container runtime (Docker or Apptainer, as configured in eWaterCycle) is required.
To run the model you need a SWMM input file (example.inp). This file describes
the drainage network and the simulation options. It can be created with QGIS via
the generate_swmm_inp
plugin, or with any other SWMM model builder.
Note: any external data files referenced by the
.inp(e.g. a time seriesexample.dat) must sit next to the.inpin the parameter-set directory, since SWMM resolves them relative to the input file. The.inpand its data files are read directly from that directory — they are not copied into the run directory.
The input file is provided to eWaterCycle through a ParameterSet:
from pathlib import Path
from ewatercycle.base.parameter_set import ParameterSet
swmm_dir = Path.home() / "ewatercycle-swmm"
input_file = swmm_dir / "example.inp"
parameters = ParameterSet(
name="SWMM_parameter_files",
directory=swmm_dir,
config=input_file,
target_model="swmm",
)A worked example is provided in
demo_containerized_model.ipynb, which
reproduces the "Latte" example from the pySWMM website (example.inp). The
essentials:
from ewatercycle.models import SWMM
model = SWMM(parameter_set=parameters)
cfg_file, _ = model.setup()
model.initialize(cfg_file)SWMM exposes its elements through four BMI grids:
| Grid | Element type |
|---|---|
| 0 | Subcatchments |
| 1 | Nodes |
| 2 | Links |
| 3 | Rain gages |
n_sc = model.get_grid_size(0) # subcatchments
n_nodes = model.get_grid_size(1) # nodes
n_links = model.get_grid_size(2) # linksThe model is advanced one step at a time with update(), reading variables with
get_value() along the way — the standard BMI pattern:
import numpy as np
times, node_depth, link_flow = [], [], []
while model.time < model.end_time:
times.append(model.get_current_time())
node_depth.append(model.get_value("node_depth"))
link_flow.append(model.get_value("link_flow"))
model.update()
node_depth = np.array(node_depth) # shape: (n_steps, n_nodes)
link_flow = np.array(link_flow) # shape: (n_steps, n_links)| Variable | Grid | Units |
|---|---|---|
subcatchment_runoff |
subcatchments | m³/s |
node_depth |
nodes | m |
node_flooding |
nodes | m³/s |
link_flow |
links | m³/s |
Input variables can be set with set_value() before each update(), for
example to inject a lateral inflow at every node:
EXTERNAL_INFLOW_M3S = 0.05 # 50 L/s at every node, on top of storm runoff
while model.get_current_time() < model.get_end_time():
model.set_value("node_lateral_inflow", np.full(n_nodes, EXTERNAL_INFLOW_M3S))
model.update()| Variable | Grid | Units |
|---|---|---|
node_lateral_inflow |
nodes | m³/s (via pySWMM Node.generated_inflow) |
precipitation |
rain gages | in/hr or mm/hr (depends on flow units) |
Always finalize the model when you are done. This shuts down the underlying container so it does not keep running in the background and consuming resources:
model.finalize()SWMMis aContainerizedModelthat points at theghcr.io/ewatercycle/swmm-grpc4bmiimage built from eWaterCycle/swmm-bmi.setup()writes aswmm_config.jsonconfiguration file (read by the container oninitialize()) into a per-run directory. It points the container at the.inpin place in the parameter-set directory — which eWaterCycle mounts into the container — rather than copying it. SWMM's writable outputs (the.rptreport and.outbinary) are directed into the run directory instead, so nothing is copied on each run.- Any attribute eWaterCycle does not expose itself is delegated to the underlying
BMI/pySWMM
Simulationobject, so you can largely treat the model like a pySWMMSimulation.
For the BMI implementation, the container build, and the full list of exposed variables, see the swmm-bmi repository.
Install the package with its development dependencies and run the test suite:
pip install -e .[dev]
pytestThe tests are intentionally lightweight and do not start a container or run the model:
tests/test_metadata.pychecks the package metadata (version, plugin entry point, container image reference) and the bundledexample.inp. These need only the standard library, so they run anywhere.tests/test_model.pychecks theSWMMwrapper class wiring. These requireewatercycleto be importable and are skipped automatically when it is not installed.
On a machine without ewatercycle you will see the metadata tests pass and the
model tests skipped; with ewatercycle installed all tests run.
ewatercycle-swmm is distributed under the terms of the
BSD-2-Clause license, following
the license of pySWMM. See
LICENSE.txt.