Custom vulnerability functions for asset-level impact calculations

In this walk-through, custom vulnerability functions are defined and used to perform the asset-level impact calculations. The portfolio is identical to the one previously calculated; the difference is only in the config-based vulnerability.

There is no option exposed to inject a custom vulnerability config via the API at the moment, hence the library is used.

We start by defining a portfolio and an acute vulnerability function for Wind.

[1]:
# pip install nbformat pandas plotly requests
[ ]:
from io import StringIO
import json
from typing import NamedTuple
from dependency_injector import providers
from physrisk.container import Container
from physrisk.vulnerability_models.vulnerability import VulnerabilityModelsFactory
from physrisk.vulnerability_models.config_based_impact_curves import (
    VulnerabilityConfigItem,
    config_items_from_csv,
    config_items_to_df,
)
import plotly.io
from plotly.subplots import make_subplots

plotly.io.renderers.default = "notebook"

Define a portfolio. The “type” attribute can be used to define an arbitrary scheme to capture categories of assets, matching the vulnerability configuration.

[3]:
portfolio = {
    "items": [
        {
            "asset_class": "RealEstateAsset",
            "type": "Buildings/Industrial",
            "location": "Asia",
            "latitude": 24.0426,
            "longitude": 91.0158,
        },
        {
            "asset_class": "RealEstateAsset",
            "type": "Buildings/Industrial",
            "location": "Asia",
            "latitude": 22.6588,
            "longitude": 90.3373,
        },
    ]
}
request = {
    "assets": portfolio,
    "include_asset_level": True,
    "include_calc_details": True,
    "include_measures": True,
    "years": [2050],
    "scenario": "ssp585",
}

Configuration can be defined programmatically, but is also intended to be maintained in CSV format. Here we write the config line to CSV and read back again. In practise, a file of config lines can be maintained.

[4]:
config_items = [
    VulnerabilityConfigItem(
        hazard_class="Wind",
        asset_class="RealEstateAsset",
        asset_identifier="type=Buildings/Industrial,location=Asia",
        indicator_id="max_speed",
        indicator_units="m/s",
        impact_id="damage",
        impact_units=None,
        curve_type="indicator/piecewise_linear",
        points_x=[20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 110],
        points_y=[
            0,
            0,
            0,
            0.03,
            0.11,
            0.23,
            0.37,
            0.52,
            0.63,
            0.71,
            0.78,
            0.83,
            0.87,
            0.89,
            0.92,
            1.0,
        ],
        activation_of_points_x=None,
        cap_of_points_x=None,
        cap_of_points_y=None,
    )
]

csv_config = config_items_to_df(config_items).to_csv(index=False)
print(csv_config)

config_items = config_items_from_csv(StringIO(csv_config))
hazard_class,asset_class,asset_identifier,indicator_id,indicator_units,impact_id,impact_units,curve_type,points_x,points_y,points_z,cap_of_points_x,cap_of_points_y,activation_of_points_x
Wind,RealEstateAsset,"type=Buildings/Industrial,location=Asia",max_speed,m/s,damage,,indicator/piecewise_linear,"[20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 110.0]","[0.0, 0.0, 0.0, 0.03, 0.11, 0.23, 0.37, 0.52, 0.63, 0.71, 0.78, 0.83, 0.87, 0.89, 0.92, 1.0]",,,,

Having defined the configuration the calculation is run. Dependency injection is used to apply the custom configuration.

[5]:
# we make use of dependency injection to inject a vulnerability model that accepts the configuration.
container = Container()
container.override_providers(
    vulnerability_models_factory=providers.Factory(
        VulnerabilityModelsFactory, config=config_items
    )
)
requester = container.requester()
response = json.loads(
    requester.get(request_id="get_asset_impact", request_dict=request)
)
[6]:
asset0_impacts = response["asset_impacts"][1]["impacts"]


class Key(NamedTuple):
    hazard_type: str
    scenario_id: str
    year: str


asset0_impact_dict = {}
for i in asset0_impacts:
    key = i["key"]
    asset0_impact_dict[Key(key["hazard_type"], key["scenario_id"], key["year"])] = i

hazard_types = set(k.hazard_type for k in asset0_impact_dict.keys())
wind_impact_histo = asset0_impact_dict[Key("Wind", "historical", "None")]
wind_impact_ssp585 = asset0_impact_dict[Key("Wind", "ssp585", "2050")]

exceedance_histo = wind_impact_histo["impact_exceedance"]
exceedance_ssp585 = wind_impact_ssp585["impact_exceedance"]

fig1 = make_subplots(rows=1, cols=1)

fig1.add_scatter(
    x=exceedance_histo["exceed_probabilities"],
    y=exceedance_histo["values"],
    name="baseline wind",
    row=1,
    col=1,
)
fig1.add_scatter(
    x=exceedance_ssp585["exceed_probabilities"],
    y=exceedance_ssp585["values"],
    name="wind SSP585",
    row=1,
    col=1,
)
fig1.update_xaxes(
    title="Annual exceedance probability",
    title_font={"size": 14},
    row=1,
    col=1,
    type="log",
    autorange="reversed",
)
fig1.update_yaxes(
    title="Damage as fraction of insurable value", title_font={"size": 14}, row=1, col=1
)