Skip to main content
Ctrl+K

OpenSWMM

  • Installation
  • Quickstart
  • Concepts & engine lifecycle
  • OpenSWMM 6 User Guide (refactored engine)
  • Legacy SWMM 5 — Compatibility Layer
    • Legacy SWMM 5 Solver
    • Legacy SWMM 5 Output Reader
    • Migrating from SWMM 5 to v6
    • API Reference
    • License
    • C/C++ Engine Docs
  • GitHub
  • PyPI
  • Installation
  • Quickstart
  • Concepts & engine lifecycle
  • OpenSWMM 6 User Guide (refactored engine)
  • Legacy SWMM 5 — Compatibility Layer
    • Legacy SWMM 5 Solver
    • Legacy SWMM 5 Output Reader
    • Migrating from SWMM 5 to v6
    • API Reference
    • License
    • C/C++ Engine Docs
  • GitHub
  • PyPI

Section Navigation

  • Running a simulation — Solver
  • Error handling, edge cases & debugging
  • Nodes
  • Links
  • Subcatchments
  • Rain gages
  • External inflows
  • Advanced forcing
  • Control rules
  • Tables (time series, curves, patterns)
  • Pollutants
  • Water quality (landuse, buildup, washoff, treatment)
  • Output reader (binary .out file)
  • Hot start
  • Mass balance
  • Statistics
  • Programmatic model construction
  • Model editing (deletion + type conversion)
  • Infrastructure (transects, streets, inlets, LIDs)
  • Spatial (CRS, coordinates, geometry)
  • OpenSWMM 6 User Guide (refactored engine)
  • Links

Links#

Note

Engine: OpenSWMM 6 — refactored. This page documents the openswmm.engine.Links class. Legacy SWMM 5 users access links through the enum-driven getValue / setValue API on openswmm.legacy.engine.Solver — see Legacy SWMM 5 Solver.

Conduits, pumps, orifices, weirs, and outlets — every connection between two nodes. The Links class is the single entry point for reading link properties at configure time and link state during a running simulation.

Reference: openswmm_links.h.


Class signature#

class Links:
    def __init__(self, solver: Solver) -> None: ...
  • solver — an active Solver.

A single Links instance covers every link in the model. Work via integer indices (fast) or string ids (convenient).


Key methods#

Identity & topology#

Method

Returns

count()

Number of links.

get_index(id)()

Integer index for a string id.

get_id(idx)()

String id for an integer index.

get_type(idx)()

LinkType (CONDUIT, PUMP, …).

get_from_node(idx)()

Upstream node integer index.

get_to_node(idx)()

Downstream node integer index.

Geometry#

Method

Returns

get_length()

Conduit length, model length units.

get_roughness()

Manning’s n.

get_slope()

Slope (computed from invert offsets).

get_offset_up()

Upstream offset above the from-node invert.

get_offset_dn()

Downstream offset above the to-node invert.

get_xsect()

(shape, geom1, geom2, geom3, geom4).

Each get_* has a matching set_* valid in OPENED. Use set_xsect() to update the cross-section; get_xsect_info() returns the shape and geometry as a structured object instead of a plain tuple.

Hydraulic state#

Method

Returns

get_flow()

Current flow.

get_depth()

Current depth at the link midpoint.

get_velocity()

Current cross-sectional velocity.

get_capacity()

Fraction of full-depth capacity used.

get_volume()

Current volume in the link.

Control settings#

Method

Action

set_control_setting() / get_control_setting()

Active control setting (0–1).

set_target_setting() / get_target_setting()

Target setting (controls ramp toward).

set_closed() / get_closed()

Force link fully closed / open.

Pump-specific#

Method

Action / returns

get_pump_curve() / set_pump_curve()

Pump curve index.

get_pump_init_state() / set_pump_init_state()

Initial on/off state.

Weir / orifice#

Method

Action / returns

get_crest_height() / set_crest_height()

Crest height above invert.

get_discharge_coeff() / set_discharge_coeff()

Discharge coefficient.

get_end_contractions() / set_end_contractions()

Number of end contractions (rect weirs).

get_loss_coeff() / set_loss_coeff()

Entrance / mid / exit loss coefficients.

get_flap_gate() / set_flap_gate()

Has flap gate?

Other#

Method

Action / returns

set_initial_flow()

Initial flow at start of simulation.

set_max_flow()

Cap on link flow magnitude.

set_seep_rate() / get_seep_rate()

Seepage rate (conduits).

set_culvert_code()

HDS-5 culvert geometry code.

set_flow()

Force flow this step (one-shot).


End-to-end example#

from openswmm.engine import Solver, Links, Nodes, LinkType, EngineState

with Solver("site_drainage.inp", "site_drainage.rpt", "site_drainage.out") as s:
    links = Links(s)
    nodes = Nodes(s)
    print(f"Model has {links.count()} links")

    # describe the network
    for i in range(links.count()):
        kind = LinkType(links.get_type(i)).name
        up = nodes.get_id(links.get_from_node(i))
        dn = nodes.get_id(links.get_to_node(i))
        print(f"  {links.get_id(i):<12}  {kind:<10}  {up} → {dn}")

    # watch a single conduit
    c1 = links.get_index("C1")
    peak_q = 0.0
    while s.state == EngineState.RUNNING:
        if s.step() != 0:
            break
        q = links.get_flow(c1)
        if q > peak_q:
            peak_q, t_peak = q, s.elapsed
    print(f"C1 peak flow = {peak_q:.3f} cfs at t={t_peak*24:.2f} h")

Common recipes#

Open / close a regulator on schedule#

gate = links.get_index("G1")
while s.state == EngineState.RUNNING:
    if s.step() != 0:
        break
    h = s.elapsed * 24.0
    # close the gate during peak rainfall (hour 6-9)
    links.set_target_setting(gate, 0.0 if 6.0 <= h <= 9.0 else 1.0)

(For declarative / rule-based control, prefer Control rules.)

Pump on / off based on upstream depth#

p1   = links.get_index("PUMP1")
sumi = nodes.get_index("WET_WELL")
on_threshold, off_threshold = 4.0, 1.5
pumping = False
while s.state == EngineState.RUNNING:
    if s.step() != 0:
        break
    d = nodes.get_depth(sumi)
    if not pumping and d >= on_threshold:
        pumping = True
        links.set_target_setting(p1, 1.0)
    elif pumping and d <= off_threshold:
        pumping = False
        links.set_target_setting(p1, 0.0)

Re-jig a conduit’s geometry mid-warm-up#

from openswmm.engine import XSectShape

# Done once after open(), before initialize()
s.open()
c1 = links.get_index("C1")
links.set_xsect(c1, XSectShape.CIRCULAR, 1.5)        # bump from 1.0 to 1.5 m
s.initialize()
s.start()
# ... loop ...

Identify pipes that ran over capacity#

overcap = []
while s.state == EngineState.RUNNING:
    if s.step() != 0:
        break
    for i in range(links.count()):
        if links.get_capacity(i) >= 0.999 and i not in overcap:
            overcap.append(i)
print("overcapacity:", [links.get_id(i) for i in overcap])

(For accumulated link statistics, prefer Statistics — Statistics.link_stats(...) exposes this directly.)


Bulk arrays#

Method

Returns / accepts

get_flows_bulk()

All link flows (np.ndarray[float64], shape (n_links,)).

set_flows_bulk(arr)()

Force flows for every link.

get_depths_bulk()

All link mid-point depths.

get_quality_bulk(p)()

Pollutant p concentration per link.

Same memory-aliasing rule as Nodes: the returned array shares memory with engine scratch space; .copy() if you keep it past the next call.

Vectorised peak detection across all links:

import numpy as np

peaks = np.zeros(links.count(), dtype=np.float64)
while s.state == EngineState.RUNNING:
    if s.step() != 0:
        break
    peaks = np.maximum(peaks, np.abs(links.get_flows_bulk()))

for i, q in enumerate(peaks):
    print(f"  {links.get_id(i):<12}  peak |Q| = {q:.3f}")

EngineState requirements & exceptions#

Method group

Required state

Notes

identity / topology accessors

OPENED or later

Topology is fixed once parsed.

geometry accessors (get_*)

OPENED or later

n/a

geometry setters (set_*)

OPENED

Modifying mid-run is generally invalid.

hydraulic state accessors

RUNNING or ENDED

Need at least one step.

control setters

RUNNING

One-shot; overwritten if a control rule fires next step.

pump / weir / orifice config

OPENED or RUNNING

Some setters check LinkType and raise INVALID_TYPE for the wrong link kind.

*_bulk accessors

same as scalar

n/a

Common EngineError codes:

  • INVALID_INDEX — integer index out of range.

  • INVALID_TYPE — calling a pump-only accessor on a conduit, etc.

  • NOT_FOUND — string id not in the model.


See also#

  • Nodes — the upstream / downstream endpoints.

  • Control rules — declarative rules for open / close / setting changes.

  • Advanced forcing — sticky cross-step overrides on a link’s flow.

  • Statistics — accumulated peak flow, peak depth, hours surcharged, etc., per link.

  • Output reader (binary .out file) — link time-series from a finished .out file.

previous

Nodes

next

Subcatchments

On this page
  • Class signature
  • Key methods
    • Identity & topology
    • Geometry
    • Hydraulic state
    • Control settings
    • Pump-specific
    • Weir / orifice
    • Other
  • End-to-end example
  • Common recipes
    • Open / close a regulator on schedule
    • Pump on / off based on upstream depth
    • Re-jig a conduit’s geometry mid-warm-up
    • Identify pipes that ran over capacity
  • Bulk arrays
  • EngineState requirements & exceptions
  • See also
Show Source

© Copyright 2026 Caleb Buahin.

Created using Sphinx 9.1.0.

Built with the PyData Sphinx Theme 0.15.2.