FloodGuard — Dam Management & Early-Warning System
FloodGuard is an advanced, real-time reservoir monitoring and predictive flood-risk decision-support system. Designed for critical infrastructure management, the platform ingests live sensor telemetry, performs high-resolution divided-difference forecasts, computes dynamic safety thresholds, and provides proportional gate-release recommendations for on-site engineers—warning of hazardous states hours before they manifest.
This repository hosts the canonical PostgreSQL database schema, the core calculation engine algorithms, the Next.js integration APIs, and a graphical weather simulation suite.
Table of Contents
- 1. Project Overview
- 2. System Architecture
- 3. PostgreSQL Database System Schema (3NF)
- 4. Calculation Reference & Predictive Algorithm
- 5. Machine Learning Integration Contract
- 6. Simulation & Testing Scenarios
- 7. Getting Started
- 8. Links
Team
- e22373, L. Sharmilan, e22373@eng.pdn.ac.lk
- e22382, F. R. Sujeevan, e22382@eng.pdn.ac.lk
- e22193, S. Kishonithan, e22193@eng.pdn.ac.lk
- e22397, R. Thilakshan, e22397@eng.pdn.ac.lk
Table of Contents
- Project Overview
- System Architecture
- PostgreSQL Database System Schema (3NF)
- Calculation Reference & Predictive Algorithm
- Machine Learning Integration Contract
- Simulation & Testing Scenarios
- Getting Started
- Links
1. Project Overview
Traditional dam safety protocols rely on static water-level thresholds. If a reservoir level crosses a fixed limit (e.g., 85%), gates are opened. However, under extreme storm conditions, the time required for physical gate configuration and downstream channel evacuation can exceed the safe response window.
FloodGuard implements a predictive, adaptive-threshold model where risk is assessed dynamically based on:
- Rise Rate & Acceleration: How fast the reservoir level is rising and whether that rate is accelerating.
- Weighted Rainfalls: Lagged, weighted precipitation rates measured across multiple meteorological stations in the catchment area.
- Upstream Inflow: Real-time inflow discharge rates feeding the reservoir.
- Downstream Channel Capacity: The capacity and water level of downstream channels to avoid flooding surrounding valleys.
Rather than waiting for a breach, the system extrapolates historical trends forward, identifies if and when the predicted water level will cross the predicted safety threshold (the Time-To-Crossing or TTC), and triggers alarms in advance.
2. System Architecture
flowchart LR
subgraph Field["Field Sensor & Simulation Layer"]
S1[Water Level Sensor]
S2[Meteorological Station Sensors]
S3[Upstream Inflow Sensor]
S4[Downstream Level Sensor]
SIM[Tkinter Weather Simulator]
end
subgraph Pred["Prediction & Calculation Layer"]
ALGO[Reference Mathematical Engine<br/>or Trained ML Model]
end
subgraph DB["PostgreSQL Database (Local)"]
WL[(water_level_readings)]
RF[(rainfall_readings)]
IF[(inflow_readings)]
DL[(downstream_level_readings)]
CM[(calculated_metrics)]
TC[(threshold_calculations)]
RS[(risk_status)]
RR[(release_recommendations)]
DT[(deescalation_tracking)]
AL[(alerts_log)]
end
subgraph FE["Frontend Dashboard"]
DASH[SCADA Operator UI]
end
S1 --> WL
S2 --> RF
S3 --> IF
S4 --> DL
SIM --> WL & RF & IF & DL
WL & RF & IF & DL --> ALGO
ALGO --> CM --> TC --> RS
RS --> RR
RS --> DT
RS --> AL
WL & CM & TC & RS & RR & AL --> DASH
The system operates on an event-driven and polling pipeline:
- Sensor Ingestion: Telemetry is written to the database.
- Processor Loop: Evaluates new raw telemetry, computes metrics and thresholds, checks predictive crossing models, and determines risk states.
- Frontend Refresh: Next.js API layer serves data, refreshing the SCADA panel dynamically on a 15-second polling interval.
3. PostgreSQL Database System Schema (3NF)
The database schema is fully normalized to Third Normal Form (3NF) to support transactional integrity and clean time-series storage.
3.1 Static Configuration Tables
dams
Holds physical limits, capacities, and baseline calibration thresholds for each dam.
dam_id(SERIAL, PRIMARY KEY): Unique identifier.dam_name(VARCHAR): Name of the dam.location(VARCHAR): GPS location name.latitude/longitude(DOUBLE PRECISION): GPS coordinates.elevation_m(DOUBLE PRECISION): Dam elevation above sea level.reservoir_capacity(DOUBLE PRECISION): Maximum reservoir volume ($m^3$).downstream_capacity(DOUBLE PRECISION): Maximum safe downstream outflow capacity ($m^3/s$).max_gate_capacity(DOUBLE PRECISION): Combined gate discharge capacity ($m^3/s$).if_baseline(DOUBLE PRECISION): Normal baseline inflow rate ($m^3/s$).base_threshold(DOUBLE PRECISION, DEFAULT 75.0): Baseline safe operating percentage limit ($L_{base}$).threshold_floor(DOUBLE PRECISION, DEFAULT 30.0): Absolute floor clamp for adjustments ($L_{floor}$).
engineers
System user profiles for dam operator logs and alert acknowledgements.
engineer_id(SERIAL, PRIMARY KEY): Unique identifier.name(VARCHAR): Operator username.role(VARCHAR): Job role.contact(VARCHAR): Contact info.assigned_dam_id(INT, REFERENCES dams): Currently assigned dam.password_hash(VARCHAR): Bcrypt hash for login credentials.
rainfall_locations
Catchment meteorological stations associated with the dam.
location_id(SERIAL, PRIMARY KEY): Unique identifier.location_name(VARCHAR): Station name.latitude/longitude(DOUBLE PRECISION): GPS coordinates.weight(DOUBLE PRECISION): Calibration coefficient of this station ($w_i$, $\sum w_i = 1.0$).delay_minutes(DOUBLE PRECISION): Physical lag time for rainfall runoff to reach reservoir ($\tau_i$).station_code(VARCHAR, UNIQUE): Station sensor identifier.is_active(BOOLEAN): Status of station connection.
3.2 Live Time-Series Sensor Tables (3NF)
water_level_readings
reading_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)reading_time(TIMESTAMPTZ): Telemetry timestamp ($t$).water_level_pct(DOUBLE PRECISION): Current water level percentage ($L(t)$).- Constraint: UNIQUE (
dam_id,reading_time)
inflow_readings
reading_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)reading_time(TIMESTAMPTZ): Inflow measurement time.inflow_rate_m3s(DOUBLE PRECISION): Upstream discharge rate ($IF(t)$, $m^3/s$).- Constraint: UNIQUE (
dam_id,reading_time)
downstream_level_readings
reading_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)reading_time(TIMESTAMPTZ): Downstream channel level measurement time.downstream_level_pct(DOUBLE PRECISION): Downstream channel level ($DL(t)$, %).- Constraint: UNIQUE (
dam_id,reading_time)
rainfall_readings
reading_id(BIGSERIAL, PRIMARY KEY)location_id(INT, REFERENCES rainfall_locations)reading_time(TIMESTAMPTZ): Precipitation measurement time.rainfall_mm_hr(DOUBLE PRECISION): Rainfall rate ($R_i(t)$, $mm/h$).- Constraint: UNIQUE (
location_id,reading_time)
3.3 Backend Prediction & Calculation Tables
prediction_runs
Records metadata for each divided-difference forecast run.
run_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)run_time(TIMESTAMPTZ): Timestamp when prediction ran.input_window_start/input_window_end(TIMESTAMPTZ): Bounds of data window.method(VARCHAR): Prediction model type (e.g."newton_divided_difference").status(VARCHAR): Result status (success/insufficient_data/error).
predicted_values
Contains extrapolated future states across forecast horizons ($t+15$ to $t+120$ mins).
value_id(BIGSERIAL, PRIMARY KEY)run_id(BIGINT, REFERENCES prediction_runs)horizon_minutes(INT): Forecast horizon in minutes ($h$, e.g., 15, 30, 45, 60, 90, 120).predicted_water_level_pct(DOUBLE PRECISION): Extrapolated reservoir level ($L_{pred}(t+h)$).predicted_r_net(DOUBLE PRECISION): Projected net rainfall runoff ($R_{net_pred}(t+h)$).predicted_inflow(DOUBLE PRECISION): Extrapolated river inflow ($IF_{pred}(t+h)$).predicted_downstream_level(DOUBLE PRECISION): Extrapolated downstream level ($DL_{pred}(t+h)$).predicted_rise_rate(DOUBLE PRECISION): Projected rise rate ($RR_{pred}(t+h)$).predicted_acc(DOUBLE PRECISION): Projected acceleration ($ACC_{pred}(t+h)$).predicted_adaptive_threshold(DOUBLE PRECISION): Calculated threshold ($AT_{pred}(t+h)$).gap(DOUBLE PRECISION): Difference margin ($AT_{pred}(t+h) - L_{pred}(t+h)$).
graph_crossing_results
Stores summary predictions on threshold crossings.
result_id(BIGSERIAL, PRIMARY KEY)run_id(BIGINT, REFERENCES prediction_runs)crossing_time_minutes(INT): Estimated Time-To-Crossing (TTC, minutes). Null if no crossing is predicted.minimum_gap(DOUBLE PRECISION): Minimum gap predicted across horizons.gap_trend(VARCHAR): Direction of the gap (increasing/decreasing/stable).final_status(risk_status_type): Predicted risk status.
calculated_metrics
Intermediate calculations performed per sensor scan.
metric_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)calc_time(TIMESTAMPTZ)rr_short(DOUBLE PRECISION): Short-term Rise Rate (%/h, 15-min window).rr_long(DOUBLE PRECISION): Long-term Rise Rate (%/h, 60-min window).acc(DOUBLE PRECISION): Water level rise acceleration ($ACC(t)$).rolling_avg(DOUBLE PRECISION): 3-hour rolling average of rise rates ($RA(t)$).deviation_score(DOUBLE PRECISION): Short-term rise deviation ($DEV(t)$).rr_band(rr_band_type): Evaluated rise rate band (NORMAL/ELEVATED/HIGH/CRITICAL).
threshold_calculations
calc_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)calc_time(TIMESTAMPTZ)rr_adj/rf_adj/if_adj/dl_adj(DOUBLE PRECISION): Factors pulling threshold down.adaptive_threshold(DOUBLE PRECISION): Computed live safety threshold ($AT(t)$).floor_triggered(BOOLEAN): True if clamped at $L_{floor}$ (30%).ceiling_triggered(BOOLEAN): True if clamped at $L_{base}$ (75%).
risk_status
Official alert state of the dam.
status_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)status_time(TIMESTAMPTZ)status(risk_status_type):GREEN,YELLOW,ORANGE,RED.ttc_minutes(INT): Active warning Time-To-Crossing.trigger_reason(TEXT): Text description of rule triggered.previous_status(risk_status_type)
release_recommendations
Proportional gate release plans calculated for ORANGE or RED status.
release_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)run_id(BIGINT, REFERENCES prediction_runs)calc_time(TIMESTAMPTZ)strategy(VARCHAR): Formula type ("proportional_rise_rate").rise_rate_used(DOUBLE PRECISION): $RR_{pred}(t+15)$ used for rate estimation.gate_opening_base_pct(DOUBLE PRECISION): Base gate percentage before downstream capacity overrides.q_desired(DOUBLE PRECISION): Volumetrically required release ($m^3/s$).q_downstream_available(DOUBLE PRECISION): Maximum safe downstream capacity window ($m^3/s$).q_release(DOUBLE PRECISION): Final recommended discharge rate ($m^3/s$).gate_opening_applied_pct(DOUBLE PRECISION): Final gate opening recommendation, rounded to 5%.conflict_warning(BOOLEAN): True if desired release exceeds downstream safety limits.estimated_duration_minutes(DOUBLE PRECISION): Time required to return to safe storage level.
deescalation_tracking
Maintains cumulative timers for de-escalation dampening.
tracking_id(BIGSERIAL, PRIMARY KEY)dam_id(INT, REFERENCES dams)condition_met_since(TIMESTAMPTZ): Start timestamp of sustained improvement.consecutive_minutes(INT): Tracked elapsed minutes.required_minutes(INT): Required wait duration for downgrade (15 / 30 / 60 mins).transition_from/transition_to(risk_status_type)eligible_flag(BOOLEAN)
4. Calculation Reference & Predictive Algorithm
The reference engine processes mathematical updates on every ingestion cycle (1-minute intervals during real-time telemetry, or 15-second intervals under simulator conditions).
4.1 Inflow, Level, & Runoff Inputs
The engine ingests:
- $L(t)$ : Reservoir water level (% of maximum height).
- $IF(t)$ : Upstream river inflow rate ($m^3/s$).
- $DL(t)$ : Downstream river level (% of safe capacity).
- $R_i(t)$ : Local rainfall rate ($mm/h$) at station $i$.
Weighted Net Catchment Rainfall ($R_{net}$)
To account for geographic delays, rainfall runoff is modeled by looking back at station delay offsets ($\tau_i$). If a station has missing telemetry, weights are scaled dynamically:
\[R_{net}(t) = \frac{\sum w_i R_i(t - \tau_i)}{\sum w_{active}}\]4.2 Rise Rate & Acceleration
Short-term Rise Rate ($RR_{short}$)
Measures water level rate of change over the last 15 minutes, scaled to an hourly rate:
\[RR_{short}(t) = [L(t) - L(t - 15)] \times 4 \quad (\%/hour)\]Long-term Rise Rate ($RR_{long}$)
Measures change over the last 60 minutes:
\[RR_{long}(t) = [L(t) - L(t - 60)] \times 1 \quad (\%/hour)\]Rise Rate Acceleration ($ACC$)
Measures the change in long-term rise rate over the preceding hour:
\[ACC(t) = RR_{long}(t) - RR_{long}(t - 60) \quad (\%/hour^2)\]Rolling Average ($RA$) & Deviation Score ($DEV$)
Calculates the 3-hour moving average of the long-term rise rate and compares it to the short-term spike rate to identify flash surge anomalies:
\[RA(t) = \text{Average}(RR_{long}) \text{ over last 3 hours}\] \[DEV(t) = RR_{short}(t) - RA(t)\]4.3 Severity Classifications (Bands)
Telemetry states are classified into four rise-rate severity bands. The worst individual parameter match determines the band.
| Severity Band | Long-Term Rise Rate | Short-Term Rise Rate | Acceleration |
|---|---|---|---|
| NORMAL | $< 1.0\,\%/h$ | AND $< 2.0\,\%/h$ | AND $\le 0.5\,\%/h^2$ |
| ELEVATED | $1.0\text{–}2.5\,\%/h$ | OR $2.0\text{–}4.0\,\%/h$ | OR $0.5\text{–}1.5\,\%/h^2$ |
| HIGH | $2.5\text{–}4.0\,\%/h$ | OR $4.0\text{–}7.0\,\%/h$ | OR $1.5\text{–}3.0\,\%/h^2$ |
| CRITICAL | $> 4.0\,\%/h$ | OR $> 7.0\,\%/h$ | OR $> 3.0\,\%/h^2$ (or $DEV > 5.0$) |
4.4 Adaptive Safety Threshold ($AT$)
The safety threshold shifts downwards from BASE (75%) depending on hydrological stresses, clamped at a minimum FLOOR (30%).
Individual Penalties
- Rise Rate Adjustment ($rr_{adj}$):
-
NORMAL: 0%ELEVATED: 8%HIGH: 18%CRITICAL: 30%
-
- Rainfall Adjustment ($rf_{adj}$):
-
$R_{net} < 10$: 0% $10\text{–}25$: 3% $25\text{–}50$: 7% $> 50\,\text{mm/h}$: 12%
-
- Inflow Adjustment ($if_{adj}$): (relative to baseline inflow $IF_{base}$)
-
$IF(t) < 1.5 \times IF_{base}$: 0% $1.5\text{–}2.5 \times$: 4% $2.5\text{–}4 \times$: 8% $> 4 \times IF_{base}$: 13%
-
- Downstream level Adjustment ($dl_{adj}$):
-
$DL < 50\%$: 0% $50\text{–}70\%$: 3% $70\text{–}85\%$: 8% $> 85\%$: 15%
-
4.5 Newton Divided-Difference Extrapolation
For prediction cycles, the engine fits a polynomial through recent historical readings to extrapolate water levels ($L_{pred}(t+h)$) and inflow rates ($IF_{pred}(t+h)$) for horizons $h \in {15, 30, 45, 60, 90, 120}$ minutes.
To avoid high-degree oscillations (Runge’s phenomenon), the engine selects a low-degree fit (linear or quadratic) using the last 3-4 historical data points spaced over a rolling 6-hour window.
Divided-Difference Table
Given nodes $(x_0, y_0), (x_1, y_1), \dots, (x_k, y_k)$, divided differences are defined recursively:
\[f[x_i] = y_i\] \[f[x_i, x_{i+1}, \dots, x_{i+j}] = \frac{f[x_{i+1}, \dots, x_{i+j}] - f[x_i, \dots, x_{i+j-1}]}{x_{i+j} - x_i}\]The interpolating polynomial is:
\[P(x) = f[x_0] + \sum_{i=1}^k f[x_0, \dots, x_i] \prod_{j=0}^{i-1} (x - x_j)\]4.6 Graph Crossing Analysis & Time-To-Crossing ($TTC$)
A crossing is predicted if the gap margin at any horizon $h$ falls to or below zero:
\[Gap(t+h) = AT_{pred}(t+h) - L_{pred}(t+h) \le 0\]- $TTC$ (Time-To-Crossing): The smallest horizon $h$ where $Gap(t+h) \le 0$.
- Gap Trend: Evaluated as
increasing,decreasing, orstableby analyzing the derivative differentials of the margins:
4.7 Risk Status Classification Rules
Risk statuses are evaluated in order of severity. First match wins.
| Risk Status | Trigger Conditions |
|---|---|
| 🔴 Red | $L(t) \ge AT(t)$ (Current level exceeds safety threshold) OR $TTC \le 15$ minutes (Crossing imminent) OR $RR_{band} = \text{CRITICAL}$ |
| 🟠 Orange | $L(t) \ge AT(t) + 3\%$ (Within 3% of threshold) OR ($TTC \le 60$ minutes) OR ($RR_{band} = \text{HIGH}$ AND Gap Trend = decreasing) |
| 🟡 Yellow | $L(t) \ge AT(t) + 10\%$ (Within 10% of threshold) OR ($TTC > 60$ minutes) OR ($RR_{band} = \text{ELEVATED}$) OR ($TTC = \text{Null}$ AND Gap Trend = decreasing) |
| 🟢 Green | All other conditions |
4.8 Proportional Release Recommendations
Calculated when risk status reaches ORANGE or RED.
-
Calculate Volumetrically Desired Discharge ($Q_{desired}$): Calculates the release rate required to bring the reservoir down to the threshold level over the next hour:
\[Q_{desired}(t) = IF(t) - \frac{(AT(t) - L(t)) \times \text{Capacity}_{reservoir}}{3600 \times 100} \quad (m^3/s)\] -
Downstream Safety Limit ($Q_{down_avail}$): Calculates the remaining capacity of the downstream channel:
\[Q_{down\_avail}(t) = \text{Capacity}_{downstream} \times \left(1 - \frac{DL(t)}{100}\right) \quad (m^3/s)\] -
Apply Safety Overrides:
\[Q_{release}(t) = \text{Min}\left(Q_{desired}(t),\; Q_{down\_avail}(t),\; \text{Capacity}_{max\_gate}\right)\]If $Q_{desired} > Q_{down_avail}$, a
CONFLICT WARNINGis flagged to inform operators that the downstream channel is constrained. -
Proportional Gate Opening:
\[\text{GateOpening}\% = \left(\frac{Q_{release}(t)}{\text{Capacity}_{max\_gate}}\right) \times 100\]Rounded to the nearest 5% for operator configuration.
-
Estimated Discharge Duration ($t_{duration}$): Calculates the duration (minutes) needed to lower the level to a target buffer level ($AT(t) - 10\%$):
\[t_{duration} = \frac{L(t) - (AT(t) - 10.0)}{\left(\frac{Q_{release} - IF(t)}{\text{Capacity}_{reservoir}}\right) \times 100 \times 60} \quad \text{minutes}\]
4.9 Sustained De-escalation Timers
Status downgrades are delayed to prevent rapid toggling due to noise. The system must meet de-escalation rules continuously:
- RED $\rightarrow$ ORANGE (15 Mins): $RR_{band} \in {\text{NORMAL}, \text{ELEVATED}}$ AND $ACC \le 0.0$ AND $L(t)$ stable or dropping.
- ORANGE $\rightarrow$ YELLOW (30 Mins): $RR_{band} \in {\text{NORMAL}, \text{ELEVATED}}$ AND $ACC \le 0.0$ AND ($R_{net}$ decreasing OR $R_{net} < 0.001\,\text{mm/h}$).
- YELLOW $\rightarrow$ GREEN (60 Mins): $RR_{band} = \text{NORMAL}$ AND $L(t)$ stable or dropping.
5. Machine Learning Integration Contract
The prediction layer is model-agnostic. The deterministic reference algorithm can be swapped for a trained ML model by satisfying this integration contract.
+------------------+ Reads telemetry +------------------+
| PostgreSQL | --------------------------> | Trained Model |
| Database | <-------------------------- | (Python Script) |
+------------------+ Writes predictions & +------------------+
risk status
5.1 Inputs
The model must query live and historical time-series data directly from:
water_level_readingsrainfall_readings(resolving weights and delays fromrainfall_locations)inflow_readingsdownstream_level_readings
5.2 Outputs
The model must write its calculated predictions into the database tables:
calculated_metrics: Populate rise rates, acceleration, rolling average, deviation, and rise rate band.threshold_calculations: Populate the calculated dynamic safety threshold and adjustments.risk_status: Set the calculated risk status.release_recommendations: Generate proportional gate openings and safe release rates.
6. Simulation & Testing Scenarios
The Tkinter weather simulator generates real-time telemetry to test system responses.
- Drought / Dry Season: Net rainfall is $0.0\,mm/h$, inflows decline ($80 \rightarrow 20\,m^3/s$), water level drops. Status remains
GREEN. - South-West Monsoon: Moderate rain ($5\text{–}16\,mm/h$), inflow increases ($120 \rightarrow 450\,m^3/s$), level rises. Status triggers
YELLOWandORANGE. - North-East Monsoon Storm: Spikes rain ($70\,mm/h$), inflow rises ($1800\,m^3/s$). Level exceeds threshold, status triggers
REDand release recommendations activate. - Inter-Monsoon Thunderstorm: Short convective rain burst ($90\,mm/h$), rapid rise rate triggers an immediate
REDstatus. - Tropical Cyclone Surge: Torrential rain ($50\,mm/h$), high downstream level ($DL > 85\%$). Triggers
REDwith active downstream constraints and conflict warnings.
7. Getting Started
We have containerized the core infrastructure (PostgreSQL database, Python backend, and Next.js frontend) using Docker for a seamless setup experience. The simulation GUI runs on your local machine so it can easily display its interface.
7.1 Start Core Services
- Copy
.env.exampleto.env:cp .env.example .env - Build and start the infrastructure in the background:
docker compose up -d --buildThis automatically sets up the PostgreSQL database with schemas and initial data, starts the backend processor, and serves the Next.js frontend on port 3000.
7.2 Access the Dashboard
Open your browser and navigate to http://localhost:3000 to view the SCADA control panel.
7.3 Run the Weather Simulator
Since the database container exposes port 5432, the Tkinter simulation GUI can run directly on your host machine and connect to it natively.
- Install Python dependencies:
pip install -r requirements.txt - Launch the simulator:
python code/simulation/db_simulator.py
8. Links
- Repository: https://github.com/cepdnaclk/e22-co2060-floodguard
- Project Site: https://cepdnaclk.github.io/e22-co2060-floodguard
- Department of Computer Engineering: http://www.ce.pdn.ac.lk/
- University of Peradeniya: https://eng.pdn.ac.lk/