Legacy SWMM 5 Solver#
Note
Engine: SWMM 5.x — legacy. This page documents the
openswmm.legacy.engine.Solver class. For the modern
reentrant OpenSWMM 6 Solver see Running a simulation — Solver.
The legacy Solver is a thin Cython wrapper around the original
EPA SWMM 5.x C solver. The class is preserved verbatim so that scripts
written against the EPA SWMM 5.x C API or earlier openswmm
releases continue to run unchanged.
If you are starting fresh, prefer openswmm.engine.Solver. If
you have existing legacy code, this page documents what’s available and
Migrating from SWMM 5 to v6 shows how to migrate.
Class signature#
from openswmm.legacy.engine import Solver
class Solver:
def __init__(
self,
inp_file: str,
rpt_file: str = "",
out_file: str = "",
swmm_progress_callback: Callable[[float], None] = None,
stride_step: int = 0,
) -> None: ...
inp_file/rpt_file/out_file— same role as the v6 Solver: input, human-readable report, binary output.swmm_progress_callback— called periodically with the simulation fraction (0.0 → 1.0). Frequency controlled viaprogress_callbacks_per_second.stride_step— number of routing steps to advance perstep()call (0 = single step).
The class supports the context-manager and iterator protocols.
Lifecycle#
The legacy lifecycle is simpler than v6 (no explicit create):
Method |
Action |
|---|---|
|
Parse the |
|
Allocate state arrays. |
|
Start routing. |
|
Advance |
|
Stop routing. |
|
Write the human-readable |
|
Close output files. |
|
Free engine memory. |
|
Convenience: open/initialize/start/step-to-end/end/report/close. |
|
Context-manager wrapper. |
|
Iterator over |
State machine#
The legacy solver uses SolverState (different from
v6’s EngineState):
from openswmm.legacy.engine import SolverState
if solver.solver_state == SolverState.RUNNING:
...
End-to-end example#
from openswmm.legacy.engine import Solver
# Context-manager style — easiest:
with Solver("model.inp", "model.rpt", "model.out") as s:
for time, mb_err in s: # iterator stops at end of sim
if int(time * 24) % 1 == 0: # every hour
print(f"t={time*24:.2f} h mb_err={mb_err:+.4f}%")
The iterator yields (elapsed_days, continuity_error_percent) tuples
and stops naturally at the simulation end.
Manual lifecycle (when you need to inspect parsed objects between
open and initialize):
from openswmm.legacy.engine import Solver, SWMMObjects
s = Solver("model.inp", "model.rpt", "model.out")
s.open()
print("nodes:", s.get_object_count(SWMMObjects.NODE))
print("links:", s.get_object_count(SWMMObjects.LINK))
s.initialize()
s.start()
while True:
time, _ = s.step()
if time >= s.end_datetime.timestamp():
break
s.end()
s.report()
s.close()
s.finalize() # free engine memory
Reading object state — getValue / setValue#
The legacy API is enum-driven: every property read or write goes
through get_value() / set_value() keyed by an
SWMMObjects (object kind) and an attribute enum specific to
that kind.
from openswmm.legacy.engine import (
Solver, SWMMObjects, SWMMNodeProperties, SWMMLinkProperties,
)
with Solver("model.inp", "model.rpt", "model.out") as s:
# Resolve names to integer indices once
j1 = s.get_object_index(SWMMObjects.NODE, "J1")
c1 = s.get_object_index(SWMMObjects.LINK, "C1")
for _ in s:
d = s.get_value(SWMMObjects.NODE, SWMMNodeProperties.DEPTH, j1)
q = s.get_value(SWMMObjects.LINK, SWMMLinkProperties.FLOW, c1)
...
The corresponding attribute enums are:
Attribute enum |
Object kind |
|---|---|
|
Rain gages |
|
Subcatchments |
|
Nodes |
|
Links |
|
System-wide values |
For the value-oriented type tags:
Type enum |
Used by |
|---|---|
|
Identify node kind from |
|
Identify link kind. |
|
Decode the |
Common recipes#
Inject a lateral inflow at a node#
from openswmm.legacy.engine import (
Solver, SWMMObjects, SWMMNodeProperties,
)
with Solver("model.inp", "model.rpt", "model.out") as s:
j1 = s.get_object_index(SWMMObjects.NODE, "J1")
for _ in s:
s.set_value(
SWMMObjects.NODE, SWMMNodeProperties.LATERAL_INFLOW,
j1, 1.5,
)
Override a rain-gage rainfall reading#
from openswmm.legacy.engine import (
Solver, SWMMObjects, SWMMRainGageProperties,
)
with Solver("model.inp", "model.rpt", "model.out") as s:
rg1 = s.get_object_index(SWMMObjects.RAIN_GAGE, "RG1")
for time, _ in s:
# custom rainfall hyetograph injected each step
r = compute_rainfall(time)
s.set_value(
SWMMObjects.RAIN_GAGE, SWMMRainGageProperties.RAINFALL,
rg1, r,
)
Continuity / mass-balance error#
runoff_err, flow_err, qual_err = s.get_mass_balance_error()
print(f"runoff: {runoff_err:+.4f}% routing: {flow_err:+.4f}%")
Hot start — save / load#
s.save_hotstart("scenario_a.hsf")
# ... later, in a new Solver:
s.use_hotstart("scenario_a.hsf")
Progress callback#
def on_progress(fraction):
print(f" {fraction*100:5.1f}%")
with Solver("model.inp", "model.rpt", "model.out",
swmm_progress_callback=on_progress) as s:
s.progress_callbacks_per_second = 2 # twice a second
for _ in s:
pass
Per-step callbacks (start, before-step, after-step, end)#
from openswmm.legacy.engine import CallbackType
def post_step(solver):
print("step completed at t =", solver.current_datetime)
with Solver("model.inp", "model.rpt", "model.out") as s:
s.add_callback(CallbackType.POST_STEP, post_step)
for _ in s:
pass
Object-oriented wrappers (LegacyNodes, LegacyLinks, …)#
For convenience, the legacy package also exposes thin Pythonic wrappers around the enum-driven API:
These are not new API — they delegate to get_value / set_value
under the hood. They exist purely so existing object-oriented code
that was built against earlier openswmm versions continues to
work.
from openswmm.legacy.engine import Solver, LegacyNodes
with Solver("model.inp", "model.rpt", "model.out") as s:
nodes = LegacyNodes(s)
j1 = nodes["J1"]
for _ in s:
print(j1.depth)
Exceptions#
The legacy solver raises SWMMSolverException on engine errors.
The error code → message mapping is exposed through
SWMMAPIErrors and the helper get_error_message().
from openswmm.legacy.engine import (
Solver, SWMMSolverException, get_error_message,
)
try:
with Solver("nonexistent.inp", "out.rpt", "out.out") as s:
for _ in s:
pass
except SWMMSolverException as e:
print("legacy solver failed:", e)
See also#
Legacy SWMM 5 Output Reader — companion reader for the binary
.outfile.Running a simulation — Solver — the modern v6
Solver(recommended).Migrating from SWMM 5 to v6 — translate this code to OpenSWMM 6.