{ "cells": [ { "cell_type": "markdown", "id": "cadaa7ff-dc17-4898-b781-35f92d2583e6", "metadata": {}, "source": [ "(measure-tutorial)=\n", "\n", "# Defining Adaptation Measures with python functions" ] }, { "cell_type": "markdown", "id": "c9170963-353e-4332-a8be-74ffbe78aa57", "metadata": {}, "source": [ "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).\n", "\n", "The primary purpose of a `Measure` is to modify one or more parts of the \"Risk Triplet\": **Exposures**, **Impact Functions**, and **Hazard**." ] }, { "cell_type": "markdown", "id": "f8f9dc2c-81dd-409d-ab02-9be8b8a052c4", "metadata": {}, "source": [ "## Quickstart" ] }, { "cell_type": "code", "execution_count": 1, "id": "0685d8fb-68b6-4a65-a0b4-860e68a46c7d", "metadata": {}, "outputs": [], "source": [ "from climada.entity.measures.measure import Measure\n", "from climada.entity.measures.cost_income import CostIncome\n", "\n", "\n", "# Define one or more transformation function\n", "# It must take an Exposures, Hazard or ImpactFuncSEt as the first argument\n", "# and return an object of the same type.\n", "def reduce_exposure_value(exposures, reduction_factor=0.2):\n", " \"\"\"Reduces the value of all assets by a fixed percentage.\"\"\"\n", " exposures.gdf[\"value\"] *= 1 - reduction_factor\n", " return exposures\n", "\n", "\n", "def sea_wall_protection(hazard, threshold=2.0):\n", " \"\"\"Cuts off flood intensity below a certain threshold.\"\"\"\n", " # intensity is typically a sparse matrix in CLIMADA hazards\n", " hazard.intensity.data[hazard.intensity.data < threshold] = 0\n", " return hazard\n", "\n", "\n", "# Define financial aspects\n", "cost_income = CostIncome(init_cost=1_000_000)\n", "\n", "# Initialize the Measure object\n", "retrofitting_and_seawall = Measure(\n", " name=\"Building Retrofit and seawall construction\",\n", " exposures_changes=reduce_exposure_value,\n", " hazard_changes=sea_wall_protection,\n", " cost_income=cost_income,\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "id": "66e0629f-da41-42e9-b3d5-02458c88d693", "metadata": {}, "source": [ "The measure can then be used to create a new triplet corresponding to the modifications or to compute the impact directly:\n", "\n", "```python\n", "new_exp, new_impf, new_haz = retrofitting.apply(exp, impf, haz, kwargs_hazard={'threshold': 3.5})\n", "impact_with_measure = measure.calc_impact(exp, impf, haz)\n", "\n", "```" ] }, { "cell_type": "markdown", "id": "56bbbcd2-f92f-4697-8631-18b9a311d4da", "metadata": {}, "source": [ "## Core logic of the Measure object\n", "\n", "When you initialize a `Measure`, you can provide one to three callables (python functions):\n", "\n", "1. **`exposures_changes`**: Modifies the `Exposures` object, the asset location, value, or metadata (e.g., relocating houses from a flood zone).\n", "2. **`impfset_changes`**: Modifies the `ImpactFuncSet` object, the vulnerability of assets (e.g., updating an impact function because houses were elevated).\n", "3. **`hazard_changes`**: Modifies the `Hazard` object, the intensity or frequency of events (e.g., a dam reducing flood depths).\n", "\n", "```{note}\n", "By default, any component not explicitly defined uses the `identity_function`, which simply returns the input object without modification.\n", "```\n", "\n", "The **only** requirement for these python functions is to **take** and **return** an `Exposures` object (or `ImpactFuncSet` or `Hazard`), which enable advanced modeling.\n", "\n", "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.\n", "\n", "For example:\n", "> \"Only reduce the exposure value (Exposure change) in areas where the peak water depth (Hazard context) exceeds 2 meters.\"\n", "\n", "Could be defined like this:" ] }, { "cell_type": "code", "execution_count": null, "id": "63b37e27-f4d1-4a0c-8a45-f923aea903e2", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "\n", "def reduce_exposure_by_depth(exposures, reduction_factor=0.2, threshold=2.0, **kwargs):\n", " \"\"\"\n", " Reduces asset value by a percentage, but only for assets located where\n", " the peak hazard intensity (e.g., water depth) exceeds the threshold.\n", " \"\"\"\n", " # Retrieve the hazard context passed by Measure.apply\n", " base_hazard = kwargs.get(\"base_hazard\")\n", "\n", " # Get the peak intensity for each centroid across all events\n", " peak_depths = np.array(base_hazard.intensity.max(axis=0).todense()).flatten()\n", "\n", " # Ensure exposures are mapped to hazard centroids\n", " if \"centr_ID\" not in exposures.gdf.columns:\n", " exposures.assign_centroids(base_hazard)\n", "\n", " # Identify which exposure points meet the depth criteria\n", " # 'centr_ID' maps each exposure row to a column index in\n", " # the hazard intensity matrix\n", " centr_indices = exposures.gdf[\"centr_ID\"].values\n", " point_peak_depths = peak_depths[centr_indices]\n", "\n", " # We apply the reduction only where Depth > threshold\n", " mask = point_peak_depths > threshold\n", " exposures.gdf.loc[mask, \"value\"] *= 1 - reduction_factor\n", "\n", " return exposures" ] }, { "cell_type": "markdown", "id": "d069e22e-1c31-44ef-a3db-62d3269f3900", "metadata": {}, "source": [ "```python\n", "# --- Usage Example ---\n", "# Define the measure\n", "flood_protection = Measure(\n", " name=\"High-Threshold Value Protection\",\n", " exposures_changes=reduce_exposure_by_depth\n", ")\n", "\n", "# When you call apply, the `Hazard` haz is automatically passed to the function\n", "# as 'base_hazard'\n", "new_exp, _, _ = flood_protection.apply(\n", " exp, impf, haz, \n", " kwargs_exposures={'reduction_factor': 0.5, 'threshold': 2.0}\n", ")\n", "\n", "# You can also overide this with another \"base\" hazard in the \n", "# `kwarg_exposures` if you want:\n", "new_exp, _, _ = flood_protection.apply(\n", " exp, impf, haz, \n", " kwargs_exposures={'base_hazard'= other_hazard,\n", " 'reduction_factor': 0.5, 'threshold': 2.0}\n", ")\n", "```" ] }, { "cell_type": "markdown", "id": "ebf8620a-181d-4f3c-989f-e4373380032b", "metadata": {}, "source": [ "```{note}\n", "The functions you define yourself are automatically made to accept keyword arguments if they do not already.\n", "```\n", "\n", "```{note}\n", "The functions do not necessarily need to use the Exposures|Hazard|ImpactFuncSet object, but they still have to accept it as an argument.\n", "```" ] }, { "cell_type": "markdown", "id": "fb888e10-5a94-4251-88be-5d92dfee2e0a", "metadata": {}, "source": [ "### Deep copies\n", "\n", "To prevent accidental \"in-place\" modification of your baseline data, `Measure` utilizes `copy.deepcopy()`. \n", "If `enforce_copy=True` in `apply()` (the default case), the object is cloned before the transformation function runs, ensuring the original objects remains untouched." ] }, { "cell_type": "markdown", "id": "80c13c5a-0048-4b80-bd35-9faffae52cae", "metadata": {}, "source": [ "### Measure cash flow (`CostIncome`)\n", "\n", "The `cost_income` attribute allows the measure to carry its own. It can notably store:\n", "\n", "* Initial investment costs.\n", "* Maintenance costs over time.\n", "* Potential indirect income generated by the measure.\n", "\n", "For more information and advanced usage see the [detailed tutorial on cost income object](cost-income-tutorial)." ] }, { "cell_type": "markdown", "id": "26b926d2-1f27-416d-925c-53bfaa0cee27", "metadata": {}, "source": [ "## Defining Measure using measure configuration" ] }, { "cell_type": "markdown", "id": "99bb6869-30f0-49b5-9c56-8664fd85318a", "metadata": {}, "source": [ "Defining adaptation measures with python functions can be a bit \\" ] }, { "cell_type": "code", "execution_count": null, "id": "01f29057-fc12-4787-92be-e4c506a054d1", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:climada_env_dev]", "language": "python", "name": "conda-env-climada_env_dev-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }