Defining Adaptation Measures with configurations#
Introduction#
CLIMADA uses Measure objects to model the effects of adaptation measures. Measure objects were formerly defined declaratively (via for instance, a shifting or scaling of the hazard intensity or a change of impact function), and are now defined as python functions to enable more flexibility on the possible changes (see the tutorial on measure objects’).
The caveat of defining measure effects as python functions is that it cannot be serialized (written to a file), and also makes reading from a file a challenge.
In order to retain close that gap, the measure module now ships MeasureConfig objects, which handle the reading, writing and “declarative” defining of Measure objects.
Measure objects can be instantiated from MeasureConfig objects using Measure.from_config(<MeasureConfig object>).
Summary of Measure vs MeasureConfig#
|
|
|---|---|
Is used for the actual computation |
Is transformed into a |
Uses python function to define what change to apply to the |
Define the changes (functions) to apply via the former way (scaling/shifting effect, alternate file loading, etc.) |
Accepts any possible effect as long as it can be defined as a python function |
Is restricted to a set of defined effects |
Cannot be written to a file (unless it was created by a |
Can easily be read from/written to a file ( |
Configuration classes#
The definition of measures via MeasureConfig is organized into a hierarchy of specialized classes:
MeasureConfig: The top-level container for a single measure.HazardModifierConfig: Defines how the hazard is changed (e.g., shifting intensity).ImpfsetModifierConfig: Adjusts impact functions (e.g., scaling vulnerability curves).ExposuresModifierConfig: Modifies exposure data (e.g., reassigning IDs or zeroing regions).CostIncomeConfig: Handles the financial aspects, including initial costs and recurring income.
Note that everything can be defined and accessed directly from the MeasureConfig container, the underlying ones are there to keep things organized.
In the following we present each of these subclasses and the possibilities they offer.
Quickstart#
You can directly define a MeasureConfig object with a dictionary, using MeasureConfig.from_dict().
Below are the possible parameters:
Scope |
Parameter |
Type |
Description |
|---|---|---|---|
Top-Level |
|
|
Unique identifier for the measure. |
|
|
The hazard type this measure targets (e.g., “TC”, “FL”). |
|
|
|
Pandas offset alias (e.g., “2Y”) for implementation time. |
|
|
|
RGB triple (0-1 range) for plotting and visualization. |
|
Hazard |
|
|
Multiplier for hazard intensity (default: 1.0). |
|
|
Additive offset for hazard intensity (default: 0.0). |
|
|
Path to an HDF5 file to replace the current hazard. |
||
|
|
Return period (years) threshold; events below this are ignored. |
|
Impact Function |
|
|
Specific impact function IDs to modify (None = all). |
|
|
Scale or shift the Mean Damage Degree curve. |
|
|
|
Scale or shift the Percentage of Assets Affected curve. |
|
|
|
Scale or shift the intensity axis of the function. |
|
|
Path to an Excel file to replace the impact function set. |
||
Exposures |
|
|
Mapping |
|
|
List of Region IDs where exposure value is set to 0. |
|
|
Path to an HDF5 file to replace the current exposures. |
||
Cost & Income |
|
|
One-time investment cost (absolute value). |
|
|
Recurring maintenance/operational costs. |
|
|
|
Recurring income generated by the measure. |
|
|
|
Reference year for pricing (default: current year). |
|
|
|
Frequency of cash flows (e.g., “Y” for yearly). |
|
|
|
Explicit list of dates and values for complex cash flows. (See the cost income tutorial) |
from climada.entity.measures.measure_config import MeasureConfig
measure_dict = {
"name": "Tutorial measure",
"haz_type": "TC",
"impf_ids": [1, 2],
"impf_mdd_mult": 0.8,
"new_hazard_path": "path/to/new_hazard.h5",
"reassign_impf_id": {"TC": {1: 2}},
"color_rgb": [0.1, 0.5, 0.3],
"init_cost": 10000,
"periodic_cost": 500,
}
meas_config = MeasureConfig.from_dict(measure_dict)
print(meas_config)
MeasureConfig(
name='Tutorial measure'
haz_type='TC'
impfset_modifier=ImpfsetModifierConfig(
Non default fields:
impf_ids=[1, 2]
impf_mdd_mult=0.8
)
hazard_modifier=HazardModifierConfig(
Non default fields:
new_hazard_path='path/to/new_hazard.h5'
)
exposures_modifier=ExposuresModifierConfig(
Non default fields:
reassign_impf_id={'TC': {1: 2}}
)
cost_income=CostIncomeConfig(
Non default fields:
init_cost=10000
periodic_cost=500
)
implementation_duration=None
color_rgb=(0.1, 0.5, 0.3))
Modifying Impact Functions: ImpfsetModifierConfig#
The ImpfsetModifierConfig is used to define how an adaptation measure changes the vulnerability (refer to the impact functions tutorial).
When “translated” to a Measure object the ImpfsetModifierConfig populates the impfset_change attribute with a function that takes an ImpactFuncSet and returns a modified one, according to the specifications.
Note
Modifications are always applied to a specific hazard type (haz_type parameter).
ImpfsetModifierConfig allows you to modify the main components of an impact function set, as well as to replace it entirely:
The MDD (Mean Damage Degree) array: via
impf_mdd_multto scale it andimpf_mdd_addto shift it.The PAA (Percentage of Assets Affected) array: via
impf_paa_multto scale it andimpf_paa_addto shift it.The intensity array: via
impf_int_multto scale it andimpf_int_addto shift it.Replacing the set: via providing the
new_impfset_pathparameter. It needs to be a valid.xlsxfile readable byImpactFuncSet.from_excel()
See below for code examples.
Warning
If you provide a new_impfset_path and other modifiers, CLIMADA will load the new file first and then apply the modifiers to it. (A warning will be issued to ensure this sequence is intended).
Note
By default the changes are applied to all the impact functions in the set, but you can provide the impf_ids parameter to apply the changes to a selection of impact function ids.
from climada.entity.measures.measure_config import ImpfsetModifierConfig
# 1. Scaling existing Impact Functions
# Let's say we want to simulate a 20% reduction in MDD
# and a slight shift in the intensity threshold for Hazard 'TC'.
impf_mod_scaling = ImpfsetModifierConfig(
haz_type="TC",
impf_ids=[1, 2], # Apply only to specific function IDs
impf_mdd_mult=0.8, # Reduce Mean Damage Degree by 20%
impf_int_add=5.0, # Shift intensity axis by 5 units (e.g., higher resistance)
)
print("--- Scaling Config ---")
print(impf_mod_scaling)
# 2. Replacing the Impact Function Set from a file
# Useful for measures that implement completely new building standards.
impf_mod_replace = ImpfsetModifierConfig(
haz_type="TC", new_impfset_path="path/to/new_impact_functions.xlsx"
)
print("\n--- Replacement Config ---")
print(impf_mod_replace)
--- Scaling Config ---
ImpfsetModifierConfig(
Non default fields:
impf_ids=[1, 2]
impf_mdd_mult=0.8
impf_int_add=5.0
)
--- Replacement Config ---
ImpfsetModifierConfig(
Non default fields:
new_impfset_path='path/to/new_impact_functions.xlsx'
)
Modifying Hazards: HazardModifierConfig#
The HazardModifierConfig is used to define how an adaptation measure changes the hazard (refer to the hazard tutorial).
When “translated” to a Measure object the HazardModifierConfig populates the hazard_change attribute with a function that takes a Hazard (possibly additional arguments, see below) and returns a modified one, according to the specifications.
Note
Modifications are always applied to a specific hazard type (haz_type parameter).
HazardModifierConfig allows you to modify the intensity and frequency of the hazard, to apply a cutoff on the return period of impacts, as well as to replace it entirely:
The intensity matrix: via
haz_int_multto scale it andhaz_int_addto shift it.The frequency array: via
haz_freq_multto scale it andhaz_freq_addto shift it.Replacing the hazard: via providing the
new_hazard_pathparameter. It needs to be a valid hazard HDF5 file readable byHazard.from_hdf5()Applying a cutoff on frequency based on impacts: via
impact_rp_cutoff(see the note).
Note
Providing a value for impact_rp_cutoff “removes” (it sets their intensity to 0.) events from the hazard, for which the exceedance frequency (inverse of return period) of impacts is below the given threshold.
For instance providing 1/20, would remove all events whose impacts have a return period below 20 years.
In that case the function changing the hazard (Measure.hazard_change) will be a function with the following signature:
f(hazard: Hazard, # The hazard to apply on
exposures: Exposures, # The exposure for the impact computation
impfset: ImpactFuncSet, # The impfset for the impact computation
base_hazard: Hazard, # The hazard for the impact computation
exposures_region_id: Optional[list[int]] = None, # Region id to filter to
) -> Hazard
Warning
If you provide a new_hazard_path and other modifiers, CLIMADA will load the new file first and then apply the modifiers to it. (A warning will be issued to ensure this sequence is intended).
from climada.entity.measures.measure_config import HazardModifierConfig
# 1. Scaling existing hazard
# Let's say we want to simulate a 20% reduction in frequency
# and a reduction by 10m/s in the intensity for our tropical cyclones.
haz_mod = HazardModifierConfig(
haz_type="TC",
haz_int_add=-10, # Reduce hazard intensity by 10 units
haz_freq_mult=0.8, # Scale hazard frequency by 20%
)
print("--- Scaling Config ---")
print(haz_mod)
# 2. Replacing the hazard from a file
# Useful for measures that correspond to a different hazard modelling.
# E.g., a dike leading to a change in (physical) flood modelling.
haz_mod_new = HazardModifierConfig(
haz_type="FL", new_hazard_path="path/to/new_floods.h5"
)
print("\n--- Replacement Config ---")
print(haz_mod_new)
# 3. Applying a cutoff on the return period of the impacts
# Useful when measures are defined to avoid damage for a specific RP (exceedance frequency).
# Note that it looks a the distribution of the impacts, not the hazard intensity!
haz_mod_cutoff = HazardModifierConfig(
haz_type="TC",
impact_rp_cutoff=1
/ 20, # Set intensity to 0 for events with impacts with a return period below 20 years
)
print("\n--- Cutoff Config ---")
print(haz_mod_cutoff)
--- Scaling Config ---
HazardModifierConfig(
Non default fields:
haz_int_add=-10
haz_freq_mult=0.8
)
--- Replacement Config ---
HazardModifierConfig(
Non default fields:
new_hazard_path='path/to/new_floods.h5'
)
--- Cutoff Config ---
HazardModifierConfig(
Non default fields:
impact_rp_cutoff=0.05
)
Modifying Exposures: ExposuresModifierConfig#
The ExposuresModifierConfig is used to define how an adaptation measure changes the exposure (refer to the exposure tutorial).
When “translated” to a Measure object the ExposuresModifierConfig populates the exposures_change attribute with a function that takes an Exposures and returns a modified one, according to the specifications.
ExposuresModifierConfig allows you to modify the impact function assigned to different hazard, to set a list of points to 0 value, or to load a different Exposures:
Remapping the impact function: via
reassign_impf_idwith a dictionary of the form{haz_type: {old_id: new_id}}.Setting values to zero: via
set_to_zerowith a list of indices of the exposure GeoDataFrame.Replacing the exposure: via providing the
new_exposures_pathparameter. It need to be a valid HDF5 exposure file readable byExposures.from_hdf5()
Warning
If you provide a new_exposures_path and other modifiers, CLIMADA will load the new file first and then apply the modifiers to it. (A warning will be issued to ensure this sequence is intended).
from climada.entity.measures.measure_config import ExposuresModifierConfig
# 1. Changing existing Exposures
exp_mod = ExposuresModifierConfig(
reassign_impf_id={"TC": {1: 2}}, # Remaps exposures points with impf_TC == 1 to 2.
set_to_zero=[
0,
25,
78,
], # Sets the value of exposure points with index 0, 25 and 78 to 0.
)
print("--- First Config ---")
print(exp_mod)
# 2. Replacing the expoosure from a file
exp_mod_new = ExposuresModifierConfig(new_exposures_path="path/to/exposures.h5")
print("\n--- Replacement Config ---")
print(exp_mod_new)
--- First Config ---
ExposuresModifierConfig(
Non default fields:
reassign_impf_id={'TC': {1: 2}}
set_to_zero=[0, 25, 78]
)
--- Replacement Config ---
ExposuresModifierConfig(
Non default fields:
new_exposures_path='path/to/exposures.h5'
)
Defining the financial aspects of the measure#
For in depth description of CostIncome objects, refer to the related tutorial.
Note
The default for mkt_price_year if not provided is the current year.
You can easily define the CostIncome object to be associated with the measure using CostIncomeConfig:
from climada.entity.measures.measure_config import CostIncomeConfig
# This models a measure where costs increase by 2% annually,
# but it generates 100k in yearly income which grows by 3%.
growth_finance = CostIncomeConfig(
init_cost=500_000.0,
periodic_cost=20_000.0,
cost_yearly_growth_rate=0.02,
periodic_income=100_000.0,
income_yearly_growth_rate=0.03,
freq="Y",
)
print("\n--- Growth & Income Config ---")
print(growth_finance)
# Custom Cash Flow
# If the investment isn't linear (e.g., a major retrofit in year 5),
# you can define a list of specific events.
custom_schedule = [
{"date": "2024-01-01", "value": -1000000}, # Initial cost
{"date": "2029-01-01", "value": -200000}, # Mid-term overhaul
{"date": "2034-01-01", "value": 500000}, # Terminal value
]
custom_finance = CostIncomeConfig(custom_cash_flows=custom_schedule)
print("\n--- Custom Schedule Config ---")
print(custom_finance)
--- Growth & Income Config ---
CostIncomeConfig(
Non default fields:
init_cost=500000.0
periodic_cost=20000.0
periodic_income=100000.0
cost_yearly_growth_rate=0.02
income_yearly_growth_rate=0.03
)
--- Custom Schedule Config ---
CostIncomeConfig(
Non default fields:
custom_cash_flows=[{'date': '2024-01-01', 'value': -1000000}, {'date': '2029-01-01', 'value': -200000}, {'date': '2034-01-01', 'value': 500000}]
)
Reading from and writing to#
You can easily write/read measure configurations from YAML, as well as from pandas Series.
You can also create Measures/MeasureSet directly, using the same methods (these methods first load the file as a MeasureConfig and convert it directly to a Measure)
Similarly you can still create MeasureSet from legacy Excel or matlab files using MeasureSet.from_excel() which takes care of remapping the legacy parameter names to the new ones.
See the measure tutorial for more details on that.
import pandas as pd
from climada.entity.measures.measure_config import MeasureConfig
# 1. Exporting to YAML
# Assuming 'my_measure_config' is a MeasureConfig object created previously
my_measure_config.to_yaml("seawall_config.yaml")
# 2. Loading from YAML
loaded_measure_config = MeasureConfig.from_yaml("seawall_config.yaml")
# 3. Loading from Pandas
row_data = pd.Series({
"name": "Mangrove_Restoration",
"haz_type": "TC",
"impf_mdd_mult": 0.7,
"init_cost": 250000,
"color_rgb": (0.1, 0.8, 0.1)
})
pandas_measure_config = MeasureConfig.from_row(row_data)
# 4. Measure object directly
measure = Measure.from_yaml("seawall_config.yaml")