movici_urban_drainage_model¶
attributes¶
Attribute specifications for urban drainage (storm/sewer) simulation using pyswmm/SWMM.
Attribute naming follows the Movici documentation convention:
urban_drainage.*: urban-drainage / SWMM specific attributesshape.length: conduit length (reused from movici core)
Enum-typed attributes store an integer index into a dataset-defined enum (the
general.enum section of the dataset). The integer maps to a SWMM keyword
string (e.g. "CIRCULAR") which the simulation wrapper feeds to SWMM. The
expected keyword values for each enum are documented next to its declaration.
dataset¶
Entity definitions for urban drainage (storm/sewer) simulation.
Entity groups map the SWMM object model onto Movici geometry entities:
nodes (junctions, outfalls, storage units) are
PointEntitylinks (conduits, pumps, orifices, weirs, outlets) are
LinkEntityand route between theirfrom_node_id/to_node_idsubcatchments are
PolygonEntityrain gages are
PointEntity
Static SWMM parameters (needed to build the model) are INIT; optional
parameters and runtime control inputs are OPT; per-step simulation results
are PUB. A few attributes are PUB | OPT: they are published every step and,
when supplied, also seed the corresponding initial condition.
Note
Dividers are not modelled: SWMM treats them as ordinary junctions under
dynamic-wave routing. Coordinates (geometry.x/y) are used to write the
SWMM [COORDINATES] section; they do not affect the simulation results.
- class ConduitEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageLinkEntityDrainage conduits: pipes and channels carrying gravity flow.
- barrels¶
- cross_section_geometry¶
- cross_section_shape¶
- from_offset¶
- length¶
- roughness¶
- to_offset¶
- class DrainageLinkEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
LinkEntityBase class for drainage network links with common output attributes.
Links route between
from_node_id/to_node_id(inherited,INIT); the line geometry is not needed by the simulation.- current_setting¶
- flow¶
- flow_depth¶
- flow_volume¶
- froude_number¶
- is_ready()¶
- target_setting¶
- class DrainageNodeEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
PointEntityBase class for drainage network nodes with common output attributes.
Keeps
geometry.x/geometry.y(used to write SWMM[COORDINATES]) and drops the elevation / reference attributes that the model does not need.- flooding_rate¶
- generated_inflow¶
- hydraulic_head¶
- lateral_inflow¶
- stored_volume¶
- total_inflow¶
- total_outflow¶
- water_depth¶
- class JunctionEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageNodeEntityDrainage junctions: manholes / pipe connection nodes.
- invert_elevation¶
- max_depth¶
- ponded_area¶
- surcharge_depth¶
- class OrificeEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageLinkEntityDrainage orifices: openings (side or bottom) regulating flow.
cross_section_geometrycarries the opening dimensions (Geom1 = height, Geom2 = width for rectangular orifices; Geom1 = diameter for circular).- crest_height¶
- cross_section_geometry¶
- discharge_coefficient¶
- flap_gate¶
- orifice_shape¶
- orifice_type¶
- class OutfallEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageNodeEntityDrainage outfalls: terminal nodes with a boundary stage condition.
- fixed_stage¶
- flap_gate¶
- invert_elevation¶
- outfall_type¶
- class OutletEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageLinkEntityDrainage outlets: flow-control links with a head/depth-discharge rating.
Rating is either functional (
flow = coefficient * x ^ exponent) or a tabularrating_curveof (head_or_depth, flow) points, selected byoutlet_rating_type.- crest_height¶
- flap_gate¶
- outlet_rating_type¶
- rating_coefficient¶
- rating_curve¶
- rating_exponent¶
- class PumpEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageLinkEntityDrainage pumps: lift water from the inlet node to the outlet node.
With no
pump_curve(or anIDEALpump_curve_type) the pump passes its inlet inflow directly. Otherwise the curve defines the pump behaviour.- pump_curve¶
- pump_curve_type¶
- shutoff_depth¶
- startup_depth¶
- class RainGageEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
PointEntityDrainage rain gages: rainfall sources for subcatchments.
Rainfall is driven at runtime by another model publishing
rainfall_intensity(applied via SWMMRainGage.total_precip); there is no configured time series.- rainfall¶
- rainfall_intensity¶
- class StorageEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageNodeEntityDrainage storage units: ponds / basins / tanks.
The storage geometry is selected by
storage_geometry(FUNCTIONAL / TABULAR / CYLINDRICAL / CONICAL / PARABOLIC / PYRAMIDAL). When it is unset the shape is inferred for backward compatibility: astorage_curvemeans TABULAR, otherwise FUNCTIONAL (area = constant + coefficient * depth ^ exponent). The geometric shapes read their L/W/Z dimensions fromstorage_geometry_parameters.- invert_elevation¶
- max_depth¶
- ponded_area¶
- storage_coefficient¶
- storage_constant¶
- storage_curve¶
- storage_exponent¶
- storage_geometry¶
- storage_geometry_parameters¶
- surcharge_depth¶
- class SubcatchmentEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
PolygonEntityDrainage subcatchments: surfaces generating runoff from rainfall.
Runoff is routed to
outlet_node_idand driven by the rain gage referenced throughraingage_id. The polygon geometry is optional and used for display only, so readiness does not depend on it.Only a single subarea per subcatchment is modelled (SWMM supports more).
- area¶
- conductivity¶
- curve_number¶
- decay_constant¶
- dry_time¶
- evaporation_loss¶
- infiltration_loss¶
- initial_deficit¶
- is_ready()¶
- max_infiltration_rate¶
- min_infiltration_rate¶
- n_imperv¶
- n_perv¶
- outlet_node_id¶
- pct_zero¶
- percent_impervious¶
- rainfall¶
- raingage_id¶
- runoff¶
- s_imperv¶
- s_perv¶
- slope¶
- suction_head¶
- width¶
- class UrbanDrainageNetwork(junctions: movici_urban_drainage_model.dataset.JunctionEntity, outfalls: movici_urban_drainage_model.dataset.OutfallEntity, storage: movici_urban_drainage_model.dataset.StorageEntity, conduits: movici_urban_drainage_model.dataset.ConduitEntity, pumps: movici_urban_drainage_model.dataset.PumpEntity, orifices: movici_urban_drainage_model.dataset.OrificeEntity, weirs: movici_urban_drainage_model.dataset.WeirEntity, outlets: movici_urban_drainage_model.dataset.OutletEntity, subcatchments: movici_urban_drainage_model.dataset.SubcatchmentEntity, raingages: movici_urban_drainage_model.dataset.RainGageEntity)¶
Bases:
object- conduits: ConduitEntity¶
- junctions: JunctionEntity¶
- orifices: OrificeEntity¶
- outfalls: OutfallEntity¶
- outlets: OutletEntity¶
- pumps: PumpEntity¶
- raingages: RainGageEntity¶
- storage: StorageEntity¶
- subcatchments: SubcatchmentEntity¶
- weirs: WeirEntity¶
- class WeirEntity(name: str | None = None, optional: bool | None = None, exclude: Iterable[str] | None = None, override_exclude: Iterable[str] | None = None)¶
Bases:
DrainageLinkEntityDrainage weirs: crested structures regulating overflow.
cross_section_geometrycarries the opening dimensions (Geom1 = height, Geom2 = length, Geom3 = side slope for trapezoidal/v-notch weirs).- crest_height¶
- cross_section_geometry¶
- discharge_coefficient¶
- flap_gate¶
- weir_type¶
inp_writer¶
Helpers for synthesising a SWMM .inp input file from a Movici dataset.
pyswmm requires an .inp file to open a Simulation. Unlike
WNTR (which builds its network model in memory), there is no supported way to
construct a SWMM model purely in code, so the simulation wrapper writes a
transient .inp describing the Movici dataset and opens the simulation on it.
InpBuilder collects rows per [SECTION] and renders them into the
ordered, whitespace-delimited text that SWMM expects.
- class InpBuilder¶
Bases:
objectCollects
.inprows by section and renders the full input file.- add(section: str, *cells: Any) None¶
Append a whitespace-delimited row of cells to section.
- render() str¶
Render all collected sections into
.inptext.
- fmt_hms(seconds: float) str¶
Format a duration in seconds as a SWMM
HH:MM:SSstring.
- fmt_num(value: float, max_decimals: int | None = None) int | float¶
Coerce a numeric value for an
.inpcell, keeping full precision by default.Returns an
intfor whole values (so10rather than10.0) and otherwise the float unchanged; the builder renders it withstr(), which round-trips fullfloat64precision (SWMM parses.inpnumbers into doubles, so nothing is truncated). Passmax_decimalsto round for a more compact file.Undefined (
NaN) values are coerced to0: fixed-shape geometry attributes (e.g.cross_section_geometrywith 4 slots) often leave the slots a given SWMM shape does not use unspecified, which surfaces asNaNhere. SWMM ignores the unusedGeomcells, so emitting0is correct and avoidsint(nan)raising.
model¶
Urban drainage simulation model using pyswmm/SWMM.
This model simulates urban drainage (storm water and sewer) networks using the EPA SWMM engine through pyswmm. It performs dynamic-wave hydraulic routing and rainfall-runoff hydrology over subcatchments.
Unlike the WNTR-backed drinking-water model (which re-runs a full simulation per
update), this model keeps a single live SWMM Simulation open
and advances it forward to each Movici moment. Control inputs (rainfall, node
inflow and regulator settings) are applied to the live simulation before
advancing, because SWMM marches forward and cannot rewind.
Note
Internal SWMM controls (rules / curves) are not used; control logic should be
supplied externally through the regulator target_setting and node
generated_inflow attributes (e.g. via the Movici Rules Model).
- class Model(model_config: dict)¶
Bases:
TrackedModelUrban drainage (storm/sewer) network simulation model using SWMM.
Simulates:
Dynamic-wave hydraulic routing through conduits, pumps, orifices, weirs and outlets
Rainfall-runoff hydrology over subcatchments driven by rain gages
Junctions, outfalls and storage units as network nodes
Per-step results (depth, head, flooding, flow, runoff, …) are published; rainfall, external inflow and regulator settings may be supplied as inputs.
- classmethod get_schema_attributes()¶
Return all AttributeSpecs used by this model.
- initialize(state: TrackedState)¶
Validate the network, synthesise the
.inpand open the simulation.
- setup(state: TrackedState, logger: Logger, **kwargs)¶
Register entity groups and wire up the simulation wrapper.
- shutdown(state: TrackedState)¶
Finalise and close the simulation.
- update(state: TrackedState, moment: Moment) Moment | None¶
Advance the live simulation to moment and publish the results.
Control inputs are applied to the live simulation before advancing, since SWMM marches forward and cannot rewind. When updated again at a timestep already simulated, there is nothing to do: the (late-arriving) control changes are read from the arrays on the next forward step.
simulation_wrapper¶
Wrapper around a live pyswmm Simulation for Movici.
This module bridges Movici’s entity-based, update(moment)-driven model with
SWMM’s live, forward-only simulation engine:
the Movici dataset is synthesised into a transient
.inpfile (seeinp_writer) and a singleSimulationis opened and kept alive for the whole run;each Movici update advances that simulation to the requested moment with
step_advance+ iteration (SWMM cannot rewind, so we never step backwards);control inputs (rainfall, node inflow, regulator settings) are applied to the live objects before advancing, so they govern the step about to be taken;
per-step results are read directly off the live
Node/Link/Subcatchmentobjects (there is no results DataFrame as in WNTR).
Per-entity-group SwmmProcessors know how to (a) contribute rows to
the synthesised .inp, (b) apply control inputs and (c) read results back.
- class ConduitProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
LinkProcessor[ConduitEntity]- PREFIX: str = 'C'¶
- build_inp(builder: InpBuilder) None¶
- class IdMapper¶
Bases:
objectMaps Movici integer ids to SWMM string names (unique across all types).
Each processor uses a type-specific prefix (
"J"for junctions,"C"for conduits, …) so names are unique across entity groups and link endpoints / subcatchment outlets resolve unambiguously.- get_swmm_name(entity_id: int) str¶
- names_by_id: Dict[int, str]¶
- register(entity_id: int, swmm_name: str) None¶
- class JunctionProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
NodeProcessor[JunctionEntity]- PREFIX: str = 'J'¶
- build_inp(builder: InpBuilder) None¶
- class LinkProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
SwmmProcessor[L]Base for link processors: resolves endpoints, reads shared link outputs.
- process_changes() None¶
- write_results() None¶
- class NodeProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
SwmmProcessor[N]Base for node processors: writes coordinates, reads shared node outputs.
- process_changes() None¶
- write_results() None¶
- class OrificeProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
LinkProcessor[OrificeEntity]- PREFIX: str = 'OR'¶
- build_inp(builder: InpBuilder) None¶
- class OutfallProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
NodeProcessor[OutfallEntity]- PREFIX: str = 'OF'¶
- build_inp(builder: InpBuilder) None¶
- class OutletProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
LinkProcessor[OutletEntity]- PREFIX: str = 'OU'¶
- build_inp(builder: InpBuilder) None¶
- class PumpProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
LinkProcessor[PumpEntity]- PREFIX: str = 'PU'¶
- build_inp(builder: InpBuilder) None¶
- class RainGageProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
SwmmProcessor[RainGageEntity]- PREFIX: str = 'RG'¶
- build_inp(builder: InpBuilder) None¶
- process_changes() None¶
- write_results() None¶
- class SimulationWrapper(logger: Logger | None = None)¶
Bases:
objectOwns the live pyswmm
Simulationand its processors.- add_curve(points: ndarray, curve_type: str) str¶
Register a SWMM curve and return its generated name.
- advance_to(target_seconds: int) None¶
Step the live simulation forward until at least target_seconds.
SWMM marches forward only, so this never steps backwards. Control inputs must already have been applied (see
process_changes()).
- checkpoint(path: str | None = None) str¶
Snapshot the current simulation state to a hotstart file.
Pass the returned path to
rollback_to()to restore this exact state (and reported time) later. May be called at any point during the run.- Parameters:
path – target
.hsfpath; a temporary file (cleaned up inclose()) is created when omitted.- Returns:
the hotstart file path.
- close() None¶
Finalise and close the simulation, removing the transient
.inp.EPA-SWMM only permits one open simulation per process, so releasing the simulation (and the object collections that reference it) here is what lets a subsequent model run open its own simulation.
- configure_options(config_options: dict, general_options: dict | None = None, start_datetime: datetime | None = None) None¶
Store the model-config and dataset-general options for synthesising
[OPTIONS].Solver options (timesteps, routing) come from the model config; data options (
flow_units,infiltration_model_default) come from the dataset general section;infiltration_model_overridecomes from the model config._optionsis the merged view (general wins) used for the solver timesteps and routing.
- elapsed_seconds() float¶
- property global_infiltration_model: str¶
The
[OPTIONS] INFILTRATIONkeyword (per-row methods may override it).
- property infiltration_default: str¶
Dataset-default infiltration model (general section), else HORTON.
- property infiltration_override: str | None¶
Model-config infiltration model that forces all subcatchments, if any.
- initialize(dataset: UrbanDrainageNetwork) None¶
Build the processors, synthesise the
.inpand open the simulation.
- property is_us_units: bool¶
True when flow_units selects SWMM’s US (inch) unit system.
flow_unitsdescribes the data, so it is read from the dataset general section only (not overridable from the model config).
- links: Links | None¶
- nodes: Nodes | None¶
- process_changes() None¶
Apply all runtime control inputs to the live simulation objects.
- processors: Dict[str, SwmmProcessor]¶
- raingages: RainGages | None¶
- rollback_to(checkpoint: str) None¶
Restore the simulation to a state captured by
checkpoint().Closes the current engine (keeping the synthesised
.inp, processors and id-map) and reopens it from the checkpoint, restoring both the engine state and the reported Movici time. The interval after the checkpoint can then be re-run - e.g. with corrected control inputs.
- sim: Simulation | None¶
- property start_date: str¶
SWMM
MM/DD/YYYYstart date (from the Movici timeline, else default).
- property start_time: str¶
SWMM
HH:MM:SSstart time (from the Movici timeline, else default).
- property subarea_defaults: Dict[str, float]¶
Default subarea/infiltration parameters for the active unit system.
- subcatchments: Subcatchments | None¶
- write_results() None¶
Read the current simulation state into the PUBLISH attribute arrays.
- class StorageProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
NodeProcessor[StorageEntity]- GEOMETRIC_SHAPES = ('CYLINDRICAL', 'CONICAL', 'PARABOLIC', 'PYRAMIDAL')¶
- KNOWN_SHAPES = ('FUNCTIONAL', 'TABULAR', 'CYLINDRICAL', 'CONICAL', 'PARABOLIC', 'PYRAMIDAL')¶
- PREFIX: str = 'ST'¶
- build_inp(builder: InpBuilder) None¶
- class SubcatchmentProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
SwmmProcessor[SubcatchmentEntity]- PREFIX: str = 'S'¶
- build_inp(builder: InpBuilder) None¶
- write_results() None¶
- class SwmmProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
Generic[T]Base class translating one entity group to/from SWMM.
- PREFIX: str = ''¶
- build_inp(builder: InpBuilder) None¶
- process_changes() None¶
- swmm_name(entity_id: int) str¶
- write_results() None¶
- class WeirProcessor(wrapper: SimulationWrapper, entity_group: T)¶
Bases:
LinkProcessor[WeirEntity]- PREFIX: str = 'W'¶
- build_inp(builder: InpBuilder) None¶
swmm_source¶
SWMM .inp DataSource for the Movici dataset creator.
Reads an existing SWMM .inp file and exposes its objects as Movici dataset
creator sources, mirroring movici_drinking_water_model.epanet_source. This
is the authoring counterpart to the model’s runtime .inp synthesis: it lets
an existing SWMM model be imported into a Movici dataset.
The parser is intentionally dependency-free (no swmmio): it tokenises the
[SECTION] blocks of the .inp and exposes scalar attributes plus point /
line / polygon geometry. Enum-like columns (shapes, types) are exposed as their
SWMM keyword strings, which the dataset creator’s enum-conversion step maps to
integer indices.
- class SWMMSource(file: Path | str)¶
Bases:
MultipleEntityTypeSourceMulti-entity source for reading SWMM
.inpfiles.Registered as the
"swmm"source type for the dataset creator. Contains entity types:junctions,outfalls,storage,conduits,pumps,orifices,weirs,outlets,subcatchmentsandraingages. Access individual entity types with bracket notation:source = SWMMSource("network.inp") source["junctions"].get_attribute("invert_elevation")
The
.inpfile is parsed lazily on first entity-type access and shared across sub-sources of the sameSWMMSourceinstance.- Parameters:
file – Path to the
.inpfile
- ENTITY_TYPES = frozenset({'conduits', 'junctions', 'orifices', 'outfalls', 'outlets', 'pumps', 'raingages', 'storage', 'subcatchments', 'weirs'})¶
- classmethod from_source_info(source_info)¶
Create from a source info dictionary.
If
entity_typeis present, returns a single-entityDataSourcefor that type; otherwise returns the full multi-entity source.
- get_bounding_box()¶
- keys() Iterable[str]¶
- class SwmmInp(path: str | Path)¶
Bases:
objectA parsed SWMM
.inpfile: tokenised sections plus geometry maps.- coordinates: Dict[str, Tuple[float, float]]¶
- curves: Dict[str, Dict[str, Any]]¶
- polygons: Dict[str, List[Tuple[float, float]]]¶
- sections: Dict[str, List[List[str]]]¶
- symbols: Dict[str, Tuple[float, float]]¶
- vertices: Dict[str, List[Tuple[float, float]]]¶
- xsections: Dict[str, List[str]]¶
- get_float_or_none(row: Sequence[str], idx: int) float | None¶
Return
float(row[idx])orNoneif the column is absent/non-numeric.
- get_gate_or_none(row: Sequence[str], idx: int) bool | None¶
Parse a SWMM gate flag (“YES”/”NO”) into a
boolfor aboolattribute.The attribute is declared
DataType(bool), so the raw keyword must be converted here:bool("NO")isTrue, so passing the string through would silently turn an open gate into a closed one.
- get_string_or_none(row: Sequence[str], idx: int) str | None¶