Full-stack web application for uploading, discretizing, and batch-running EPA SWMM models through the browser — with multi-engine support, real-time progress, AI-powered report analysis, and ZIP export.
BatchSWMM56 replaces the repetitive open-file → run → inspect loop with a single browser tab. Drop one or hundreds of .inp files onto the upload zone, choose an engine, configure optional solver overrides (routing step, flow-routing method, date range, inertial damping, …), and the application:
- Runs every file through the selected SWMM engine.
- Parses each
.rptoutput for continuity errors, warnings, time-series data, and key performance indicators. - Displays per-file summaries in a sortable, filterable results table with inline charts.
- Exports results as CSV, Excel (
.xlsx), PDF, AI-generated narrative report, or a ZIP archive of raw.rpt/.inppairs.
The in-browser WebAssembly modes run simulations entirely on the client — files never leave your device. The server modes stream real-time progress over WebSocket and support live node/link data dashboards during API-mode runs.
| Mode | Engine binary | Execution context | Live snapshots |
|---|---|---|---|
| Executable | EPA SWMM 5.2 runswmm (bundled in swmm-engine/) |
Server process | No |
| API | EPA SWMM 5.2 shared library via koffi FFI (libswmm5.so) |
Server in-process | Yes — node depths, link flows, link velocities streamed over WebSocket |
| WASM | EPA SWMM 5.2.4 compiled to WebAssembly with Emscripten | Browser web worker | No |
| WASM6 | OpenSWMM 6.0.0-alpha.4 (swmm6_rel) compiled to WASM |
Browser web worker | No |
| WASM6 Dev | OpenSWMM 6.0.0-alpha (develop branch) compiled to WASM |
Browser web worker | No |
| Hydra | Hydra UDS compiled to WebAssembly | Browser web worker | No |
Parallel processing applies to in-browser WASM modes only: up to 4 web workers run concurrently (scaled to device hardware; large models run sequentially to conserve memory). Server modes process files one at a time.
The WASM modes are always available. Server modes require the bundled engine to be executable on the host — check GET /api/swmm-status to verify availability.
- Upload up to 500
.inpfiles per batch (25 MB per file, 250 MB total). - Discretization sweeps — vary routing step, report step, start/end date, flow-routing method, inertial damping, variable-step ratio, and lengthening step across a matrix of combinations.
- Design storm sweeps — run a single model across multiple design-storm configurations.
- Batch jobs are tied to the anonymous browser session and automatically deleted after 24 hours.
- WebSocket connection (
/api/ws?jobId=…) streams per-file status:queued → running → complete/error. - API mode additionally streams
api_snapshotmessages at each report step, populating the Live API Dashboard (real-time line charts for node depths, link flows, and link velocities).
- Extracts continuity errors (surface runoff, flow routing), warnings, engine version, simulation period, and timestep from every
.rptfile. - Validates report structure and flags truncated or failed runs.
- Optionally parses the binary
.outfile for time-series data when present.
- Sends parsed metrics to an OpenAI-compatible endpoint and generates a natural-language narrative report covering errors, warnings, and model health.
- Requires
AI_INTEGRATIONS_OPENAI_API_KEY(and optionallyAI_INTEGRATIONS_OPENAI_BASE_URLfor alternative providers).
- Browse and load
.inpsample models directly from a configured GitHub repository without manual download.
/docspage with user guide, full engine-mode reference, and the HTTP/WebSocket API specification (also available as/llms.txtfor LLM and script consumers).- Searchable SWMM5 API Guide (5,400-line markdown document) rendered inline.
BatchSWMMRunner/
├── client/ # React + Vite frontend (TypeScript)
│ ├── src/
│ │ ├── pages/ # Home, Results, Docs, API Dashboard pages
│ │ ├── components/ # Upload zone, results table, charts, modals
│ │ ├── hooks/ # useWebSocket, useBatch, useSwmmWasm, …
│ │ └── lib/
│ │ ├── swmmWasmEngine.ts # WASM5 orchestration (web worker bridge)
│ │ └── swmm6WasmEngine.ts # WASM6 orchestration
│ └── public/
│ ├── wasm/ # swmm5.js + swmm5.wasm (EPA SWMM 5.2.4)
│ ├── wasm6/ # openswmm6.js + .wasm (OpenSWMM stable)
│ ├── wasm6dev/ # openswmm6.js + .wasm (OpenSWMM dev)
│ ├── samples/ # Bundled sample .inp files
│ ├── engines.json # Machine-readable engine identity manifest
│ ├── llms.txt # HTTP + WS API reference for LLMs/scripts
│ └── NOTICE.txt # Bundled engine attribution notices
├── server/ # Express backend (TypeScript)
│ ├── index.ts # App bootstrap, middleware, error handler
│ ├── routes.ts # All HTTP routes and WebSocket server
│ ├── storage.ts # PostgreSQL via Drizzle ORM (jobs, results)
│ ├── reportParser.ts # .rpt text parser (metrics, warnings, version)
│ ├── swmmOutParser.ts # Binary .out file time-series parser
│ ├── swmm5api.ts # koffi FFI bridge to libswmm5.so (API mode)
│ ├── swmmInvocation.ts # Engine path resolution (bundled vs override)
│ ├── session.ts # Anonymous session middleware
│ └── githubModels.ts # GitHub model browser integration
├── shared/ # Types used by both client and server
│ ├── schema.ts # Zod schemas + Drizzle table definitions
│ ├── inpOptions.ts # INP override helpers, SWMM6 normalization
│ ├── inpScanner.ts # Lightweight .inp section scanner
│ └── inpCaseNormalize.ts # Case normalization for INP keywords
├── swmm-engine/ # Bundled EPA SWMM 5.2 executable + shared lib
├── swmm-source/ # C sources for the bundled SWMM 5 engines
├── swmm6-source/ # Source reference for SWMM 6 WASM builds
├── migrations/ # Plain-SQL migrations (applied in filename order)
├── tests/ # Vitest integration tests
├── docs/ # Engine manifest schema, contributing notes
├── scripts/ # Build and maintenance helper scripts
├── .github/workflows/ # CI (ci.yml): typecheck + tests on push/PR
├── package.json
├── vite.config.ts
├── tailwind.config.ts
├── drizzle.config.ts
├── tsconfig.json
└── vitest.config.ts
- Node.js 20+
- PostgreSQL (any recent version)
git clone https://github.com/dickinsonre/BatchSWMMRunner.git
cd BatchSWMMRunner
npm install| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string, e.g. postgres://user:pass@localhost:5432/batchswmm |
SESSION_SECRET |
Yes | Random string for signing anonymous session cookies |
RUNSWMM_PATH |
No | Override the bundled runswmm path with a custom SWMM executable |
AI_INTEGRATIONS_OPENAI_API_KEY |
No | Enables AI narrative report generation |
AI_INTEGRATIONS_OPENAI_BASE_URL |
No | Alternative OpenAI-compatible base URL (e.g. Azure, local Ollama) |
PORT |
No | HTTP port (default 5000) |
Apply the plain-SQL migrations in order before first run:
for f in migrations/*.sql; do psql "$DATABASE_URL" -f "$f"; donenpm run dev # Vite + Express on http://localhost:5000npm run build # Compiles client (Vite) and server (esbuild)
npm run start # Serves the built appThe build step also validates the engines.json manifest and verifies that all required notice files are present in dist/public.
The full API reference is available at /llms.txt (served at runtime) and mirrored in the in-app /docs page. The core workflow:
POST /api/upload # Upload .inp files → returns jobId
POST /api/batch/{jobId}/start # Set engine, overrides, launch batch
GET /api/batch/{jobId} # Poll job status and per-file results
GET /api/batch/{jobId}/results/{resultId}/content # Fetch raw .rpt text
WS /api/ws?jobId={jobId} # WebSocket stream (progress + API snapshots)
| Limit | Value |
|---|---|
| Max files per batch | 500 |
| Max file size | 25 MB |
| Max total upload size | 250 MB |
| Job timeout (configurable) | 10–60 minutes |
| Job retention | 24 hours |
| Max concurrent server jobs | 4 |
| WASM parallel workers | Up to 4 (browser) |
All fields are optional and validated server-side before any file is run.
| Override | Type | Valid range / values |
|---|---|---|
reportStepMinutes |
number | > 0, ≤ 1440 |
routingStepSeconds |
number | > 0, ≤ 3600 |
flowRouting |
string | steady, kinematic, dynamic |
startDate |
string | ISO 8601 YYYY-MM-DD |
endDate |
string | ISO 8601 YYYY-MM-DD, must be ≥ startDate |
variableStep |
number | 0–2 |
lengtheningStep |
number | 0–3600 seconds |
inertialDamping |
string | NONE, PARTIAL, FULL |
Note: SWMM6-specific solver keywords are rejected for server (SWMM 5.x) modes to prevent ERROR 205 failures across the entire batch.
Pre-select an engine and load a sample model on page open:
/?engine=wasm6&sample=Demo_extran2.inp
Stable data-testid attributes on all interactive elements support Playwright / Puppeteer automation. See /llms.txt for the full list.
npm test # Vitest integration tests (run serially — shared uploads dir)
npm run check # TypeScript typecheck only
npm run verify # typecheck + tests (what CI runs)CI runs npm run verify on every push and pull request via GitHub Actions (.github/workflows/ci.yml), using a PostgreSQL service container. The test suite also validates engines.json manifest integrity before running Vitest.
Overrides are applied by shared/inpOptions.ts before the engine sees the file. The module handles:
- Section patching — rewrites
[OPTIONS]keys in-place, preserving all other content. - Virtual junction stripping — removes virtual junction nodes that are incompatible with certain engine modes (
hasVirtualJunctions,stripVirtualJunctions). - EXTRAN8 hotstart rewriting — adjusts hotstart file paths when the working directory changes (
needsExtran8Hotstart,rewriteHotstartPath). - SWMM6 normalization — detects and routes SWMM6-only keywords to the correct engine.
- Matrix variant cap —
MAX_MATRIX_VARIANTSlimits combinatorial sweep size to prevent runaway job counts.
client/public/engines.json (served at /engines.json) is a machine-readable record for all five browser engines. It includes:
- JavaScript and WASM SHA hashes and byte sizes
- Binary fingerprints
- Source commit references and build metadata
- Legal-file hashes
The schema is documented in docs/engine-manifest.md. npm test validates the manifest before running Vitest; npm run build re-validates before deploying. Missing historical provenance is represented as "unknown" rather than inferred.
The two WASM6 builds are object-code distributions of HydroCouple's OpenSWMM Engine:
| Artifact | Source commit | Date | License |
|---|---|---|---|
wasm6/ (stable) |
137e65e4… |
2026-09-10 | Apache-2.0 |
wasm6dev/ (dev) |
19a1bc42… |
2026-08-11 | MIT |
Full attribution notices, dependency audits, Emscripten runtime notices, and musl libc copyright are bundled under client/public/licenses/openswmm/.
- WASM modes:
.inpfiles are processed entirely in the browser. No data is sent to the server. - Server modes: files are stored under
uploads/<jobId>/for the duration of the batch, scoped to the anonymous session that uploaded them. Other sessions cannot access them. All files and database records are deleted automatically after 24 hours. - Rate limiting: upload and batch-start endpoints are rate-limited with
express-rate-limit(respectsX-Forwarded-Forwhen behind a proxy). - Session isolation: each browser session receives a signed anonymous session cookie; job ownership is enforced server-side.
- Upload validation: only
.inpfiles are accepted; size and count limits are enforced by multer before any processing begins.
| Layer | Technology |
|---|---|
| Frontend framework | React 18 + TypeScript |
| Build tool | Vite |
| Styling | Tailwind CSS + shadcn/ui components |
| State / data fetching | TanStack Query (React Query) |
| Charts | Recharts |
| Backend framework | Express (Node.js) |
| Database ORM | Drizzle ORM |
| Database | PostgreSQL |
| WebSocket | ws library |
| FFI (API mode) | koffi |
| File upload | multer |
| Validation | Zod |
| Testing | Vitest |
| CI | GitHub Actions |
| WASM compilation | Emscripten (EPA SWMM 5.2.4) |
The BatchSWMM56 application is MIT licensed; see LICENSE.
The bundled EPA SWMM 5.2 engine is public-domain U.S. government software. The bundled OpenSWMM 6 engines are Apache-2.0 / MIT licensed (see above). Third-party components remain under their own terms; see NOTICE and THIRD_PARTY_NOTICES.md.
Neither the U.S. Environmental Protection Agency, HydroCouple, nor the OpenSWMM authors endorse BatchSWMM56; their names identify origin and attribution only.
Pull requests and issues are welcome. Run npm run verify before submitting a PR — CI will reject any build that fails typecheck or tests. See HANDOVER.md for a detailed technical handover, architecture notes, and known limitations.