Inspiration
A sensor fault passes a test. A valve fault passes a test. Together, they cause a flood.
Stormwater control depends on connected systems, where one disturbance can change what happens hours later. Testing components separately can miss those interactions. A proposed fix can also look excellent in one experiment and fail under different conditions.
I built StormPilot to help stormwater modelers uncover these failures, challenge proposed responses, and inspect the evidence before recommending a controller.
What it does
StormPilot is an interactive stormwater testing workbench powered by the actual EPA SWMM hydraulic solver.
Users can:
- Start with a published benchmark or import a supported SWMM model.
- Configure sensor faults, valve restrictions, timing, and performance requirements.
- Search for combinations that fail even when each fault passes individually.
- Compare normal, individual-fault, and combined-fault simulations.
- Search candidate responses against flooding, downstream-flow, and terminal-storage constraints.
- Inspect evaluation failures and export reports, complete traces, source information, and replay instructions.
The hosted application supports fresh experiments with editable inputs. Recorded results are explicitly labeled.
The finding
In the public Theta benchmark, StormPilot found a failure involving two faults that occur at different times:
- A sensor reads one metre high during hours 3–6.
- A valve is restricted during hours 6–8.
Over the complete 78-hour simulation:
- No fault: 0 m³ flooding
- Sensor fault alone: 0 m³ flooding
- Valve fault alone: 0 m³ flooding
- Both faults: 237.263 m³ flooding
The earlier sensor disturbance changes stored water before the valve restriction arrives. Separate tests miss the resulting interaction.
The declared search checked nine fault pairs using 21 native simulator calls, including five retained full-proof simulations.
Testing the proposed fix
A candidate response reduced flooding in the development case from 237.263 m³ to 19.361 m³: a 91.84% reduction.
That result was promising, but further testing changed the decision.
Across a separately frozen eight-case evaluation, all 64 simulations were valid. However, aggregate flooding improved by only 7.29%, below the required 10%. Only two cases achieved material improvement, and one downstream constraint failed.
StormPilot rejected the candidate.
An earlier candidate increased aggregate flooding by 22.45% in its own reserved evaluation and was also rejected. Both outcomes remain visible.
This is a central feature of StormPilot: a strong development result does not override contradictory evaluation evidence.
How I built it
The hydraulic engine uses pinned EPA SWMM 5.2.4 C source through a Python bridge. Native subprocesses isolate simulator state.
The experiment layer implements bounded fault discovery, matched comparisons, candidate search, and explicit physical constraints. A separate validator recomputes metrics from complete simulation traces and checks inputs, sources, and experiment accounting.
The interface uses React, TypeScript, and SVG for interactive plots and network schematics. Vercel hosts the application and native solver, while private Vercel Blob storage preserves completed experiment evidence.
EPA SWMM provides established hydraulic physics, and pystorms provides public benchmark networks and control foundations. My contribution is connecting these foundations into an editable, inspectable investigation and decision workflow.
The project was built solo with AI-assisted development and technical review.
Challenges
Preserving the simulator's adaptive routing behavior was essential. An early timestep approach changed that behavior, and a second benchmark exposed the resulting continuity problem.
Independent recomputation also revealed missing link storage and inconsistent unit conversions. These findings led to stronger checks against complete routing traces and native simulator totals.
Another challenge was avoiding misleading optimization. Reducing flooding alone could worsen downstream conditions, so candidate selection needed multiple physical constraints and separate evaluation.
Accomplishments
- Demonstrated a compound failure that individual-fault tests missed.
- Connected real hydraulic computation to a usable browser workbench.
- Made rejected candidates and failed criteria visible.
- Preserved reproducible evidence alongside the decision.
- Supported fresh investigations and bounded model imports.
What I learned
Passing one scenario is not enough to recommend a controller. The useful result is a decision that survives explicit tests—or a clear rejection when it does not.
Reproducibility also requires more than a chart: exact inputs, solver sources, full traces, declared criteria, and a way to execute the experiment again all matter.
What's next
The next step is testing with stormwater practitioners on calibrated partner networks and measuring whether StormPilot helps them identify missed interactions more efficiently.
I also want to expand supported model features and evaluate future candidates on genuinely new, reserved cases.
Current results come from simulated public benchmarks. Field validation remains future work.
Built With
- blob
- c
- css
- epa
- pystorms
- python
- react
- svg
- swmm
- typescript
- vercel
- vite

Log in or sign up for Devpost to join the conversation.