Quickstart#

Note

Engine: OpenSWMM 6 — refactored. All examples on this page use openswmm.engine. For the legacy SWMM 5 API see Legacy SWMM 5 — Compatibility Layer.

A 10-minute walkthrough that runs a small SWMM model end-to-end.

If you have not yet installed the package, see Installation.


1. Run a model from a .inp file#

The Solver is the entry point. It owns the engine handle, loads the model, and drives the simulation forward in time.

from openswmm.engine import Solver, EngineState

with Solver("model.inp", "model.rpt", "model.out") as s:
    while s.state == EngineState.RUNNING:
        if s.step() != 0:
            break       # non-zero return = engine error

The context manager:

  • Calls open()initialize()start() on entry.

  • Calls end()report()close()destroy() on exit, so the .rpt and .out files are flushed and the engine handle released even if your loop raises.

If you do not need a binary output file, pass an empty string for the out argument: Solver("model.inp", "model.rpt", "").



3. Vectorise with NumPy bulk methods#

Per-element accessors cross the Python/C boundary once per call. When you want every node’s depth in one go, use the *_bulk family — they return a contiguous numpy.ndarray filled in one C call:

import numpy as np
from openswmm.engine import Solver, Nodes, Links, EngineState

with Solver("model.inp", "model.rpt", "model.out") as s:
    nodes = Nodes(s)
    links = Links(s)

    depths_history = []
    flows_history  = []

    while s.state == EngineState.RUNNING:
        if s.step() != 0:
            break
        depths_history.append(nodes.get_depths_bulk().copy())
        flows_history.append(links.get_flows_bulk().copy())

    depths = np.stack(depths_history)   # shape (T, n_nodes)
    flows  = np.stack(flows_history)    # shape (T, n_links)

The .copy() is intentional — the returned array shares memory with an internal scratch buffer that the engine reuses on the next call. If you only read it once and discard it, the copy is unnecessary.


4. Inject runtime forcings#

To override an inflow, rainfall, or rule action without editing the .inp file, use the per-domain setters or the Forcing API.

Lateral inflow (one-shot — overwritten by the engine on the next step):

nodes.set_lateral_inflow("J1", 1.5)     # cfs (or m³/s, per FLOW_UNITS)

Sticky override that survives every step until you clear it:

from openswmm.engine import Forcing, ForcingMode

forcing = Forcing(s)
forcing.node_lat_inflow("J1", 1.5, ForcingMode.REPLACE, persist=True)
# … run …
forcing.clear_all()

See Advanced forcing for the full forcing model.


5. Read the binary .out file after the run#

After the run completes, point an OutputReader at the .out file to query time-series data:

from openswmm.engine import OutputReader, OutNodeVar

with OutputReader("model.out") as reader:
    n = reader.get_period_count()
    # Resolve "J1" to its zero-based index by scanning ids
    j1 = next(
        i for i in range(reader.get_node_count())
        if reader.get_node_id(i) == "J1"
    )
    depth_series = reader.get_node_series(
        j1, OutNodeVar.DEPTH, start=0, end=n - 1,
    )
    print(n, "steps, depth peak =", float(depth_series.max()))

The OutputReader is independent of any running solver — you can use it on a file produced by any past run.


6. Build a model in Python (no .inp file required)#

The ModelBuilder constructs a complete model programmatically:

from openswmm.engine import (
    ModelBuilder, NodeType, LinkType, XSectShape, EngineState,
)

m = ModelBuilder()
m.add_node("J1", NodeType.JUNCTION)
m.add_node("OUT1", NodeType.OUTFALL)
m.add_link("C1", LinkType.CONDUIT)
m.set_link_nodes(0, 0, 1)              # link 0 from node 0 to node 1
m.set_link_length(0, 300.0)
m.set_link_roughness(0, 0.013)
m.set_link_xsect(0, XSectShape.CIRCULAR, 1.0)
m.validate()
m.finalize()

solver = m.to_solver()                 # ready to run
solver.start()
while solver.state == EngineState.RUNNING:
    if solver.step() != 0:
        break
solver.end()
solver.destroy()

See Programmatic model construction for the full builder API and Model editing (deletion + type conversion) for in-place modification of an existing model.


Where to next?#