Defining Adaptation Measures with python functions#
The Measure object is the core component of the Adaptation module in CLIMADA. It acts as a wrapper for transformation logic, allowing you to model how specific interventions alter the risk landscape (like building sea walls, retrofitting houses, or implementing early warning systems).
The primary purpose of a Measure is to modify one or more parts of the “Risk Triplet”: Exposures, Impact Functions, and Hazard.
Quickstart#
from climada.entity.measures.measure import Measure
from climada.entity.measures.cost_income import CostIncome
# Define one or more transformation function
# It must take an Exposures, Hazard or ImpactFuncSEt as the first argument
# and return an object of the same type.
def reduce_exposure_value(exposures, reduction_factor=0.2):
"""Reduces the value of all assets by a fixed percentage."""
exposures.gdf["value"] *= 1 - reduction_factor
return exposures
def sea_wall_protection(hazard, threshold=2.0):
"""Cuts off flood intensity below a certain threshold."""
# intensity is typically a sparse matrix in CLIMADA hazards
hazard.intensity.data[hazard.intensity.data < threshold] = 0
return hazard
# Define financial aspects
cost_income = CostIncome(init_cost=1_000_000)
# Initialize the Measure object
retrofitting_and_seawall = Measure(
name="Building Retrofit and seawall construction",
exposures_changes=reduce_exposure_value,
hazard_changes=sea_wall_protection,
cost_income=cost_income,
)
The measure can then be used to create a new triplet corresponding to the modifications or to compute the impact directly:
new_exp, new_impf, new_haz = retrofitting.apply(exp, impf, haz, kwargs_hazard={'threshold': 3.5})
impact_with_measure = measure.calc_impact(exp, impf, haz)
Core logic of the Measure object#
When you initialize a Measure, you can provide one to three callables (python functions):
exposures_changes: Modifies theExposuresobject, the asset location, value, or metadata (e.g., relocating houses from a flood zone).impfset_changes: Modifies theImpactFuncSetobject, the vulnerability of assets (e.g., updating an impact function because houses were elevated).hazard_changes: Modifies theHazardobject, the intensity or frequency of events (e.g., a dam reducing flood depths).
Note
By default, any component not explicitly defined uses the identity_function, which simply returns the input object without modification.
The only requirement for these python functions is to take and return an Exposures object (or ImpactFuncSet or Hazard), which enable advanced modeling.
Notably, the apply() method always passes the entire triplet (base_exposures, base_impfset, base_hazard) to every transformation function, so you can write complex logic.
For example:
“Only reduce the exposure value (Exposure change) in areas where the peak water depth (Hazard context) exceeds 2 meters.”
Could be defined like this:
import numpy as np
def reduce_exposure_by_depth(exposures, reduction_factor=0.2, threshold=2.0, **kwargs):
"""
Reduces asset value by a percentage, but only for assets located where
the peak hazard intensity (e.g., water depth) exceeds the threshold.
"""
# Retrieve the hazard context passed by Measure.apply
base_hazard = kwargs.get("base_hazard")
# Get the peak intensity for each centroid across all events
peak_depths = np.array(base_hazard.intensity.max(axis=0).todense()).flatten()
# Ensure exposures are mapped to hazard centroids
if "centr_ID" not in exposures.gdf.columns:
exposures.assign_centroids(base_hazard)
# Identify which exposure points meet the depth criteria
# 'centr_ID' maps each exposure row to a column index in
# the hazard intensity matrix
centr_indices = exposures.gdf["centr_ID"].values
point_peak_depths = peak_depths[centr_indices]
# We apply the reduction only where Depth > threshold
mask = point_peak_depths > threshold
exposures.gdf.loc[mask, "value"] *= 1 - reduction_factor
return exposures
# --- Usage Example ---
# Define the measure
flood_protection = Measure(
name="High-Threshold Value Protection",
exposures_changes=reduce_exposure_by_depth
)
# When you call apply, the `Hazard` haz is automatically passed to the function
# as 'base_hazard'
new_exp, _, _ = flood_protection.apply(
exp, impf, haz,
kwargs_exposures={'reduction_factor': 0.5, 'threshold': 2.0}
)
# You can also overide this with another "base" hazard in the
# `kwarg_exposures` if you want:
new_exp, _, _ = flood_protection.apply(
exp, impf, haz,
kwargs_exposures={'base_hazard'= other_hazard,
'reduction_factor': 0.5, 'threshold': 2.0}
)
Note
The functions you define yourself are automatically made to accept keyword arguments if they do not already.
Note
The functions do not necessarily need to use the Exposures|Hazard|ImpactFuncSet object, but they still have to accept it as an argument.
Deep copies#
To prevent accidental “in-place” modification of your baseline data, Measure utilizes copy.deepcopy().
If enforce_copy=True in apply() (the default case), the object is cloned before the transformation function runs, ensuring the original objects remains untouched.
Measure cash flow (CostIncome)#
The cost_income attribute allows the measure to carry its own. It can notably store:
Initial investment costs.
Maintenance costs over time.
Potential indirect income generated by the measure.
For more information and advanced usage see the detailed tutorial on cost income object.
Defining Measure using measure configuration#
Defining adaptation measures with python functions can be a bit <continue on for MeasureConfig linkage>