Output reader (binary .out file)#
Note
Engine: OpenSWMM 6 — refactored. Documents
openswmm.engine.OutputReader. For the legacy SWMM 5
reader see Legacy SWMM 5 Output Reader.
The OutputReader reads the binary .out file produced by
either the v6 or the legacy engine. It is independent of any running
solver — open it on a path and query results.
Reference: openswmm_output.h.
Class signature#
class OutputReader:
def __init__(self, path: str) -> None: ...
def __enter__(self) -> "OutputReader": ...
def __exit__(self, exc_type, exc_value, traceback) -> None: ...
def close(self) -> None: ...
Use the context-manager form so the file handle closes on error:
with OutputReader("model.out") as out:
...
Key methods#
File metadata#
Method |
Returns |
|---|---|
|
Output-file format version. |
|
|
|
Number of reporting periods written. |
|
Decimal-day timestamp of the first report. |
|
Reporting interval in seconds. |
|
Time at reporting period |
|
Non-zero if the engine flagged an error. |
Element counts & ids#
Method |
Returns |
|---|---|
|
Counts per element kind. |
|
String id for an integer index. |
Time-series queries (one element, all times)#
Method |
Returns |
|---|---|
|
Time series for one subcatchment variable. |
|
Time series for one node variable. |
|
Time series for one link variable. |
|
Time series for a system-level variable. |
The variable enums are OutSubcatchVar, OutNodeVar,
OutLinkVar, OutSystemVar.
Snapshot queries (all elements, one time)#
Method |
Returns |
|---|---|
|
One subcatchment value / all subcatchments at one time. |
|
One node value / all nodes at one time. |
|
One link value / all links at one time. |
|
One system-level value at one time. |
The *_attribute flavours return numpy arrays — use them for
post-processing every element at once.
End-to-end example#
from openswmm.engine import OutputReader, OutNodeVar, OutLinkVar
with OutputReader("model.out") as out:
n = out.get_period_count()
print(f"{n} reporting periods, step = {out.get_report_step()} s")
# Time series for one node and one link:
t = [out.get_period_time(i) for i in range(n)]
d = out.get_node_series(out.get_node_id(0), OutNodeVar.DEPTH)
q = out.get_link_series(out.get_link_id(0), OutLinkVar.FLOW_RATE)
print(f"first node depth: peak = {max(d):.3f}")
print(f"first link flow: peak = {max(q):.3f}")
Common recipes#
Build a NumPy depth matrix (T × n_nodes)#
import numpy as np
from openswmm.engine import OutputReader, OutNodeVar
with OutputReader("model.out") as out:
T = out.get_period_count()
N = out.get_node_count()
depths = np.empty((T, N), dtype=np.float64)
for t in range(T):
depths[t] = out.get_node_attribute(t, OutNodeVar.DEPTH)
# depths.shape == (T, N) ; ready for vectorised analysis
Plot one node’s depth with matplotlib#
import matplotlib.pyplot as plt
from openswmm.engine import OutputReader, OutNodeVar
with OutputReader("model.out") as out:
n = out.get_period_count()
t = [out.get_period_time(i) for i in range(n)]
d = out.get_node_series("J1", OutNodeVar.DEPTH)
plt.plot(t, d); plt.title("J1 depth"); plt.xlabel("time (days)")
plt.ylabel("depth"); plt.show()
Convert one snapshot to pandas#
import pandas as pd
from openswmm.engine import OutputReader, OutLinkVar
with OutputReader("model.out") as out:
last = out.get_period_count() - 1
flows = out.get_link_attribute(last, OutLinkVar.FLOW_RATE)
ids = [out.get_link_id(i) for i in range(out.get_link_count())]
df = pd.DataFrame({"link": ids, "flow": flows}).set_index("link")
Detect peak flooding across the run#
import numpy as np
with OutputReader("model.out") as out:
T = out.get_period_count()
N = out.get_node_count()
peak = np.zeros(N)
for t in range(T):
peak = np.maximum(peak, out.get_node_attribute(t, OutNodeVar.OVERFLOW))
flooded = [
(out.get_node_id(i), peak[i])
for i in range(N) if peak[i] > 0.0
]
for name, q in sorted(flooded, key=lambda kv: -kv[1]):
print(f" {name:<12} peak overflow = {q:.3f}")
Bulk arrays#
The *_attribute methods are the bulk surface — they return
np.ndarray[float64] of the appropriate length:
get_subcatch_attribute(period, var)→ shape(n_subcatch,)get_node_attribute(period, var)→ shape(n_nodes,)get_link_attribute(period, var)→ shape(n_links,)
For per-element series (one element, all times), the *_series
methods return Python lists. Wrap with np.asarray() as needed.
EngineState requirements & exceptions#
The OutputReader is independent of the engine — there is no
EngineState to honour. All queries are valid as long as the
file is open.
Common exceptions:
FileNotFoundError— path does not exist.ValueError— file format incompatible.EngineError— file present but corrupt (truncated, bad header, schema mismatch).
Open files are closed automatically when the context manager exits or
when the object is garbage-collected; calling close() is safe at
any time and is a no-op on a closed reader.
See also#
Running a simulation — Solver — runs that produce the
.outfile.Statistics — accumulated cumulative statistics during the run (peaks, durations) without needing the
.outfile.Legacy SWMM 5 Output Reader — the legacy SWMM 5 reader.