Seismic vulnerability modelling

This example runs the full MDOF vulnerability workflow: backbone fitting, EDP-IM prediction, storey drift and floor acceleration demand estimation, and final loss computation via VMMDOF.

Input files

  • slf.json — storey loss functions per storey and component group, one set per building. See the SLF examples for how to generate these.

All other inputs (backbone parameters, hazard curve coefficients) are defined inline in the script below.

Running the example

import asyncio
import copy
import json

from djura.edp_im.predict import edp_im_batch
from djura.vulnerability_modeller.backbone import Backbone, get_infill_spo
from djura.vulnerability_modeller.demands import Demands
from djura.vulnerability_modeller.vm_mdof import VMMDOF
from djura.vulnerability_modeller.utilities import deep_merge

# --- 1. EDP-IM model parameters (one entry per building) ----------------

data_edp_im = [
    {
        "hysteresis": "bilin",
        "im_type": "sa",
        "method": "shahnazaryan-oreilly",
        "backboneMethod": "backbone",
        "backbone": {
            "damping": 0.02,
            "ductility": 3,
            "ductility_f": 6,
            "hardening_ratio": 0.05,
            "period": 1.0,
        },
    },
    {
        "hysteresis": "bilin",
        "im_type": "sa",
        "method": "shahnazaryan-oreilly",
        "backboneMethod": "backbone",
        "backbone": {
            "damping": 0.03,
            "ductility": 3,
            "ductility_f": 6,
            "hardening_ratio": 0.02,
            "period": 0.5,
        },
    },
    {
        "hysteresis": "infill",
        "im_type": "sa",
        "backboneMethod": "backbone",
        "backbone": {
            "period": 1.0,
            "c_y": 3.5,
            "c_rp": 2.0,
            "mu_h": 3.2,
            "mu_s": 3.2,
            "mu_rp": 3.5,
            "mu_ult": 10.0,
        },
    },
]

# --- 2. Building geometry and backbone parameters -----------------------

data_est = [
    {
        "backbone": {"heights": [3.5, 3.0, 3.0, 3.0, 3.0],
                     "dy": 0.03, "ductility_f": 6},
        "building-type": "frame",
    },
    {
        "backbone": {"heights": [4.5, 3.5, 3.5],
                     "dy": 0.035, "ductility_f": 6},
        "building-type": "frame",
    },
    {
        "backbone": {"heights": [3.5, 3.0, 3.0, 3.0, 3.0],
                     "dy": 0.02, "ductility_f": 6},
        "building-type": "infill",
    },
]

# --- 3. Site hazard curve coefficients (power-law fit) ------------------

hazard = {
    "pga": [0.0001144, 2.6348751, 0.2340722],
    "im":  [0.000298042, 2.551209397, 0.242236676],
}

# --- 4. Predict EDP-IM relationships ------------------------------------

edp_ims = edp_im_batch(data_edp_im)

# --- 5. Estimate storey drifts and floor accelerations ------------------

psds = asyncio.run(Demands(data_est).estimate_drifts())

data_acc = [deep_merge(d1, d2) for d1, d2 in zip(data_est, data_edp_im)]
for i, d in enumerate(data_acc):
    d["pfa-model"] = "muho"
    d["hazard"] = hazard
    d["psd"] = (
        next((r for r in psds["successes"] if r["index"] == i), None
             )["out"]["psd"]
        if i in psds["success_ids"] else []
    )
    d["sr"] = (
        next((r for r in edp_ims["successes"] if r["index"] == i), None
             )["out"]["strength-ratios"]
        if i in edp_ims["success_ids"] else []
    )

pfas = asyncio.run(Demands(data_acc).estimate_accelerations())

# --- 6. Load storey loss functions and assign storeys -------------------

with open("slf.json", encoding="utf-8") as f:
    base_slfs = json.load(f)

rc = 400_000   # replacement cost in the same currency as the SLFs

n = len(edp_ims["failure_ids"]) + len(edp_ims["success_ids"])
slfs = [copy.deepcopy(base_slfs) for _ in range(n)]

# Building 1: 5 storeys
slfs[0]["1 - NS: PFA"]["Storey"] = [0, 1, 2, 3, 4, 5]
slfs[0]["3 - NS: PSD"]["Storey"] = [1, 2, 3, 4, 5]
slfs[0]["2 - S: PSD"]["Storey"]  = [1, 2, 3, 4, 5]

# Building 2: 3 storeys
slfs[1]["1 - NS: PFA"]["Storey"] = [0, 1, 2, 3]
slfs[1]["3 - NS: PSD"]["Storey"] = [1, 2, 3]
slfs[1]["2 - S: PSD"]["Storey"]  = [1, 2, 3]

# Building 3 (infill): expected to fail — storey assignment not needed

for i, slf in enumerate(slfs):
    slf["rc"] = rc
    slf["cases"] = [i]

# --- 7. Run vulnerability assessment ------------------------------------

data = {
    "slfs": slfs,
    "edp-ims": edp_ims,
    "psds": psds,
    "pfas": pfas,
}

result = asyncio.run(VMMDOF([data]).vulnerability())

print("Status :", result["status"])
print("Results:", result)

Output

result is a dictionary with a "status" key ("success" or "failure") and per-building loss metrics. Buildings for which the EDP-IM or demand estimation step failed are reported in "failure_ids" and excluded from the loss calculation.

Workflow summary

The five steps mirror the djura vulnerability assessment pipeline:

  1. EDP-IMedp_im_batch fits strength-ratio curves to the backbone model for each building.

  2. Storey driftsDemands.estimate_drifts converts the backbone into peak storey drift profiles at each IM level.

  3. Floor accelerationsDemands.estimate_accelerations derives PFA profiles using the drift results and the selected PFA model.

  4. SLFs — pre-generated storey loss functions (see Storey loss function generation) map EDP demands to repair costs per storey.

  5. VMMDOF — assembles the above into expected annual loss and vulnerability curves for each building.