Skip to main content

SWMM Utils

Utilities for interpreting EPA SWMM input (.inp), report (.rpt), and output (.out) files.

Tests Python License

Overview

This project provides a comprehensive toolkit for working with EPA SWMM (Storm Water Management Model) files:

  • Input Files (.inp) - Load, modify, and save SWMM models with typed properties. Support for .inp, JSON, and Parquet formats.
  • Report Files (.rpt) - Parse simulation results and extract all major sections (node depths, link flows, pumping, storage, LID performance, etc.)
  • Output Files (.out) - Extract binary SWMM output data with optional time series loading. Export to JSON or Parquet.

Quick Start

Installation

# Clone the repository
git clone https://github.com/neeraip/swmm-utils.git
cd swmm-utils

# Install the package
pip install -e .

Basic Usage

For detailed examples and API documentation for each file type, see:

Quick example:

from swmm_utils import SwmmInput, SwmmReport, SwmmOutput

# Input files
with SwmmInput("model.inp") as inp:
    inp.title = "Modified Model"
    inp.to_json("model.json")

# Report files
with SwmmReport("simulation.rpt") as report:
    for node in report.node_depth:
        print(f"{node['name']}: {node['maximum_depth']:.2f} ft")

# Output files (metadata only or with time series)
output = SwmmOutput("simulation.out", load_time_series=True)
output.to_json("output_complete.json", pretty=True)

API Reference

Detailed API documentation is available in the respective docs files:

  • SwmmInput API - High-level interface for input files with typed properties
  • SwmmReport API - High-level interface for report files
  • SwmmOutput API - High-level interface for output files with optional time series loading

Core Classes

All classes support context manager pattern (with statement) for clean resource management:

# All three file types use context managers
with SwmmInput("model.inp") as inp:
    ...

with SwmmReport("simulation.rpt") as report:
    ...

with SwmmOutput("simulation.out") as output:
    ...

Lower-Level APIs (Advanced)

For direct decoder/encoder access:

  • SwmmInputDecoder & SwmmInputEncoder - Direct dict-based access to input file data
  • SwmmReportDecoder - Direct dict-based access to report file data

See Input Files documentation for details.

Architecture

Input Files:
.inp file → SwmmInput → Modify Properties → Save (.inp/JSON/Parquet)
               ↓
          Typed Properties
       (title, junctions, etc.)

Report Files:
.rpt file → SwmmReport → Access Results
               ↓
          Typed Properties
       (node_depth, link_flow, etc.)

Output Files:
.out file → SwmmOutput → Export (JSON/Parquet)
               ↓
       Metadata or Time Series
    (default: metadata only)

The architecture uses Python dictionaries as the in-memory data model:

  1. SwmmInput/SwmmReport/SwmmOutput: High-level interfaces with typed properties and context managers
  2. Decoders: Read .inp/.rpt/.out/JSON/Parquet files into Python dict structures
  3. Encoders: Write dict objects to .inp/JSON/Parquet formats (input files only)
  4. Dict Model: Simple Python dictionaries - easy to inspect, modify, and manipulate

Features

Input File Features

  • ✅ Simple, intuitive API with typed properties
  • ✅ Context manager support for clean resource management
  • ✅ Decode all SWMM 5.2.4 input file sections (60+ sections)
  • ✅ Encode to .inp, JSON, and Parquet formats
  • ✅ Decode from .inp, JSON, and Parquet formats
  • ✅ Export to Pandas DataFrames for data analysis and manipulation
  • ✅ Configurable Parquet output (single-file or multi-file modes)
  • ✅ Round-trip conversion (load → modify → save) without data loss
  • ✅ Full support for comments, whitespace, and formatting

Report File Features

  • ✅ Comprehensive parsing of SWMM 5.2 report files
  • ✅ Extract 20+ report sections (hydraulics, hydrology, water quality)
  • ✅ Node results: depth, inflow, flooding, surcharge
  • ✅ Link results: flow, velocity, classification
  • ✅ Pump and storage performance metrics
  • ✅ LID (Low Impact Development) performance analysis
  • ✅ Water quality: pollutant loads, washoff, continuity
  • ✅ Groundwater and RDII tracking
  • ✅ Easy result lookup by element name

Output File Features

  • ✅ Binary SWMM 5.0+ output file parsing (.out format)
  • ✅ Extract simulation time series metadata and statistics
  • ✅ Access node, link, and subcatchment properties
  • ✅ Time index generation with full timestamp support
  • ✅ Export to JSON and Parquet formats
  • ✅ Export to Pandas DataFrames for time series analysis
  • ✅ Three-level DataFrame export: full data, sections, or individual elements
  • ✅ Pollutant tracking and water quality data
  • ✅ Efficient memory usage (metadata-based access, not full time series loading)
  • ✅ Large runs: with load_time_series=True the results block is read once into numpy (output.time_series_arrays, shape (periods, elements, vars) per role) — a 278 MB .out with 8,700 elements summarises in under a second and ~400 MB of RAM
  • ✅ Element lookup by name

Testing

  • ✅ Comprehensive test suite (69 tests passing)
  • ✅ Input file tests (28 tests)
  • ✅ Report file tests (12 tests)
  • ✅ Output file tests (29 tests)

Supported SWMM Sections

Project Configuration

  • [TITLE] - Project title and description
  • [OPTIONS] - Simulation options (34 parameters)
  • [REPORT] - Output reporting options
  • [FILES] - External file references
  • [MAP] - Map extent and units
  • [BACKDROP] - Background image settings
  • [PROFILES] - Longitudinal profile definitions

Hydrology

  • [RAINGAGES] - Rain gage definitions
  • [EVAPORATION] - Evaporation data
  • [SUBCATCHMENTS] - Subcatchment properties
  • [SUBAREAS] - Subcatchment surface areas
  • [INFILTRATION] - Infiltration parameters
  • [AQUIFERS] - Groundwater aquifer properties
  • [GROUNDWATER] - Subcatchment groundwater
  • [GWF] - Groundwater flow equations
  • [SNOWPACKS] - Snow pack parameters
  • [TEMPERATURE] - Temperature data
  • [ADJUSTMENTS] - Climate adjustments

Hydraulic Network - Nodes

  • [JUNCTIONS] - Junction nodes
  • [OUTFALLS] - Outfall nodes
  • [STORAGE] - Storage unit nodes
  • [DIVIDERS] - Flow divider nodes
  • [CONDUITS] - Conduit links
  • [PUMPS] - Pump links
  • [ORIFICES] - Orifice links
  • [WEIRS] - Weir links
  • [OUTLETS] - Outlet links

Cross-Sections

  • [XSECTIONS] - Link cross-section geometry
  • [LOSSES] - Minor losses
  • [TRANSECTS] - Irregular cross-section data

Water Quality

  • [POLLUTANTS] - Pollutant properties
  • [LANDUSES] - Land use categories
  • [COVERAGES] - Subcatchment land use coverage
  • [BUILDUP] - Pollutant buildup functions
  • [WASHOFF] - Pollutant washoff functions
  • [TREATMENT] - Treatment equations
  • [INFLOWS] - External inflows
  • [DWF] - Dry weather inflows
  • [RDII] - RDII inflow parameters
  • [HYDROGRAPHS] - Unit hydrograph data
  • [LOADING] - Initial pollutant loads

LID Controls (Low Impact Development)

  • [LID_CONTROLS] - LID control definitions
  • [LID_USAGE] - LID usage in subcatchments

Street/Inlet Modeling (SWMM 5.2+)

  • [STREETS] - Street cross-section properties
  • [INLETS] - Inlet design parameters
  • [INLET_USAGE] - Inlet usage on streets

Curves & Time Series

  • [TIMESERIES] - Time series data
  • [PATTERNS] - Time patterns
  • [CURVES] - Curve data

Operational Controls

  • [CONTROLS] - Rule-based controls

Visualization

  • [COORDINATES] - Node coordinates
  • [VERTICES] - Link vertices
  • [POLYGONS] - Subcatchment polygons
  • [SYMBOLS] - Rain gage symbols
  • [LABELS] - Map labels
  • [TAGS] - Object tags

Scenarios

Scenario 1: Input File - Decode and Analyze

from swmm_utils import SwmmInputDecoder

decoder = SwmmInputDecoder()
model = decoder.decode_file("large_network.inp")

# Count elements
print(f"Junctions: {len(model.get('junctions', []))}")
print(f"Conduits: {len(model.get('conduits', []))}")
print(f"Subcatchments: {len(model.get('subcatchments', []))}")

# Find high-elevation junctions
for junc in model.get('junctions', []):
    if float(junc['elevation']) > 100:
        print(f"High junction: {junc['name']} at {junc['elevation']}m")

Scenario 2: Report File - Analyze Simulation Results

from swmm_utils import SwmmReport

with SwmmReport("results.rpt") as report:
    # Check for critical conditions
    print(f"Analysis: {report.header['title']}")
    print(f"Flow Units: {report.analysis_options.get('flow_units', 'N/A')}")
    
    # Find nodes with excessive depth
    critical_nodes = [
        node for node in report.node_depth 
        if node['maximum_depth'] > 10
    ]
    print(f"\n{len(critical_nodes)} nodes exceeded 10 ft depth")
    
    # Analyze pump efficiency
    if report.pumping_summary:
        for pump in report.pumping_summary:
            if pump['percent_utilized'] < 20:
                print(f"Pump {pump['pump_name']} underutilized: "
                      f"{pump['percent_utilized']:.1f}%")
    
    # Check system continuity
    continuity = report.continuity.get('flow_routing', {})
    error = continuity.get('continuity_error')
    if error and abs(error) > 1.0:
        print(f"Warning: Continuity error {error:.2f}%")

Scenario 3: Export Input Files to Pandas DataFrames

from swmm_utils import SwmmInput

with SwmmInput("model.inp") as inp:
    # Export all sections as dictionary of DataFrames
    all_dfs = inp.to_dataframe()
    
    # Access specific section
    junctions_df = all_dfs['junctions']
    print(f"Model has {len(junctions_df)} junctions")
    print(junctions_df[['name', 'elevation', 'max_depth']])
    
    # Analyze with pandas operations
    conduits_df = all_dfs['conduits']
    avg_length = conduits_df['length'].astype(float).mean()
    print(f"Average conduit length: {avg_length:.2f}")
    
    # Or export specific section directly
    subcatchments_df = inp.to_dataframe('subcatchments')
    total_area = subcatchments_df['area'].astype(float).sum()
    print(f"Total subcatchment area: {total_area:.2f}")
    
    # Export to CSV for external analysis
    junctions_df.to_csv("junctions.csv", index=False)
    conduits_df.to_csv("conduits.csv", index=False)

Scenario 4: Export Specific Section to DataFrame

from swmm_utils import SwmmInput
import matplotlib.pyplot as plt

with SwmmInput("model.inp") as inp:
    # Export specific section to DataFrame
    junctions_df = inp.to_dataframe('junctions')
    
    print("Junctions Summary:")
    print(junctions_df[['name', 'elevation', 'max_depth', 'init_depth']])
    
    # Perform analysis on the section
    print(f"\nStatistics:")
    print(f"Number of junctions: {len(junctions_df)}")
    print(f"Average elevation: {junctions_df['elevation'].astype(float).mean():.2f}")
    print(f"Min/Max elevation: {junctions_df['elevation'].astype(float).min():.2f} / "
          f"{junctions_df['elevation'].astype(float).max():.2f}")
    
    # Filter and analyze subset
    high_junctions = junctions_df[junctions_df['elevation'].astype(float) > 100]
    print(f"\nHigh-elevation junctions (>100): {len(high_junctions)}")
    
    # Export filtered results
    high_junctions.to_csv("high_elevation_junctions.csv", index=False)
    
    # Visualize if matplotlib available
    try:
        junctions_df['elevation'].astype(float).hist(bins=20)
        plt.xlabel('Elevation')
        plt.ylabel('Count')
        plt.title('Junction Elevation Distribution')
        plt.savefig('elevation_distribution.png')
    except ImportError:
        pass

Scenario 5: Convert Input Files for Analytics

from swmm_utils import SwmmInputDecoder, SwmmInputEncoder

# Decode SWMM model
decoder = SwmmInputDecoder()
model = decoder.decode_file("network.inp")

# Export to Parquet for analysis in pandas/R/SQL
encoder = SwmmInputEncoder()
encoder.encode_to_parquet(model, "network_parquet/", single_file=False)

# Now analyze with pandas
import pandas as pd
junctions = pd.read_parquet("network_parquet/junctions.parquet")
conduits = pd.read_parquet("network_parquet/conduits.parquet")

print(junctions.describe())
print(f"Average pipe length: {conduits['length'].astype(float).mean():.2f}")

Scenario 6: Complete Workflow - Simulate and Analyze

import subprocess
from swmm_utils import SwmmInput, SwmmReport

# Step 1: Modify input file
with SwmmInput("model.inp") as inp:
    # Increase all pipe roughness by 10%
    for conduit in inp.conduits:
        roughness = float(conduit.get('roughness', 0.01))
        conduit['roughness'] = str(roughness * 1.1)
    
    inp.to_inp("modified.inp")

# Step 2: Run SWMM simulation
subprocess.run([
    "./bin/runswmm", 
    "modified.inp", 
    "modified.rpt", 
    "modified.out"
])

# Step 3: Analyze results
with SwmmReport("modified.rpt") as report:
    print(f"Simulation complete!")
    print(f"Total runtime: {report.analysis_time.get('elapsed', 'N/A')}")
    
    # Compare peak flows
    for link in report.link_flow[:10]:
        print(f"{link['name']}: {link['maximum_flow']:.2f} CFS")

Scenario 8: Output File - Time Series Analysis with DataFrames

from swmm_utils import SwmmOutput
import pandas as pd

# Load output file with full time series
output = SwmmOutput("simulation.out", load_time_series=True)

# Export full time series to dict of DataFrames
full_data = output.to_dataframe()

# Metadata for this simulation
print(f"Version: {full_data['metadata']['version'].iloc[0]}")
print(f"Flow units: {full_data['metadata']['flow_unit'].iloc[0]}")
print(f"Total periods: {full_data['metadata']['n_periods'].iloc[0]}")

# Access each section - MultiIndex DataFrames (timestamp, element_name)
nodes_df = full_data['nodes']        # All nodes, all timesteps
links_df = full_data['links']        # All links, all timesteps
subcatchments_df = full_data['subcatchments']  # All subcatchments, all timesteps

print(f"\nNodes: {nodes_df.shape[0]} rows, {nodes_df.shape[1]} columns")
print(f"Links: {links_df.shape[0]} rows, {links_df.shape[1]} columns")
print(f"Subcatchments: {subcatchments_df.shape[0]} rows, {subcatchments_df.shape[1]} columns")

# Find peak inflow across all timesteps (if available)
if 'value_0' in links_df.columns:
    peak_by_link = links_df.groupby(level='element_name')['value_0'].max()
    print(f"\nPeak flows by link:")
    print(peak_by_link.nlargest(5))

Scenario 9: Output File - Export Specific Section

from swmm_utils import SwmmOutput

output = SwmmOutput("simulation.out", load_time_series=True)

# Export only subcatchments section (no metadata)
subcatchments_df = output.to_dataframe('subcatchments')

print(f"Subcatchment Time Series Data:")
print(f"Shape: {subcatchments_df.shape} (timesteps × elements × properties)")
print(f"Index levels: {subcatchments_df.index.names}")

# Filter: all timesteps for a specific subcatchment
subcatch_name = subcatchments_df.index.get_level_values('element_name')[0]
single_subcatch = output.to_dataframe('subcatchments', subcatch_name)

print(f"\nSingle Subcatchment ({subcatch_name}):")
print(f"Time period: {single_subcatch.index.min()} to {single_subcatch.index.max()}")
print(f"Properties measured: {single_subcatch.shape[1]} values")
print(single_subcatch.head())

# Export to CSV for external analysis
single_subcatch.to_csv(f"subcatchment_{subcatch_name}.csv")

Scenario 10: Output File - Single Element Time Series

from swmm_utils import SwmmOutput
import matplotlib.pyplot as plt

output = SwmmOutput("simulation.out", load_time_series=True)

# Get time series for a specific link
link_name = "Conduit1"
link_df = output.to_dataframe('links', link_name)

print(f"Conduit {link_name} time series:")
print(f"Simulation period: {link_df.index.min()} to {link_df.index.max()}")
print(f"Number of timesteps: {len(link_df)}")
print("\nFirst 5 timesteps:")
print(link_df.head())

# Analyze the flow data
if 'value_0' in link_df.columns:
    flow = link_df['value_0']
    print(f"\nFlow Statistics:")
    print(f"  Peak: {flow.max():.2f}")
    print(f"  Mean: {flow.mean():.2f}")
    print(f"  Min: {flow.min():.2f}")
    
    # Plot time series if matplotlib available
    try:
        flow.plot(figsize=(12, 5))
        plt.xlabel('Time')
        plt.ylabel('Flow (CFS)')
        plt.title(f'Conduit {link_name} Flow Time Series')
        plt.tight_layout()
        plt.savefig(f'{link_name}_flow_timeseries.png')
    except ImportError:
        pass

Scenario 11: Batch Processing

from pathlib import Path
from swmm_utils import SwmmInputDecoder, SwmmInputEncoder

decoder = SwmmInputDecoder()
encoder = SwmmInputEncoder()

# Convert all .inp files in a directory to JSON
for inp_file in Path("models/").glob("*.inp"):
    model = decoder.decode_file(str(inp_file))
    json_file = inp_file.with_suffix('.json')
    encoder.encode_to_json(model, str(json_file), pretty=True)
    print(f"Converted {inp_file.name} → {json_file.name}")

Scenario 12: LID Performance Analysis

from swmm_utils import SwmmReport

with SwmmReport("lid_scenario.rpt") as report:
    # Analyze LID performance
    if report.lid_performance:
        # Group by subcatchment
        from collections import defaultdict
        by_subcatchment = defaultdict(list)
        
        for lid in report.lid_performance:
            by_subcatchment[lid['subcatchment']].append(lid)
        
        # Calculate total infiltration per subcatchment
        for sub, lids in by_subcatchment.items():
            total_infil = sum(lid['infil_loss'] for lid in lids)
            total_inflow = sum(lid['total_inflow'] for lid in lids)
            reduction = (total_infil / total_inflow * 100) if total_inflow > 0 else 0
            
            print(f"{sub}: {reduction:.1f}% runoff reduction via infiltration")

Scenario 13: Round-Trip Conversion

from swmm_utils import SwmmInputDecoder, SwmmInputEncoder

decoder = SwmmInputDecoder()
encoder = SwmmInputEncoder()

# Decode from .inp
model = decoder.decode_file("original.inp")

# Encode to JSON
encoder.encode_to_json(model, "model.json", pretty=True)

# Decode from JSON
json_model = decoder.decode_json("model.json")

# Encode to Parquet (single file)
encoder.encode_to_parquet(json_model, "model.parquet", single_file=True)

# Decode from Parquet
parquet_model = decoder.decode_parquet("model.parquet")

# Encode back to .inp
encoder.encode_to_inp_file(parquet_model, "final.inp")

# All data preserved throughout the round-trip!

Testing

# Run all tests
pytest -q

# Run with coverage
pytest --cov=swmm_utils --cov-report=html

# Run specific test file
pytest tests/test_rpt.py -v

All 40 tests pass, including comprehensive format conversion, round-trip tests, and report parsing.

Running Scenarios

Before running these scenarios, make sure you have the built SWMM binary executable runswmm in the /bin directory.

# Scenario 1: Basic input file operations
python examples/example1/example1.py

# Scenario 5: Report parsing with water quality
python examples/example2/example2.py

Project Structure

swmm-utils/
├── src/
│   └── swmm_utils/              # Main package
│       ├── __init__.py          # Package exports
│       ├── inp.py               # High-level input file interface
│       ├── inp_decoder.py       # Decode .inp/JSON/Parquet → dict
│       ├── inp_encoder.py       # Encode dict → .inp/JSON/Parquet
│       ├── rpt.py               # High-level report file interface
│       └── rpt_decoder.py       # Decode .rpt → dict
├── examples/
│   ├── example1/                # Basic input file example
│   └── example2/                # Report parsing example
├── tests/
│   ├── test_inp.py              # Input file interface tests
│   ├── test_inp_decoder_encoder.py  # Core parsing tests
│   ├── test_inp_formats.py      # Format conversion tests
│   └── test_rpt.py              # Report parser tests
├── data/                        # Location for sample SWMM files
├── bin/                         # Location for swmm binary executable
├── docs/
│   └── SWMM_INPUT_FILE.md       # Complete SWMM input file reference
├── setup.py                     # Package configuration
├── requirements.txt             # Core dependencies
├── requirements-dev.txt         # Development dependencies
└── README.md                    # Project information

Performance

Input Files

Tested on various SWMM models:

  • Decode .inp: ~0.05 seconds (240 junctions)
  • Encode to JSON: 873 KB (240 junctions)
  • Encode to Parquet (multi-file): 18 files, ~110 KB total
  • Encode to Parquet (single-file): 1 file, ~109 KB
  • Round-trip (.inp → JSON → Parquet → .inp): All data preserved

Report Files

Tested on diverse simulation results:

  • Parse .rpt: ~0.02 seconds (small models) to ~0.5 seconds (large models)
  • Large model support: Successfully parsed 809 KB report with 2,227 nodes
  • Memory efficient: Processes reports on-demand without loading entire file

Documentation

Dependencies

Required

  • Python 3.8+
  • pandas >= 1.0.0 (for Parquet support)
  • pyarrow >= 10.0.0 (for Parquet support)

Development

  • pytest >= 7.0.0
  • pytest-cov >= 4.0.0

Known Limitations

Input Files

  1. Round-trip Formatting: Some cosmetic differences

    • Comments may not be preserved in exact original positions
    • Whitespace normalized to SWMM standard format
    • All data and structure fully preserved
  2. Complex Sections: Some sections have simplified handling

    • [CONTROLS] - Stored as text (complex rule syntax)
    • [TRANSECTS] - Multi-line format preserved

Report Files

  1. Read-only: Report files are parsed for reading only (no modification/encoding)
  2. Section Availability: Not all sections appear in every report (depends on simulation settings)
  3. Format Variations: Minor format differences across SWMM versions handled gracefully

Changelog

Latest Changes (Feb 2026)

Output File Parser Bug Fixes:

Fixed 4 critical bugs in the .out file decoder (out_decoder.py) that caused incorrect parsing of SWMM binary output files:

  1. Variable counts not reading codes - The parser read the variable count but didn't skip the variable codes that follow each count. This caused all subsequent reads to be offset.

    # Before (wrong): read count, then immediately read next count
    # After (fixed): read count, then read N codes, then read next count
    
  2. Start date format - Was reading as 5 integers (year, month, day, hour, minute), but SWMM stores it as a double (Excel serial date format, 8 bytes).

    # Before: self._read_datetime(f)  # Expected 5 ints
    # After: self._read_double(f)  # Excel serial date (days since 1899-12-30)
    
  3. Record size missing timestamp - Each time series record starts with an 8-byte timestamp that wasn't included in the record size calculation.

    # Before: record_size = (n_subcatch * n_subcatch_vars + ...) * 4
    # After: record_size = 8 + (n_subcatch * n_subcatch_vars + ...) * 4
    
  4. Time series read position - Used incorrect file position after metadata parsing instead of the correct position from the file footer.

Impact: These fixes enable correct parsing of all time series data from .out files, including:

  • 6 node variables (depth, head, volume, lateral_inflow, total_inflow, flooding)
  • 5 link variables (flow, depth, velocity, volume, capacity)
  • 15 system variables
  • Correct timestamps

Added: Variable codes are now exposed in the metadata (variable_codes field) for reference.

License

MIT LICENSE

Contact

For questions or issues, please open a GitHub issue.

Release files for swmm-utils 1.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for swmm-utils 1.2.1
File Size Uploaded
swmm_utils-1.2.1.tar.gz 111.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for swmm-utils 1.2.1
File Interpreter ABI Platform
swmm_utils-1.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 190.4 kB

Release files / swmm_utils-1.2.1.tar.gz

Download URL swmm_utils-1.2.1.tar.gz
Size 111.2 kB
Tags Source
SHA-256 checksum
How to use checksums
84bdabc84f565b3b1a6507f0922fc167b09fdd5a752931ee70bdb8970a361c57
BLAKE2b-256 checksum
How to use checksums
da0555670bc4fb4c2440c25efdae3fe469c2cd986c8e0f92d02173f8cb2f907a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release files / swmm_utils-1.2.1-py3-none-any.whl

Download URL swmm_utils-1.2.1-py3-none-any.whl
Size 79.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0abe3ad466a85d9af19b6f73509ee042909a69f24a9a73b8ab53409dec3eef36
BLAKE2b-256 checksum
How to use checksums
eb5a43c42959d8a117f59d83a911d2b29db56ea2313c3a0c4d851d8ae4eb75d9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release history Release notifications | RSS feed

1.4.0

2 release files

1.3.0

2 release files

1.2.2

2 release files

This release

1.2.1 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.2

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page