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 activeSolver.
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 |
|---|---|
|
Number of links. |
|
Integer index for a string id. |
|
String id for an integer index. |
|
|
|
Upstream node integer index. |
|
Downstream node integer index. |
Geometry#
Method |
Returns |
|---|---|
|
Conduit length, model length units. |
|
Manning’s n. |
|
Slope (computed from invert offsets). |
|
Upstream offset above the from-node invert. |
|
Downstream offset above the to-node invert. |
|
|
Each get_* has a matching set_* valid in OPENED. The
set_link_xsect() flavour from ModelBuilder is preferred
when constructing a model from scratch.
Hydraulic state#
Method |
Returns |
|---|---|
|
Current flow. |
|
Current depth at the link midpoint. |
|
Current cross-sectional velocity. |
|
Fraction of full-depth capacity used. |
|
Current volume in the link. |
Control settings#
Method |
Action |
|---|---|
|
Active control setting (0–1). |
|
Target setting (controls ramp toward). |
|
Force link fully closed / open. |
Pump-specific#
Method |
Action / returns |
|---|---|
|
Pump curve index. |
|
Initial on/off state. |
Weir / orifice#
Method |
Action / returns |
|---|---|
|
Crest height above invert. |
|
Discharge coefficient. |
|
Number of end contractions (rect weirs). |
|
Entrance / mid / exit loss coefficients. |
|
Has flap gate? |
Other#
Method |
Action / returns |
|---|---|
|
Initial flow at start of simulation. |
|
Cap on link flow magnitude. |
|
Seepage rate (conduits). |
|
HDS-5 culvert geometry code. |
|
Force flow this step (one-shot). |
End-to-end example#
from openswmm.engine import Solver, Links, Nodes, LinkType
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.step():
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.step():
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.step():
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_link_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.step():
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 |
|---|---|
|
All link flows ( |
|
Force flows for every link. |
|
All link mid-point depths. |
|
Pollutant |
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.step():
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 |
|
Topology is fixed once parsed. |
geometry accessors ( |
|
n/a |
geometry setters ( |
|
Modifying mid-run is generally invalid. |
hydraulic state accessors |
|
Need at least one step. |
control setters |
|
One-shot; overwritten if a control rule fires next step. |
pump / weir / orifice config |
|
Some setters check |
|
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
.outfile.