Team (University of Florida): Vittesh Arora, Siddhant Chauhan, Samarth Vinayaka and Pulkit Garg, Computer Science (Department of Computer & Information Science & Engineering); and Subash Sathya Bhaskar, Electrical & Computer Engineering (Department of Electrical & Computer Engineering).
Inspiration
When a hurricane hits a small Florida county, one emergency manager has to decide which shelters to open, where to send buses and how to spend a fixed budget, often within hours. The plan that looks best on paper is often the first thing to break: a shelter loses power, a road floods, the money falls short.
At the same time, most AI tools tell you what to do with total confidence and no proof. We wanted the opposite: an AI that attacks its own plan until it survives, and code that checks every number before a human ever sees it.
The timing made it possible. NASA-ISRO's NISAR L-band radar released calibrated data in July 2026, and it imaged Florida's Big Bend on Sep 28 (before) and Oct 10 (after) Hurricane Isaias made landfall on Oct 9. So we could score plans against where the water actually was, not against a forecast.
What it does
cascade.ai builds a hurricane shelter plan for Dixie and Taylor counties, stress-tests it, and writes it up as a report and PDF that an emergency manager can actually use:
- A Planner agent (Gemma 4) proposes a plan: which shelters to open, transport seats, and a reserve, plus claims about what it costs and how many people it shelters.
- A Verifier (plain code, no AI) recomputes those claims with a deterministic simulation and rejects any plan whose numbers don't match.
- An Adversary agent finds the one failure that hurts the plan most: a shelter goes offline, a town's road closes, or the budget is cut.
- After 3 rounds, code picks the plan that shelters the most people, then scores it under every single failure and every pair (about 75 cases), and builds a backup plan for each failure that hurts.
You watch it happen on the map: shelters light up, arcs run from each town to its shelters, attacks strike, people reroute, and backup plans take over.
How we built it
Flood truth from radar. Radar backscatter is compared in decibels between the two passes:
$$ \Delta = \sigma^0_{\text{after}} - \sigma^0_{\text{before}} \quad (\text{dB}), \qquad \sigma^0_{\text{dB}} = 10\log_{10}\sigma^0 $$
We used the 10 m GCOV product and averaged 6 × 6 pixels into cells of about 60 m. Averaging N = 36 samples cuts speckle noise by a factor of √36 = 6. A cell is flagged as:
- Open water when it darkens: Δ ≤ −3 dB and σ⁰ after the storm ≤ −15 dB.
- Water under tree canopy when it brightens beyond the scene's typical change: Δ minus the median change over land ≥ 3 dB, where the canopy was bright before the storm. Water under trees bounces L-band radar back twice, off the water and then the trunks ("double bounce"), which makes those cells brighter.
The result: 33,965 flagged cells (33,154 under canopy, 811 open water) over a 2.2-million-cell grid, with 960 people living in them according to 2020 Census blocks.
Simulation as the single source of truth. People walk through dry cells to the nearest open shelter within reach, transport carries the rest, and actions are funded in order until the money runs out. Every number in the app comes from this one function, including the report, the stress test and the map animation.
Verification. A claim passes only when
$$ |\text{claimed} - \text{recomputed}| \le \max(0.5,\; 0.01 \cdot \text{recomputed}) $$
and the plan fits the budget, uses only real, open shelters, and doesn't claim more than they hold.
Choosing the final plan. With s the share of affected people sheltered and F the set of single failures, plans are ranked by s, then by the robustness (the average share sheltered across every single failure)
$$ \bar{s} = \frac{1}{|F|}\sum_{f \in F} s_f $$
then by cost. A plan built entirely by code always competes too, so the final plan never shelters fewer people than the budget allows.
Stack: Python (uv, FastAPI, numpy, h5py) for radar ingest, the simulation and the agents; Gemma 4 26B via OpenRouter, with the Gemini API as a fallback, forced into a strict JSON schema so it can't invent shelters; Next.js 15, React 19, MapLibre and Tailwind for the map and report.
Challenges we ran into
- The coarse data showed almost nothing. At 200 m, only 0.006% of land pixels darkened. One day after landfall, the radar mostly sees water under the trees, not open floodwater, so we had to move to 10 m data and treat brightening as a flood signal too.
- Rain fooled a fixed threshold. Wet soil brightened land by a median of +1.55 dB, so a flat +3 dB rule flagged 16% of all land. Measuring change relative to the scene's median fixed it.
- Huge files, little time. Each radar granule is about 6.5 GB. We read only the Big Bend region using HTTP range requests, about 290 MB per granule, and stitched two radar frames together without a seam.
- LLMs propose well but add badly. Gemma copied totals well but added them up wrong. So code simulates each draft and hands back the exact cost of every action, and the Verifier still rejects anything that doesn't add up.
- Our first loop judged plans against a disaster. Attacks piled up round after round until nothing could survive. We redesigned it so failures are tests, not changes to the world.
- A "verified" plan can still be a bad one. A Gemma plan that sheltered only 453 of 960 people passed verification, even though $116,850 could shelter everyone. That's why the code-built plan now always competes.
- Making radar readable. Our first before/after view showed raw greyscale radar, which made no sense without radar training. We replaced it with a swipe over the normal map.
What we learned
- AI proposes, code checks. The most trustworthy design was letting the model be creative and making code the judge.
- Be honest about what "verified" means. It means the plan's numbers are true under our model. Radar shows water change, not depth, from a single pass. Every report lists these limits.
- L-band radar sees through forest canopy, which matters in the Big Bend, where much of the flooding sat under trees.
- A demo should show the reasoning. Watching the Adversary knock out a shelter and the people reroute explains the product better than any slide.
What's next
Better flood truth
- Track the water over time. NISAR passes over the same area every 12 days. Using every pass after a storm would show where water rose, where it drained and where it stayed, instead of a snapshot from one day after landfall.
- Estimate depth, not just presence. Combining radar with elevation data (USGS 3DEP lidar) and river gauges would let us estimate how deep the water is, so a plan can tell ankle-deep from life-threatening.
- Confirm the radar flags. Checking flagged cells against field reports, 911 calls and high-water marks would measure how often the radar is right, and cut false alarms from rain-wet ground.
- Plan before the storm, not only after. Feeding NOAA storm-surge and rainfall forecasts into the same loop would stress-test plans before landfall, then re-check them against the radar once it passes.
A more realistic simulation
- Real roads instead of grid steps. Routing people over OpenStreetMap roads, with flooded segments removed, would give real travel times, bridges that wash out, and closures that cut off single roads instead of whole towns.
- Real people, real behavior. Not everyone evacuates. Published evacuation rates, people without cars, people with medical needs and pets would decide who actually needs a shelter seat and a bus.
- Current, live resources. Pull today's shelter capacities, staffing and generator status from county systems, plus real bus fleets and drivers, instead of 2020 figures and assumed costs.
- More kinds of resources. Supplies, medical shelters, generators, staff shifts and fuel, so the Adversary can attack the things that really break during a storm.
For emergency managers
- Any county NISAR covers. Turn the Big Bend setup into a pipeline: pick a county, and it downloads the radar, census and FEMA data and builds the map automatically.
- Live updates during a storm. When a new radar pass or field report comes in, re-run the stress test and show only what changed in the plan.
- Fit into existing tools. Export to the formats counties already use (WebEOC, ICS forms, GIS layers), so the plan goes straight into the emergency operations center.
- Tabletop exercises. Let teams replay past storms, inject their own failures and compare their decisions with the stress-tested plan, as training before hurricane season.
- Field testing. Work with Florida county emergency managers to test it on a real exercise and learn what they need before they would trust it.
Beyond hurricanes
- Other disasters. The same loop works anywhere radar can see the damage: river floods, wildfire burn scars, earthquake ground movement and landslides. Only the hazard layer changes; the Planner, Adversary, Verifier and stress test stay the same.
Built With
- carto
- fastapi
- fema
- gemini-api
- gemma
- h5py
- maplibre
- nasa-earthdata
- next.js
- nisar
- numpy
- openrouter
- openstreetmap
- pillow
- pydantic
- pyproj
- pytest
- python
- react
- tailwindcss
- typescript
- us-census
- uv
- uvicorn
- vitest
Log in or sign up for Devpost to join the conversation.