πŸ“˜ Example: Advanced Scenario

In this notebook we demonstrate the workflow on how you can build an advanced FloodAdapt scenario in Charleston, USA, using the API.

In this notebook we will cover the following steps:

  1. Create a synthetic event
  2. Create a projection - Sea level rise (SLR)
  3. Create a measure and strategy - Seawall
  4. Create and run a scenario
  5. Investigate the output

Import libraries

# Import packages
import contextily as cx
import geopandas as gpd
import rasterio
import rasterio.plot

from datetime import datetime
from pathlib import Path
from IPython.display import HTML

from flood_adapt.objects import forcing as f
from flood_adapt.objects import (
    HurricaneEvent,
    TimeFrame,
    Elevate,
    FloodProof,
    FloodWall,
    GreenInfrastructure,
    SelectionType,
    MeasureType,
    Projection, 
    PhysicalProjection, 
    SocioEconomicChange,
    Scenario, 
    Strategy, 
)

from flood_adapt.objects.events.hurricane import TranslationModel

from flood_adapt import FloodAdapt, Settings
from flood_adapt import unit_system as us

πŸš€ Step 1. Reading-in the FloodAdapt database

Let’s start with initiating the database and FloodAdapt class. 1. Initiate the database class Settings by defining the DATABASE_ROOT and DATABASE_NAME. 2. Initiate the FloodAdapt class by parsing the Settings().database_path.

# Define the static data folder
STATIC_DATA_DIR = Path("../../_data/examples/static-data/5_Advanced_Scenario").resolve()
SFINCS_BIN_PATH = Path("../../_data/system/win-64/sfincs/sfincs.exe").resolve()
FIAT_BIN_PATH = Path("../../_data/system/win-64/fiat/fiat.exe").resolve()

# Set up the settings for the database
settings = Settings(
    DATABASE_ROOT=Path("../../_data/examples").resolve(),
    DATABASE_NAME="charleston_test",
    FIAT_BIN_PATH=FIAT_BIN_PATH,
    SFINCS_BIN_PATH=SFINCS_BIN_PATH,
    VALIDATE_BINARIES=True,
)

# Create the FloodAdapt instance
fa = FloodAdapt(settings.database_path)

🌊 Step 2. Events - Create a synthetic Event

Events in FloodAdapt are categorized into different forcings: 1. Wind 2. Rainfall 3. Discharge 4. Water Level

If you want to learn more about the individual forcings in FloodAdapt, please go and read the section on Events in the FloodAdapt documentation.

When creating an event, we need to create an Event object. Depending on which type of event we create, we select a different class. In this example we create a synthetic event, therefore we use the SyntheticEvent class.

To create the SyntheticEvent object we use the time attribute to define the event duration. This should be parsed as a TimeFrame object. In the forcings attribute we aggregated the different forcing objects in a dictionary.

In this event example we will create an event with the following forcings:
🌬️ WindConstant: Define a value for a constant wind speed (mps) and direction (degrees)
🌧️ RainfallConstant: Define a value for a constant rainfall (mm/hr)
πŸ’¦ DischargeConstant: Define the x and y coordinates of the discharge point of the Cooper River and a value for a constant mean discharge (cfs) in the River- and Discharge model (same value)
🌊 WaterlevelSynthetic SurgeModel: Define a peak time (h), peak value in (m) and duration (d)
β†”οΈŽοΈ WaterlevelSynthetic TideModel: Define the harmonic amplitude (m), harmonic period (h) and harmonic phase (h)

For a complete guide on all the possible event options and inputs check out the notebook specifically on events.

Step 2.1: Create a Historical Hurricane Event

In FloodAdapt we can re-create and edit historical hurricanes and explore their impacts with altered parameters to answer community questions e.g. what would have happened if the hurrican made landfall further south/north? To create a hurricane we need to do he following steps: 1. Set a time frame 2. Define the waterlevel 3. Obtain the hurricane track 4. Add rainfall 5. Add wind 6. Add River discharge

For a detailed description on how to create a hurricane event go to the event notebook.

# Create an time frame for the simulation
start_time = datetime(year=2025, month=1, day=1) 
end_time = datetime(year=2025, month=1, day=2)
time_frame = TimeFrame(start_time=start_time, end_time=end_time)

# Define Water Level Forcing
water_levels = f.WaterlevelModel()

# Obtain a hurricane track
## Get the cyclone database
cyclone_db = fa.database.static.get_cyclone_track_database()
ian_index = cyclone_db.list_names().index("IAN")

## Not all cyclone tracks have names, in addition to duplicate names existing, so it is better to use the index
track = fa.get_cyclone_track_by_index(index=ian_index) 
track_file = STATIC_DATA_DIR / "IAN.cyc"
track.write_track(filename=track_file, fmt="ddb_cyc")

## Optionally translate the cyclone track from what is defined in the file
translation = TranslationModel(
    eastwest_translation=us.UnitfulLength(value=3000, units=us.UnitTypesLength.meters),
    northsouth_translation=us.UnitfulLength(value=5000, units=us.UnitTypesLength.meters),
)

# We want to include the rainfall and wind from the hurricane track
rainfall = f.RainfallTrack(path=track_file)
wind = f.WindTrack(path=track_file)

# 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.
river = fa.database.site.sfincs.river[0]

discharge = f.DischargeConstant(
    river=river,
    discharge=us.UnitfulDischarge(value=100, units=us.UnitTypesDischarge.cms)
)

# Inspect
df = discharge.to_dataframe(time_frame=time_frame)
df.plot(title="Constant Discharge River", xlabel="Time", ylabel="Discharge (cms)", legend=True, figsize=(5, 2))

πŸŒͺοΈπŸŒ€ Let’s put the forcings together and create the HurricaneEvent object.

# Create a hurricaneEvent with the forcings and time frame
event = HurricaneEvent(
    name="ian_hurricane_event",
    time=time_frame,
    forcings = {
        # The keys of the dictionary are the forcing types, and the values are lists of the corresponding forcing objects.
        f.ForcingType.WATERLEVEL: [water_levels],
        f.ForcingType.RAINFALL: [rainfall],
        f.ForcingType.WIND: [wind],
        f.ForcingType.DISCHARGE: [discharge],
    },
    track_name=track.name,
    hurricane_translation=translation,
)

πŸ’Ύ Step 2.1. Saving the event to the database

# Save the event to the database
fa.save_event(event)

πŸ“ˆ Step 3. Projections - Create a projection

Projections in FloodAdapt allow us to adjust our model to future conditions such as sea level rise or/and population growth. If you want to learn more about projections in FlooAdapt, please go to the section Projections in the FloodAdapt documentation.

The projections can be divided into two categories: 1. 🌊 Physical Projections: Sea level rise, intensified precipitation, increased storm frequency 2. πŸ’° Socio economic change: Population growth (existing built area, new development area), economic growth

When creating a projection we need to create a Projection object. The PhysicalProjection attribute is parsed as a PhysicalProjection object which captures the physical projection such as sea lvel rise. The SocioEconomicChange attribute is parsed as a SocioEconomicChange object which captures the socioeconomic projection such as population growth. It’s not mandatory to parse both projections. If we only want to use one of the two types of projections we can leave the other one blank ().

The attributes of the PhysicalProjection or SocioEconomicChange object define the projection. In this case we parse the attribute sea_level_rise to the PhysicalProjection object and define the value in UnitfulLength and the unit in UnitTypesLength.

To get a deeper understanding for all the possible projections and their inputs go to the notebook specifically about projections.

In this example we will make use of sea level rise scenarios for a PhysicalProjection and explore population growth as SocioEconomicChange.

First, let’s get the sea level rise scenarios from the database.
A FloodAdapt database can include sea level rise scenarios, describing a timeline of future sea level rise relative to a reference year. If these scenarios are available in the database the get_slr_scn_names() method will return a list of the available scenarios. To get a deeper understanding on how you can create different Projections read the projectionblablab notebook.

# Get the Scenarios
fa.get_slr_scn_names()

# Interpolate until 2050
fa.interp_slr(slr_scenario="ssp585", year=2060)
0.85

Use that projection to create the Projection object.

projection = Projection(
    name="proj_2060",
    description="2060 projection", 
    physical_projection= PhysicalProjection(
        sea_level_rise=us.UnitfulLength(value=fa.interp_slr(slr_scenario="ssp585", year=2060),
        units=us.UnitTypesLength.meters)
    ),
)
projection
Projection(name='proj_2060', description='2060 projection', physical_projection=PhysicalProjection(sea_level_rise=UnitfulLength(value=0.85, units=UnitTypesLength.meters), subsidence=UnitfulLength(value=0.0, units=UnitTypesLength.meters), rainfall_multiplier=1.0, storm_frequency_increase=0.0), socio_economic_change=SocioEconomicChange(population_growth_existing=0.0, economic_growth=0.0, population_growth_new=0.0, new_development_elevation=None, new_development_shapefile=None))

Now we can create the population growth projection. To capture population growth, we will create a new development area and pass the population_growth_existing and population_growth_new. You find a detailed explanation of these input attributes in the FloodAdapt User Guide.

We can have a quick look at the new development area.

new_dev = gpd.read_file(STATIC_DATA_DIR / "new_dev.gpkg")
new_dev.explore()
Make this Notebook Trusted to load map: File -> Trust Notebook

Now we can create the SocioEconomicChange object as part of the Projection object we created above. Then we are ready to save the projection.

# Create new development area
projection.socio_economic_change = SocioEconomicChange(
    population_growth_existing=10,
    economic_growth=0,
    population_growth_new=5,
    new_development_shapefile=str(STATIC_DATA_DIR / "new_dev.gpkg"),
    new_development_elevation=us.UnitfulLengthRefValue(
        value=0.5,
        units=us.UnitTypesLength.meters,
        type=us.VerticalReference.datum
    )
)

πŸ’Ύ Step 3.1. Saving the projection to the database

# Save projection
fa.save_projection(projection)

🧱 Step 4. Measures - Create a measure

Measures in FloodAdapt enable the user to mititgate the event impacts and investigate their efficiency on the fly.

Measures can be: 1. πŸ’¦ Hydraulic measures on the hazard level 2. 🌱 Green infrastructure measures on the hazard level 3. 🏠 Impact measures on the building level.

You can read more about measures in the section Measures in the FloodAdapt documentation.

πŸ’¦ Hydraulic measures
In this example we will create two hydraulic measures, a sea wall of 12ft and a water square.

🧱 To create a measure we need to create a Measure object. In the attributes we define the measure type object, in the first example a FloodWall object. Additionally to the other attributes, we need to add the elevation value as UnitfulLength and the unit as UnitTypesLength of the sea wall.

# Create a measure object for a seawall
seawall = FloodWall(
    name="seawall_16ft",
    description="16ft Seawall",
    selection_type=SelectionType.polyline,
    polygon_file=str(STATIC_DATA_DIR / "seawall.geojson"),
    elevation=us.UnitfulLength(value=16, units=us.UnitTypesLength.feet)
)

🟦 Next, we create the GreenInfrastructure water square object. Instead of the elevation attribute we need to define the volume of the total storage capacity of the water square. We use the UnitfulVolume object to define the value and unit of the storage capacity. Optionally we can add the height of the water square to validate the total storage volume.

# Create a measure object for a water square
water_square = GreenInfrastructure(
    name="water_square",
    description="Water Square",
    type=MeasureType.water_square,
    selection_type=SelectionType.polygon,
    polygon_file=str(STATIC_DATA_DIR / "water_square.geojson"),
    volume=us.UnitfulVolume(value=43975190.31512848, units=us.UnitTypesVolume.cf),
    height=us.UnitfulHeight(value=3, units = us.UnitTypesLength.feet)
)

🏠 Impact Measure
Let’s add two more measures on the impact level. We can for example flood proof and elevate buildings in a specific area to mititgate the impact on these assets.

⬆️ When elevating buildings we need to create a Elevate object. To specify which buildings should be elevated we choose a selection_type and then either provide a spatial file with the boundaries of the area or select a whole aggregation area from our model. We can also specify which building types we want the measure to be applied on by defining the property type attribute. we need to use the property type (e.g. residential, commercial, ALL…) that is used in our Delft-FIAT Model.
To capture the magnitude of elevation of the buildings we define the elevation attribute. Here we provide the value, unit of type UnitTypesLength, and vertical reference of type VerticalReference as part of the UnitfulLengthRefValue object.

# Create a measure object for elevating buildings
elevate = Elevate(
    name="elevated_homes_1ft",
    description="Elevate residential buildings",
    selection_type=SelectionType.polygon,
    polygon_file=str(STATIC_DATA_DIR / "raise_property_polygon.geojson"),
    property_type="ALL",
    elevation=us.UnitfulLengthRefValue(value=1, units=us.UnitTypesLength.feet, type=us.VerticalReference.floodmap)
)

🦺 When we flood proof buildings we follow similar steps as when we elevate buildings. We create a FloodProof object where we define the same attributes as in the Elevate object. The only difference is here is, that we do not need to provide a vertical_reference for the elevation attribute.

# Create a measure object for flood proofing commercial buildings
flood_proof = FloodProof(
    name="floodproof_all_commercial",
    description="Floodproofing all commercial buildings.",
    selection_type=SelectionType.all,
    property_type="commercial",
    elevation=us.UnitfulLength(value=2, units=us.UnitTypesLength.feet)
)

πŸ’Ύ Step 4.1. Saving the measure to the database

# Save the measure
fa.save_measure(seawall)
fa.save_measure(water_square)
fa.save_measure(elevate)
fa.save_measure(flood_proof)

🧩 Step 5. Strategies - Create a strategy

Strategies are combinations measures. They allow us to run an test multiple measures in a single model run.

To create a strategy we need to create a Strategy object. In the measures attribute we parse a list of all the names of the measures that we want to apply in that strategy.

In this example we will create a strategy with the measures we created above, a sea wall of 12ft, a water square, elevating buildings 2ft above the floodmap reference in a specific area and flood proofing commercial buildings up to 1 ft.

# Create a strategy object
strategy = Strategy(
    name="seawall_greening_elev_build_floodproof_com",
    description="Strategy with a seawall, water square, elevation of buildings and floodp proofing commercial buildings",
    measures=[seawall.name, water_square.name, elevate.name, flood_proof.name],
)

πŸ’Ύ Step 5.1. Saving the strategy to the database

# Save the strategy
fa.save_strategy(strategy)

πŸ—ΊοΈ Step 6. Create a scenario

We reached the final step where we can put all the building blocks together to create a complete scenario!
A scenario is composed of:

1. Event
2. Projection
3. Strategy (Measures)

If you want to read more about the composition of scenarios, go read the Scenario-section of the FloodAdapt documentation.

When creating a scenario we need to create a Scenario object in which we parse the name of the event, projection and strategy as attributes.

# Create a scenario object
scenario = Scenario(
    name="slr_pop_growth_seawall_water_square_elev_floodproof_build",
    description="Nearshore event with SLR and population growth projection. Strategy with a seawall, water square, elevation of buildings and floodp proofing commercial buildings.",
    event=event.name,
    projection=projection.name,
    strategy=strategy.name,
)

πŸ’Ύ Step 6.1. Saving the scenario to the database

# Save the scenario
fa.save_scenario(scenario)

πŸƒβ€β™€οΈ Final step: Run a scenario

We are ready to run the scenario! Simply parse the scenario.name into the function run_scenario.

# Run the scenario
fa.run_scenario(scenario.name)
The provided timeseries does not cover the entire model time period.
2025-06-12 04:57:19 PM - FloodAdapt.SfincsAdapter - WARNING - Failed to add event rainfall multiplier, no rainfall forcing found in the model.
Downloading file 'v0.0.9/data_catalog.yml' from 'https://raw.githubusercontent.com/Deltares/hydromt/main/data/catalogs/artifact_data/v0.0.9/data_catalog.yml' to 'C:\Users\runneradmin\.hydromt_data\artifact_data'.
Downloading data from 'https://github.com/DirkEilander/hydromt-artifacts/releases/download/v0.0.9/data.tar.gz' to file 'C:\Users\runneradmin\.hydromt_data\artifact_data\v0.0.9\data.tar.gz'.
SHA256 hash of downloaded file: 32de5b95c171628547f303d7f65d53cbb1b9da9af4834717c8efff93fe55aad4
Use this value as the 'known_hash' argument of 'pooch.retrieve' to ensure that the file hasn't changed if it is downloaded again in the future.
Untarring contents of 'C:\Users\runneradmin\.hydromt_data\artifact_data\v0.0.9\data.tar.gz' to 'C:\Users\runneradmin\.hydromt_data\artifact_data\v0.0.9\data.tar'
GeoDataFrame : CRS from data catalog does not match CRS of data. The original CRS will be used. Please check your data catalog.
2025-06-12 04:57:22 PM - FloodAdapt.SfincsAdapter - WARNING - Could not use height data from file due to missing `z` column or missing values therein. Using uniform height of 16.0 feet instead.
GeoDataFrame : CRS from data catalog does not match CRS of data. The original CRS will be used. Please check your data catalog.
2025-06-12 04:57:23 PM - FloodAdapt.SfincsAdapter - WARNING - Failed to add projected rainfall multiplier, no rainfall forcing found in the model.
Replacing geom: obs
Model dir already exists and files might be overwritten: C:\a\FloodAdapt\FloodAdapt\docs\_data\examples\charleston_test\output\scenarios\slr_pop_growth_seawall_water_square_elev_floodproof_build\Flooding\simulations\overland\gis.

Finished!

Congratulations you created and ran your first FloodAdapt scenario!

Output: πŸ•΅οΈβ€β™€οΈ Let’s inspect the output

1. Output files

In your scenario output folder you should see the following files: - Flooding: Folder - Impacts: Folder - finished.txt: text file - Infometrics_β€œscenario_name”.csv: csv file of the overall infometrics - Infometrics_β€œscenario_nameβ€β€œaggregation_layer”.csv: csv file of the aggregated areas. You have one file per aggregation level. In this example we have two files. - logfile”scenario_name”.log: The log of the scenario run - **β€œscenario_name”_metrics.html**: A metric file of your scenario output

The figure below presents a visual overview of all the output files that should be in your database after running the scenario

2. Floodmap - Inspect the floodmap

We can open and inspect the floodmap geotiff.

# Plot max wwater level map
max_water_level = fa.get_max_water_level_map(scenario.name)
rasterio.plot.show(max_water_level, title="Max water level map", cmap="Blues")

# Plot floodmap geotiff
geotiff_file = fa.get_flood_map_geotiff(scenario.name)
tiff = rasterio.open(geotiff_file)
rasterio.plot.show(tiff, title="GeoTiff Floodmap", cmap="Blues")

3. Economic Impacts - Inspect the economic impacts on the building level and aggregated

We can plot the economic impacts on the building level and on the aggregated level.

## Building Impacts
# Open building impacts
gdf_impacts_buildings = fa.get_building_footprint_impacts(scenario.name).to_crs(epsg=3857)

# Plot building impacts
ax = gdf_impacts_buildings.plot(
    figsize=(10, 10),
    column="Total Damage", 
    cmap="Reds", 
    legend=True, 
    vmin=0, 
    vmax=60000, 
    legend_kwds={"label": "Total Damages ($) Buildings", "orientation": "horizontal"}
)
cx.add_basemap(ax)
ax.plot()

## Aggregated Impacts
impacts = fa.get_aggregated_impacts(scenario.name)
gdf_impacts_aggr_lvl1 = impacts["aggr_lvl_1"].to_crs(epsg=3857)

# Plot aggregated impacts
ax = gdf_impacts_aggr_lvl1.plot(
    figsize=(10, 10),
    column="TotalDamageEvent",
    cmap="Reds", 
    legend=True, 
    vmin=0, 
    vmax=10000000, 
    edgecolor="k", 
    legend_kwds={"label": "Total Damages ($) per aggregatetion area", "orientation": "horizontal"}
)
cx.add_basemap(ax)
ax.plot()

4. Infometrics & Infographics

Which infometrics and infographics to generate can be defined in the infometrics and infographics and configuration file in your database ../Database charleston_full/static/templates/infometrics/, ../Database/charleston_full/static/templates/infographics/"xyz”.toml, respectively.

The figure below shows the infographics of the scenario we created above.

# Display HTML infographics
fn = fa.get_infographic(scenario.name)
HTML(filename=fn)
Back to top