import flood_adapt.objects.forcing as f
from pathlib import Path
from datetime import datetime
from flood_adapt.objects import SyntheticEvent, TimeFrame
from flood_adapt import unit_system as us
from flood_adapt import FloodAdapt, Settings
# Setup FloodAdapt
= Path("../../_data/examples/static-data").resolve()
STATIC_DATA_DIR = Settings(
settings =Path("../../_data/examples").resolve(),
DATABASE_ROOT="charleston_test"
DATABASE_NAME
)= FloodAdapt(database_path=settings.database_path) fa
📘 Example: Creating a Synthetic Event in FloodAdapt
This notebook demonstrates how to create a synthetic event using FloodAdapt. Synthetic events are valuable for controlled testing, sensitivity analysis, and understanding the behavior of flood models under simplified or hypothetical scenarios.
A FloodAdapt Event consists of 2 things:
- a
TimeFrame
describing the start and end time of the hazard simulation(s) - a collection of forcings to be applied to the hazard model(s)
In this example, we construct a full synthetic event with water level
, rainfall
, wind
, and river discharge
forcings, and then save it to a FloodAdapt database.
⏱️ Step 1. Setup and Imports
We begin by importing the required classes and modules for constructing synthetic forcings and managing event data within the FloodAdapt framework.
🗓️ Step 2. Define the Simulation Time Frame
We specify a one-day time frame for the SyntheticEvent
, from January 1 to January 2, 2025.
# Create an time frame for the simulation
= datetime(year=2025, month=1, day=1)
start_time = datetime(year=2025, month=1, day=2)
end_time = TimeFrame(start_time=start_time, end_time=end_time) time_frame
🌊 Step 3. Define Water Level Forcing
Synthetic water levels are constructed from a combination of tidal and surge components. All FloodAdapt events require exactly 1 water level forcing, all other forcings are optional.
# Synthetic water levels can be computed from a combination of surge and tide.
= f.SurgeModel(
surge =f.GaussianTimeseries(
timeseries=us.UnitfulTime(value=12, units=us.UnitTypesTime.hours),
duration# Choose the middle of the time frame for peak time
=us.UnitfulTime.from_timedelta(time_frame.duration / 2),
peak_time=us.UnitfulLength(value=2, units=us.UnitTypesLength.meters),
peak_value
)
)
= f.TideModel(
tide =us.UnitfulLength(value=1, units=us.UnitTypesLength.meters),
harmonic_amplitude# Choose the middle of the time frame for peak time
=us.UnitfulTime.from_timedelta(time_frame.duration / 2),
harmonic_phase=us.UnitfulTime(value=12.4, units=us.UnitTypesTime.hours),
harmonic_period
)
= f.WaterlevelSynthetic(
water_levels_synthetic =surge,
surge=tide,
tide
)
# Inspect
= surge.timeseries.to_dataframe(time_frame=time_frame)
gaussian = ["Surge"]
gaussian.columns = tide.to_dataframe(time_frame=time_frame)
harmonic = ["Tide"]
harmonic.columns = water_levels_synthetic.to_dataframe(time_frame=time_frame)
total = ["Water levels"]
total.columns = gaussian.join(harmonic).join(total)
df_combined
df_combined.plot(="Synthetic Water Levels (Surge + Tide)",
title="Time",
xlabel="Water Level (m)",
ylabel=True,
legend=(5, 2)
figsize )
🧩 Step 4. Create a minimal event and modify it
Given a water level forcing
, and a TimeFrame
, you can create the simplest possible event in FloodAdapt as shown below.
In many cases, it is interesting to to investigate a combination of different forcings. In steps 5 - 9, we will show the creation of various forcings and how to add them to an event.
= SyntheticEvent(
simple_event ="simple_event",
name=time_frame,
time={
forcings
f.ForcingType.WATERLEVEL: [water_levels_synthetic],
} )
🌧️ Step 5. Define Rainfall Forcing
The rainfall options for a synthetic event are:
- Constant: Constant rainfall intensity for the entire simultion period and spatially uniform across the model domain.
- Synthetic: Custom rainfall intensity timeseries built from key parameters applied spatially uniform across the entire model domain. The key parameters can be used to describe either a gaussian-, block- or triangle-shaped rainfall curve.
- CSV: Custom rainfall intensity from a csv file applied spatially uniform across the entire model domain.
= f.RainfallConstant(
rainfall_constant =us.UnitfulIntensity(value=10, units=us.UnitTypesIntensity.mm_hr)
intensity
)
= f.RainfallSynthetic(
rainfall_synthetic =f.GaussianTimeseries(
timeseries=us.UnitfulTime(value=12, units=us.UnitTypesTime.hours),
duration# Choose the middle of the time frame for peak time
=us.UnitfulTime.from_timedelta(time_frame.duration / 2),
peak_time=us.UnitfulIntensity(value=10, units=us.UnitTypesIntensity.mm_hr),
peak_value
)
)
= f.RainfallCSV(path=STATIC_DATA_DIR / "rainfall.csv")
rainfall_csv
# Inspect
= rainfall_constant.to_dataframe(time_frame=time_frame)
df_constant = ["Constant"]
df_constant.columns = rainfall_synthetic.to_dataframe(time_frame=time_frame)
df_synthetic = ["Synthetic"]
df_synthetic.columns = rainfall_csv.to_dataframe(time_frame=time_frame)
df_csv = ["CSV"]
df_csv.columns
= df_constant.join(df_synthetic).join(df_csv)
df_combined
df_combined.plot(="Synthetic Rainfall (Constant, Gaussian, CSV)",
title="Time",
xlabel="Rainfall Intensity (mm/hr)",
ylabel=True,
legend=(5, 2)
figsize )
🌬️ Step 5. Define Wind Forcing
The wind options for a synthetic event are:
- CSV: Custom wind speed and direction from a csv file applied spatially uniform across the entire model domain.
- Constant: Constant wind speed and direction is applied spatially uniform across the entire model domain.
Note: Wind set up in embayments, lagoons etc is only included for the water bodies included in the overland model. Large scale wind effects creating surges and wind set up are typically included in the water level forcing. When the water level forcing is set to “Model” (see ‘Historical Event’), this effect can be simulated by FloodAdapt in the offshore model.
= f.WindConstant(
wind_constant =us.UnitfulVelocity(value=12, units=us.UnitTypesVelocity.mps),
speed=us.UnitfulDirection(value=2, units=us.UnitTypesDirection.degrees)
direction# 0 degrees is North, 90 degrees is East, 180 degrees is South, and 270 degrees is West
)
= f.WindCSV(path=STATIC_DATA_DIR / "wind.csv")
wind_csv
# Inspect
= wind_constant.to_dataframe(time_frame=time_frame)
df = ["speed", "direction"]
df.columns = wind_csv.to_dataframe(time_frame=time_frame)
df_csv = ["speed", "direction"]
df_csv.columns = df.join(df_csv, lsuffix="_constant", rsuffix="_csv")
df_combined
df_combined.plot(="Synthetic Wind (Constant, CSV)",
title="Time",
xlabel="Wind Speed (m/s)",
ylabel=True,
legend=(5, 2),
figsize )
🏞️ Step 6. Define River Discharge Forcing
Discharge is required to be defined for all pre-configured rivers. These rivers are registered in the hazard model configuration beforehand and it is important that the order of rivers and their source locations are consistent with the setup of the hazard model. The timeseries data provided will determine the in-flow of water at the river’s location.
Discharge options for a SyntheticEvent
are:
- Constant: Constant discharge for the entire simulation period.
- CSV: Custom discharge specified in a csv file.
- Synthetic: Custom discharge timeseries built from key parameters. The key parameters can be used to describe either a gaussian-, block- or triangle-shaped rainfall curve.
# The available rivers are defined in the hazard model when creating the database.
# You cannot add new rivers to the model in an event, you can only set the discharge of each given river.
print(f"Number of available rivers: {len(fa.database.site.sfincs.river)}")
= fa.database.site.sfincs.river[0]
river
= f.DischargeConstant(
discharge_constant =river,
river=us.UnitfulDischarge(value=100, units=us.UnitTypesDischarge.cms)
discharge
)
= f.DischargeSynthetic(
discharge_synthetic =river,
river=f.BlockTimeseries(
timeseries=us.UnitfulTime(value=12, units=us.UnitTypesTime.hours),
duration# Choose the middle of the time frame for peak time
=us.UnitfulTime.from_timedelta(time_frame.duration / 2),
peak_time=us.UnitfulDischarge(value=80, units=us.UnitTypesDischarge.cms),
peak_value
)
)
= f.DischargeCSV(
discharge_csv =river,
river=STATIC_DATA_DIR / "discharge.csv"
path
)
# Inspect
= discharge_constant.to_dataframe(time_frame=time_frame)
df_constant = ["Constant"]
df_constant.columns = discharge_synthetic.to_dataframe(time_frame=time_frame)
df_synthetic = ["Synthetic"]
df_synthetic.columns = discharge_csv.to_dataframe(time_frame=time_frame)
df_csv = ["CSV"]
df_csv.columns = df_constant.join(df_synthetic).join(df_csv)
df_combined
df_combined.plot(="Synthetic Discharge (Constant, Gaussian, CSV)",
title="Time",
xlabel="Discharge (cms)",
ylabel=True,
legend=(5, 2)
figsize )
Number of available rivers: 1
🧩 Step 7. Combine Forcings and Create Synthetic Event
All defined forcings are collected into a single dictionary, which is used to construct a SyntheticEvent
. Now construct the forcings dictionary that contains the forcings you want to include.
NOTE: each event can only have 1 forcing of the types:
water level
,rainfall
andwind
. Fordischarge
however, each river is required to have a forcing associated with it.
# Create a SyntheticEvent with the forcings and time frame
= SyntheticEvent(
full_event ="example_synthetic_event",
name=time_frame,
time={
forcings
f.ForcingType.WATERLEVEL: [water_levels_synthetic],
f.ForcingType.RAINFALL: [rainfall_constant],
f.ForcingType.WIND: [wind_csv],
f.ForcingType.DISCHARGE: [discharge_synthetic], # The discharge list needs to be exactly as long as the number of rivers in the hazard model
}, )
💾 Step 8. Save the Event to a FloodAdapt Database
Finally, we save the event to a FloodAdapt database.
# Save the event to the database
=full_event) fa.save_event(event