A Unified Python Toolkit for Building, Editing, Running, Importing, Visualizing, and Exporting EPA SWMM Models.
Project description
Development notice:
swmmxis under active testing and development. Issues are being identified and corrected continuously, so please make sure you are using the latest available release before reporting unexpected behavior.
Disclaimer
- Domain knowledge required:
swmmxis a programmatic wrapper and toolkit for EPA SWMM. Effective use requires an understanding of hydrologic and hydraulic principles, SWMM input conventions, and the mechanics of the EPA SWMM engine. - Engineering judgment required: Simulation results should always be validated before use. Professional engineering judgment is required to interpret, verify, and apply any output generated by this package for real-world design, planning, or decision-making.
- Beta software / notice of risk: This project is actively maintained, but it may still contain bugs, errors, or unresolved issues that can cause incorrect calculations, incomplete outputs, or runtime errors. Please verify critical calculations independently and report bugs through the repository Issues tab.
swmmx
swmmx A Unified Python Toolkit for Building, Editing, Running, Importing, Visualizing, and Exporting EPA SWMM Models:
Help: Documentation
Install:
pip install swmmx
How to use:
from swmmx import swmm
m = swmm("examples/example.inp")
m.plot_layout()
print(m.time.count())
lengths = m.get.conduit.length()
m.set.conduit.roughness(0.013)
m.run()
print(m.log())
m.plot_timeseries.link.flow()
Version 0.0.39 currently provides:
swmm(path=None, new=None, flow_unit=None, custom_dll_path=None)m.time.vector(),m.time.count(),m.time.vector_run(),m.time.count_run()- structured parameter access through
m.get.<main_category>.<sub_category>()andm.set.<main_category>.<sub_category>() - discoverable object counts through
m.count.<main_category>(),m.count.model(),m.count.model_dict(), andm.count.model_df() - editable model construction through
m.add.<category>.<element_type>()andm.remove.<category>.<element_type>() - matplotlib plotting through
m.plot_layout(),m.plot_timeseries.<category>.<sub_category>(), andm.plot_profile.* - tabular and spatial imports through
m.import_csv.<category>.<element_type>()andm.import_gis.<category>.<element_type>() - external-format export through
m.export.gis(),m.export.csv(), andm.export.excel() m.save(),m.run(),m.runs()for step-by-step simulation,m.validate(),m.log(), andm.clone()- lazy native-engine loading for bundled Windows/Linux/macOS engines plus custom engine paths
- preserving
.inpparsing/writing that keeps comments, unknown sections, and section order whenever possible
Constructor examples:
m = swmm("examples/example.inp") # open an existing model
m = swmm() # new SI model, LPS by default
m = swmm(new="SI", flow_unit="CMS") # new SI model
m = swmm(new="US", flow_unit="GPM") # new US model
Examples
The repository includes a small learning suite in examples/. The main folder now holds the simplest teaching scripts: no main() function, no try blocks, and fewer defensive branches, so the API is easy to read line by line. The fuller runnable versions live in examples/standard/, where they include validation, safer branching, and file-output handling.
| Topic | Simple learning version | Standard example |
|---|---|---|
| Open, validate, and run | 01_open_validate_run.py |
standard/01_open_validate_run.py |
| Modify conduit diameters and compare | 02_modify_conduit_diameters_compare.py |
standard/02_modify_conduit_diameters_compare.py |
| Step-by-step runs | 03_step_by_step_runs_dynamic_control.py |
standard/03_step_by_step_runs_dynamic_control.py |
| Layout plots | 04_plot_layout_examples.py |
standard/04_plot_layout_examples.py |
| Time-series plots | 05_plot_timeseries_examples.py |
standard/05_plot_timeseries_examples.py |
| Profile plots | 06_plot_profile_examples.py |
standard/06_plot_profile_examples.py |
| GIS/CSV/Excel export | 07_export_examples.py |
standard/07_export_examples.py |
| Time and count helpers | 08_time_and_count_functions.py |
standard/08_time_and_count_functions.py |
| Get/set patterns | 09_get_set_examples.py |
standard/09_get_set_examples.py |
| Build a model from scratch | 10_create_model_from_scratch_add_remove.py |
standard/10_create_model_from_scratch_add_remove.py |
Run them from the repository root:
python examples/01_open_validate_run.py
python examples/standard/01_open_validate_run.py
The examples use examples/example.inp, avoid overwriting it, and write generated files into examples/output/.
Installation note
swmmx intentionally does not install third-party scientific packages for you. This keeps package installation lightweight and avoids changing an existing scientific Python environment unexpectedly.
Before creating a model, install the runtime packages you need:
python -m pip install numpy pandas matplotlib networkx
When you call swmm(...), the package checks that those runtime packages are available and raises a clear error if any are missing.
Native engines
The package includes the supplied bundled engines:
- Windows 64-bit:
swmm5.dll - Linux 64-bit:
libswmm5.so - macOS:
libswmm5.dylib
Time semantics
m.time.vector() mirrors SWMM reporting behavior: it starts at the first report interval after REPORT_START_* and proceeds through END_*.
frame = m.time.vector() # pandas DataFrame with timestamp index
Run-time vectors use the actual period count read from the .out file after the engine finishes.
m.time.count() remains the expected pre-run count; m.time.count_run() is the actual post-run count and requires results.
Step-by-step simulation with m.runs()
m.runs() is one of the most powerful tools in swmmx: it exposes the native SWMM step loop as a Python iterator. Use it when you want to monitor a simulation as it advances, build progress displays, inspect step times, or prepare workflows that react during a run instead of waiting only for the final .out file.
for step in m.runs():
print(step.index, step.time, step.elapsed_days)
print(m.time.count_run()) # available after the stepped run finishes
Parameter access
lengths = m.get.conduit.length()
one_length = m.get.conduit.length("P001")
flow = m.get.link.flow(ids=["P001", "P005"], format="df")
m.set.conduit.roughness(0.013)
m.set.conduit.roughness([0.013, 0.014], ids=["P001", "P005"])
Supported getters default to NumPy output; format="df" gives pandas output. dir(m.get.<category>) now exposes the full declared parameter surface, including input fields, attached records, derived values, and result variables. dir(m.set.<category>) exposes the full editable surface. Attempting to set a derived or result parameter raises a read-only error. If a valid model simply has no objects of a requested type, an all-object getter such as m.get.weir.crest_height() returns an empty result; explicit missing IDs still raise a clear UnknownIDError.
API reference notebooks
For users who prefer a browsable learning reference, the examples/ folder includes several complete Jupyter notebooks:
examples/11_all_get_functions.ipynb: a categorized guide to every availablem.get.<main_category>.<sub_category>()function. It lists all categories and sub-items, shows the callable form, identifies input fields such asidsandformat, and explains expected outputs for scalar values, arrays, DataFrames, structured fields, result variables, and empty object collections.examples/12_all_set_functions.ipynb: a categorized guide to every availablem.set.<main_category>.<sub_category>()path. It explains accepted value types, scalar broadcasting, 1D vector/Series inputs, structured payloads such as coordinates and geometry, reference validation, and which parameters are intentionally read-only.examples/13_all_add_functions.ipynb: a complete constructor reference form.add.<category>.<element_type>().examples/14_all_remove_functions.ipynb: a complete removal reference form.remove.<category>.<element_type>().examples/15_all_plot_functions.ipynb: a complete plotting reference for layout, time-series, and profile functions.examples/16_all_import_export_functions.ipynb: a complete import/export reference form.import_csv,m.import_gis, andm.export.
These notebooks are useful both as tutorials and as a practical API checklist when editing models interactively in Jupyter, VS Code, or Spyder.
Counts
m.count.conduit()
m.count.node()
m.count.subcatchment()
total = m.count.model()
by_type = m.count.model_dict()
summary = m.count.model_df()
Count helpers take no ids or format arguments and always reflect the current in-memory model, including unsaved add/remove edits. m.count.model() totals detailed element types without double-counting composite rollups such as node and link; the dictionary and DataFrame summaries expose the detailed counts behind that total.
Add and remove elements
from swmmx import swmm
m = swmm(new="SI")
m.add.node.junction("J1", x=0.0, y=0.0, invert_elevation=10, max_depth=3)
m.add.node.outfall("OUT1", x=100.0, y=0.0, invert_elevation=9, type="FREE")
m.add.link.conduit(
"C1",
from_node="J1",
to_node="OUT1",
length=100,
roughness=0.013,
shape="CIRCULAR",
diameter=1.0,
)
# Referenced objects must exist before they are used.
m.add.time.time_series(
"Rain1",
data=[
("2026-01-01 00:00", 0.0),
("2026-01-01 00:05", 5.0),
],
)
m.add.hydrology.rain_gage(
"RG1",
format="INTENSITY",
interval="00:05",
source_type="TIMESERIES",
time_series="Rain1",
)
m.add.hydrology.subcatchment(
"S1",
rain_gage="RG1",
outlet="J1",
x=0.0,
y=0.0,
area=1.0,
)
m.save("new_model.inp")
m.remove.link.conduit("C1")
The add API validates IDs, required fields, numeric values, enums, and references before it writes EPA SWMM records. The remove API validates dependencies before deletion; by default it refuses unsafe removals, while force=True performs only conservative cascades that are known to remain valid. For example, removing a node with force=True can remove dependent conduits, but unsupported cascades raise a clear error instead of leaving broken references.
Coordinate handling is explicit where geometry is part of the object definition:
- new node objects require
xandymap coordinates; - new subcatchments require
xandycentroid coordinates, whilepolygon=remains the optional outline geometry; - new rain gages may still omit coordinates, in which case
swmmxuses the maximum mappedxandy, or(0, 0)when the model has no map coordinates.
Every add or remove operation sets m.modified to True. If the model already had results, the edit also sets m.results_stale to True and invalidates the old result accessors until the model is run again.
Generic fallbacks are also available:
m.add_element("node", "junction", "J2", x=200.0, y=0.0, invert_elevation=11, max_depth=2)
m.remove_element("node", "junction", "J2")
Add/remove reference notebooks
For a full constructor/removal reference, see:
examples/13_all_add_functions.ipynb: everym.add.<category>.<element_type>()endpoint, grouped by category, with tables for required inputs, optional inputs, types, defaults, coordinate rules, references, implementation status, and conditions.examples/14_all_remove_functions.ipynb: everym.remove.<category>.<element_type>()endpoint, grouped by category, with tables forids,force, dependency checks, removal summaries, implementation status, and safe-cascade behavior.
Plotting
swmmx uses matplotlib directly for network maps, result time series, and longitudinal profiles:
m.plot_layout()
m.plot_layout(
title="Drainage Network",
legend=True,
grid=True,
axis=True,
)
m.plot_layout(
links={
"color": {
"by": "parameter",
"category": "conduit",
"variable": "roughness",
"mode": "continuous",
"cmap": "viridis",
}
}
)
m.plot_layout(
annotation={
"nodes": "id",
"conduits": {
"template": "{id}\nD={diameter:.2f} m",
"rotation": "link",
"bbox": True,
},
}
)
m.plot_timeseries.link.flow(["C1", "C2"])
m.plot_profile.nodes(
"J1",
"OUT1",
show_hgl=True,
aggregation="max",
)
m.plot_layout() draws mapped subcatchments, links, nodes, rain gages, and LID usages from the model coordinates. Subcatchments show dashed outlet connectors from each polygon centroid to its outlet node or downstream subcatchment. The default symbology is type-aware: node types use different markers, link types use different line styles, and LID controls use distinct markers in the legend when present. Presentation controls are explicit: title=... sets the map title, axis=True shows coordinate axes, grid=True keeps a visible reference grid even when coordinate labels stay hidden, and show=False suppresses automatic figure display while still returning (fig, ax). Layout maps now render titles, grids, and visible coordinate axes with safe ordinary artists instead of Matplotlib's native title/tick/grid machinery, avoiding the Spyder/Agg recursive tick-copy path while keeping the same public API. On non-interactive matplotlib canvases such as Agg, show=True avoids useless GUI calls but keeps the figure available for Spyder/Jupyter rendering; show=False removes only the library-created figure manager so hidden plots stay hidden.
Layer dictionaries support ordinary static styling as well as data-driven styling from fixed input parameters, simulation results, or your own ID-to-value mappings. Nodes, links, and subcatchments can be colored with continuous or discrete colormaps; nodes can use data-driven marker size, and links can use data-driven line width (or the size alias). Result-driven styles require a completed run; user-data styles are useful for classes such as risk bands, inspection status, or scenario groups. When color, size, or width is data-driven, the plot adds a dedicated legend section so the encoded value is visible on the finished figure instead of living only in the function call. Continuous-color legends are rendered with safe sampled swatches rather than native Matplotlib colorbar axes, which keeps Spyder/Agg rendering out of the recursive tick-copy path.
m.plot_layout(annotation=...) adds map labels without changing existing layout behavior. It supports simple ID annotations, multi-field labels, templates such as {id}\nD={diameter:.2f} m, prefixes, suffixes, numeric formatting, callables, per-layer styling, ID and conditional filtering, max_labels, external user data, and link-aligned rotation with rotation="link".
m.plot_timeseries.<category>.<sub_category>() routes through the result API and plots one or many timestamped series with matplotlib. The time axis has three explicit modes: time_format="datetime" for full date-time labels, time_format="clock" for hour-minute labels, and time_format="elapsed" for elapsed hours. X-axis tick labels are rotated 45 degrees by default for readability. Time-series and profile plots draw their title, grid, frame, ticks, and axis labels with safe ordinary artists instead of Matplotlib's native Axis artists, and their legends use lightweight proxy handles rather than cloning plotted Line2D artists. This keeps Spyder/Agg out of both recursive marker-copy paths while preserving the same public plotting API.
m.plot_profile.nodes(), .links(), and .longest() render geometry-first longitudinal profiles, with HGL/water overlays available after a run. Profiles show prominent vertical dotted node guides, shaded conduit barrels between invert and crown, and link names by default; distance and elevation axes infer ft or m automatically from the model units. m.plot_profile.links([...]) accepts any connected ordered walk, even when the supplied sequence runs opposite to the hydraulic direction.
All plotting calls return (fig, ax), accept ax= for composition, and support save_path= / save_format=. Common errors are explicit: layout plots need coordinates, result-based plots need m.run(), invalid IDs raise UnknownIDError, and disconnected profile requests raise NoPathError.
For a complete plotting reference, see examples/15_all_plot_functions.ipynb. It documents every plotting endpoint, every input and default, all dynamic time-series variables, runnable layer-dictionary examples for nodes/links/subcatchments/connectors/rain gages/LIDs/labels, custom-style legend behavior, profile controls, outputs, save behavior, and common validation errors.
Import
swmmx uses m.import_csv and m.import_gis instead of m.import because import is a reserved Python keyword. The import API mirrors the rest of the package:
m.import_csv.node.junction("junctions.csv")
m.import_csv.node.outfall("outfalls.csv")
m.import_csv.link.conduit("conduits.csv")
m.import_csv.hydrology.subcatchment("subcatchments.csv")
m.import_gis.node.junction("junctions.shp")
m.import_gis.link.conduit("pipes.geojson")
m.import_gis.hydrology.subcatchment("subcatchments.gpkg", layer="subcatchments")
Importers normalize column names, apply aliases such as Node ID -> id, Easting -> x, Northing -> y, validate rows, and return an ImportResult:
import_result = m.import_csv.node.junction("junctions.csv")
print(import_result.summary())
result = m.import_csv.link.conduit(
"pipes.csv",
field_map={
"id": "PipeID",
"from_node": "FromNode",
"to_node": "ToNode",
"length": "Length",
"roughness": "ManningN",
"diameter": "Diameter",
},
)
print(result.summary())
print(result.to_frame())
Use dry_run=True to validate without modifying the model, and mode="upsert" to add missing objects while updating existing ones:
dry_run = m.import_csv.node.junction("junctions.csv", dry_run=True)
upserted = m.import_csv.node.junction("junctions.csv", mode="upsert")
Group-level shortcuts dispatch rows by a type column:
m.import_csv.node("nodes.csv", default_type="junction")
m.import_csv.link("links.csv", default_type="conduit")
m.import_gis.node("nodes.shp")
m.import_gis.link("links.shp")
Common options are mode="add" | "update" | "upsert", dry_run=True, on_missing_required="error" | "skip", on_unknown_fields="ignore" | "warn" | "error", and on_error="raise" | "skip" | "collect". GIS import lazily requires geopandas and shapely; install them with pip install geopandas shapely.
CSV files created by m.export.csv() are designed to import back cleanly. Exact SWMM input columns take priority over result or derived columns with similar names (for example, max_depth is preferred over exported result depth), and exported helper fields such as element_type, stage_data, source_data, and serialized coordinates are translated back into the matching import options where possible.
For a complete import/export reference, see examples/16_all_import_export_functions.ipynb.
Export
m.export.gis()
m.export.gis(
path="exports/gis",
elements=["nodes", "links", "subcatchments"],
)
m.export.csv(
path="exports/csv",
elements="all",
time_step=-1,
)
m.export.excel(
path="exports",
file_name="model_export.xlsx",
)
If path is omitted, exports go beside the model file when one exists, otherwise into the current working directory. file_name controls a single workbook name or the prefix used for multi-file CSV/GIS exports. elements accepts named tables, groups such as hydrology or quality, or "all".
When results are available, time_step=-1 attaches the last simulated snapshot to result-capable tables. Without results, exports continue with parameter-only tables and a warning by default; use strict_results=True when missing results should be treated as an error. CSV writes one UTF-8 file per table, while Excel writes one workbook with one sheet per selected table. All CSV, Excel, and GIS export tables include a coordinates field: point objects store [x,y], links store the full coordinate chain from upstream node through vertices to downstream node, and subcatchments store polygon coordinates when available. Node-based and point-based export tables also include plain x and y fields: all node types use [COORDINATES], rain gages use [SYMBOLS], node-attached records inherit their node coordinates, and subcatchments/subcatchment-attached records use the polygon centre point.
GIS export writes spatial layers for nodes, links, subcatchments, and rain gages. It requires the optional packages geopandas and shapely (pip install geopandas shapely). Existing outputs are protected by default; pass overwrite=True only when replacing files is intentional.
Public parameter catalog
The public dotted API is organized into the following categories and subcategories:
option_general:flow_units,infiltration_model,flow_routing,link_offsets,force_main_equation,allow_ponding,minimum_slope,skip_steady_state,system_flow_tolerance,lateral_flow_toleranceoption_process:ignore_rainfall,ignore_snowmelt,ignore_groundwater,ignore_rdii,ignore_routing,ignore_qualityoption_date_time:start_date,start_time,end_date,end_time,report_start_date,report_start_time,report_step,wet_step,dry_step,routing_step,rule_step,sweep_start,sweep_end,dry_daysoption_dynamic_wave:inertial_damping,normal_flow_limited,surcharge_method,variable_step,minimum_step,lengthening_step,minimum_surface_area,head_tolerance,maximum_trials,threadsrain_gage:id,count,format,interval,snow_catch_factor,source_type,time_series,filename,station,units,rainfallsubcatchment:id,count,rain_gage,outlet,area,width,slope,impervious_percent,curb_length,snow_pack,tag,polygon,centroid,rainfall,snow_depth,evaporation,infiltration,runoff,groundwater_flow,groundwater_elevation,soil_moisture,pollutant_concentration,n_impervious,n_pervious,depression_storage_impervious,depression_storage_pervious,zero_depression_storage_impervious_percent,subarea_routing,percent_routedinfiltration_horton:maximum_rate,minimum_rate,decay,dry_time,maximum_volumeinfiltration_green_ampt:suction_head,hydraulic_conductivity,initial_moisture_deficitinfiltration_curve_number:curve_number,conductivity,dry_timenode:id,count,type,invert_elevation,max_depth,initial_depth,surcharge_depth,ponded_area,tag,coordinate,external_inflow,dry_weather_flow,treatment,depth,head,volume,lateral_inflow,total_inflow,flooding,overflow,pollutant_concentrationjunction:id,count,invert_elevation,max_depth,initial_depth,surcharge_depth,ponded_areaoutfall:id,count,invert_elevation,type,fixed_stage,tidal_curve,time_series,tide_gate,route_toflow_divider:id,count,invert_elevation,max_depth,initial_depth,surcharge_depth,ponded_area,type,diverted_link,cutoff_flow,diversion_curve,weir_height,weir_coefficientstorage_unit:id,count,invert_elevation,max_depth,initial_depth,storage_curve_type,storage_curve,area,area_coefficient,area_exponent,area_constant,evaporation_factor,seepage_losslink:id,count,type,from_node,to_node,inlet_offset,outlet_offset,initial_flow,maximum_flow,flap_gate,tag,vertices,flow,depth,velocity,volume,capacity,setting,pollutant_concentrationconduit:id,count,from_node,to_node,length,roughness,inlet_offset,outlet_offset,initial_flow,maximum_flow,shape,geometry,barrels,culvert_code,entry_loss,exit_loss,average_loss,flap_gate,seepage_rate,slope,full_area,full_depth,hydraulic_radius,full_flow,normal_depth,critical_depth,flow,depth,velocity,capacitypump:id,count,from_node,to_node,curve,initial_status,startup_depth,shutoff_depth,flow,status,setting,energyorifice:id,count,from_node,to_node,type,shape,height,width,offset,discharge_coefficient,flap_gate,open_close_time,flow,settingweir:id,count,from_node,to_node,type,crest_height,length,side_slope,discharge_coefficient,flap_gate,end_contractions,end_coefficient,surcharge,road_width,road_surface,flow,settingoutlet:id,count,from_node,to_node,offset,flap_gate,rating_type,curve,coefficient,exponent,flow,settingcross_section:link,shape,geometry_1,geometry_2,geometry_3,geometry_4,barrels,culvert_code,height,width,side_slope,shape_curvetransect:id,count,roughness_left,roughness_right,roughness_channel,left_bank,right_bank,stations,elevations,modifierscurve:id,count,type,x,y,pointscoordinate:node_coordinates,subcatchment_coordinates,link_vertices,polygons,labels,map_dimensions,map_unitsstreet:id,count,crown_width,curb_height,cross_slope,roughness,depression_storage,gutter_width,gutter_slope,spreadinlet:id,count,type,grate_length,grate_width,grate_type,curb_length,curb_height,slotted_length,slotted_width,captured_flowinlet_usage:node,inlet,conduit,number,clogging_factor,flow_restrictionlid_control:id,count,typelid_surface:storage_depth,vegetation_fraction,roughness,slope,side_slopelid_pavement:thickness,void_ratio,impervious_surface_fraction,permeability,clogging_factorlid_soil:thickness,porosity,field_capacity,wilting_point,conductivity,conductivity_slope,suction_headlid_storage:height,void_ratio,seepage_rate,clogging_factorlid_drain:coefficient,exponent,offset_height,delay,open_level,closed_level,control_curvelid_usage:subcatchment,lid_control,number,area,width,initial_saturation,from_impervious_percent,from_pervious_percent,outlet,drain_to,inflow,evaporation,infiltration,surface_outflow,drain_outflow,storageaquifer:id,count,porosity,wilting_point,field_capacity,conductivity,conductivity_slope,tension_slope,upper_evaporation_fraction,lower_evaporation_depth,lower_groundwater_loss_rate,bottom_elevation,water_table_elevation,unsaturated_moisture,upper_evaporation_patterngroundwater:subcatchment,aquifer,node,surface_elevation,a1,b1,a2,b2,a3,fixed_depth,threshold_elevation,lateral_flow_equation,deep_flow_equationsnow_pack:id,count,plowable_fraction,impervious_fraction,pervious_fraction,minimum_melt_coefficient,maximum_melt_coefficient,base_temperature,free_water_capacity_fraction,initial_snow_depth,initial_free_water,depth_at_100_percent_cover,removal_depth,fraction_to_impervious,fraction_to_pervious,fraction_to_immediate_melt,fraction_to_subcatchment,fraction_to_outflow,destination_subcatchmentclimate:temperature_time_series,evaporation_type,evaporation_constant,evaporation_monthly,evaporation_time_series,evaporation_recovery_pattern,evaporation_dry_only,wind_speed_type,wind_speed_monthly,snowmelt_parameters,areal_depletion_impervious,areal_depletion_perviousclimate_adjustment:temperature,evaporation,rainfall,conductivitypollutant:id,count,units,rain_concentration,groundwater_concentration,rdii_concentration,decay_coefficient,snow_only,co_pollutant,co_pollutant_fraction,dry_weather_flow_concentration,initial_concentrationland_use:id,count,sweeping_interval,sweeping_availability,last_sweptcoverage:subcatchment,land_use,percentloading:subcatchment,pollutant,initial_buildupbuildup:land_use,pollutant,function,maximum_buildup,rate_constant,power,normalizerwashoff:land_use,pollutant,function,coefficient,exponent,cleaning_efficiency,bmp_efficiencytreatment:node,pollutant,expressiontime_series:id,count,datetime,values,filename,descriptiontime_pattern:id,count,type,multipliersexternal_inflow:node,constituent,time_series,type,units_factor,scale_factor,baseline,patterndry_weather_flow:node,constituent,average_value,monthly_pattern,daily_pattern,hourly_pattern,weekend_patternrdii:node,unit_hydrograph,sewer_areaunit_hydrograph:id,count,rain_gage,month,short_term_r,short_term_t,short_term_k,medium_term_r,medium_term_t,medium_term_k,long_term_r,long_term_t,long_term_kcontrol_rule:id,count,text,conditions,actions,priority,enabled,action_loginterface_file:rainfall,runoff,hotstart,rdii,inflow,outflow,use_file,save_filesystem_result:air_temperature,rainfall,snow_depth,evaporation,infiltration,runoff,dry_weather_inflow,groundwater_inflow,rdii_inflow,direct_inflow,total_lateral_inflow,flooding,outfall_flow,storage_volume,pollutant_loadingsummary:model,counts,options,subcatchment_runoff,subcatchment_washoff,node_depth,node_inflow,node_flooding,node_surcharge,storage_volume,outfall_loading,link_flow,link_velocity,conduit_surcharge,pump_operation,lid_performance,runoff_continuity,flow_routing_continuity,quality_routing_continuity,validation_issues
Validation
validation = m.validate()
print(validation.ok)
print(validation.to_frame())
The first validator checks duplicate IDs, missing required options, invalid unit values, missing nodes/links, conduit endpoints, and several common cross-section/reference errors.
Comparison with common EPA SWMM Python packages
| Feature / capability | swmmx | PySWMM | swmm_api | swmmio | swmm-toolkit |
|---|---|---|---|---|---|
| Main purpose | End-to-end SWMM modeling toolkit | Runtime SWMM control | SWMM file/result automation | Pandas-based model/result analysis | Low-level solver/output bindings |
| Unified model object | Yes — m = swmm(...) |
Partial | Partial | Yes, but more DataFrame/file oriented | No |
| Discoverable dotted API | Yes — m.get, m.set, m.add, m.remove, m.import_csv, m.import_gis, m.export |
Limited | More section/object oriented | More pandas/DataFrame oriented | Low-level API |
Open existing .inp model |
Yes | Yes | Yes | Yes | Solver-level |
| Create new model from scratch | Yes — swmm(new="SI"), m.add.* |
Limited / not main focus | Supported in workflows | Limited / not main focus | No |
| Add/remove SWMM elements | Yes — validated m.add.* and m.remove.* |
Limited | Yes, through model/input structures | Some support | No |
| Structured parameter get/set | Yes — broad public parameter catalog | Yes for runtime objects | Yes | Yes via DataFrames | Low-level |
| Step-by-step simulation | Yes — m.runs() iterator |
Very strong; core strength | Run support, less runtime-control focused | Usually external / via integration | Engine-level possible |
| Runtime control during simulation | Developing / possible through m.runs() workflows |
Strongest option | Not primary focus | Not primary focus | Low-level possible |
| Run SWMM from Python | Yes — m.run() and m.runs() |
Yes | Yes | Usually via integration/tools | Yes |
| Read simulation results | Yes — result variables through m.get.* and plots |
Yes | Yes, including .out / .rpt workflows |
Yes | Output API level |
| Layout plotting | Yes — built-in m.plot_layout() |
Not primary | Available in ecosystem/examples | Yes / visualization support | No |
| Time-series plotting | Yes — m.plot_timeseries.* |
User builds or uses ecosystem | Possible | Possible | No |
| Longitudinal profile plotting | Yes — m.plot_profile.* |
Not primary | Available / supported in workflows | Some visualization support | No |
| CSV import | Yes — m.import_csv.<category>.<element_type>() |
Not primary | Possible through user workflows | DataFrame-based workflows | No |
| GIS import | Yes — m.import_gis.<category>.<element_type>() |
Not primary | GIS interaction available | GeoDataFrame support | No |
| CSV export | Yes — m.export.csv() |
Not primary | Possible | DataFrame export possible | No |
| GIS export | Yes — m.export.gis() |
Not primary | GIS interaction available | GeoDataFrame support | No |
| Excel export | Yes — m.export.excel() |
Not primary | Possible through pandas workflows | Possible through pandas workflows | No |
| Round-trip import/export workflow | Yes — exported CSV designed to import back cleanly | No | Partial / workflow dependent | Partial / workflow dependent | No |
Preserve .inp comments / unknown sections / section order |
Yes, where possible | Not main focus | Strong .inp handling |
.inp handling support |
No |
| Validation helpers | Yes — m.validate() and import result reporting |
Engine/runtime errors | Strong file/model checks depending workflow | Data checks possible | Low-level |
| Native engine handling | Bundled Windows/Linux/macOS engines plus custom path | Uses SWMM engine bindings | Runs SWMM through configured executable/tooling | Usually external / integration | Solver bindings |
| Best use case | Complete model lifecycle in one Python API | Real-time control and simulation intervention | Advanced .inp, .rpt, .out automation |
Pandas/GeoPandas engineering analysis | Developers needing solver/output bindings |
| Main strength | Unified, readable, high-level workflow | Runtime control | Mature file/result automation | Pandas-first productivity | Low-level SWMM access |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swmmx-0.0.39.tar.gz.
File metadata
- Download URL: swmmx-0.0.39.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89b8eb4777dddf55f04a59562ff7de4f2e4298d62b4ed4ea2bd905e1fd266a79
|
|
| MD5 |
fab2413b5e6b2ad179975f52beaf111a
|
|
| BLAKE2b-256 |
bec3e2d4d4872dc4f40a64b4c5286224f4df4fb204fa8717f4d51c8cd5547f33
|
Provenance
The following attestation bundles were made for swmmx-0.0.39.tar.gz:
Publisher:
publish.yml on mgeranmehr/swmmx_dev
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
swmmx-0.0.39.tar.gz -
Subject digest:
89b8eb4777dddf55f04a59562ff7de4f2e4298d62b4ed4ea2bd905e1fd266a79 - Sigstore transparency entry: 1584476783
- Sigstore integration time:
-
Permalink:
mgeranmehr/swmmx_dev@b4541574d542e1a7df077bd0f1b225459b3b4364 -
Branch / Tag:
refs/tags/v0.0.39 - Owner: https://github.com/mgeranmehr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b4541574d542e1a7df077bd0f1b225459b3b4364 -
Trigger Event:
push
-
Statement type:
File details
Details for the file swmmx-0.0.39-py3-none-any.whl.
File metadata
- Download URL: swmmx-0.0.39-py3-none-any.whl
- Upload date:
- Size: 1.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1080e16d130470576b06bdbb19c45229cb6e9f270859f27c05143219c12e305a
|
|
| MD5 |
d89f791d1a2b0e4fd2aa6756c6706376
|
|
| BLAKE2b-256 |
0d71413131475d655c31898dc0e22a0c7de078780955bc95bf9ba04e8f6a0aa6
|
Provenance
The following attestation bundles were made for swmmx-0.0.39-py3-none-any.whl:
Publisher:
publish.yml on mgeranmehr/swmmx_dev
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
swmmx-0.0.39-py3-none-any.whl -
Subject digest:
1080e16d130470576b06bdbb19c45229cb6e9f270859f27c05143219c12e305a - Sigstore transparency entry: 1584477076
- Sigstore integration time:
-
Permalink:
mgeranmehr/swmmx_dev@b4541574d542e1a7df077bd0f1b225459b3b4364 -
Branch / Tag:
refs/tags/v0.0.39 - Owner: https://github.com/mgeranmehr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b4541574d542e1a7df077bd0f1b225459b3b4364 -
Trigger Event:
push
-
Statement type: