Source code for djura.vulnerability_modeller.eal

# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (C) 2025-2026 Djura | Risk - Data - Engineering S.r.l.
from typing import List
import numpy as np


[docs] class EAL: def __init__(self, elr: List[float], mafe: List[float]) -> None: """Object related to expected annual loss (EAL) computations Parameters ---------- elr : List[float] Expected loss ratio (ELR) mafe : List[float] Mean annual frequency of exceedance (MAFE) of a limit state """ self.elr = elr self.mafe = mafe
[docs] def fit_loss_curve(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ Fitting of a refined loss curve that passes through the performance limit states Returns ------- tuple[np.ndarray, np.ndarray, np.ndarray] Coefficients of the analytical form Fitted ELRs Fitted MAFEs Notes ----- The analytical form is defined as: .. math:: MAFE = \\mathrm{coef}_0 * exp( -\\mathrm{coef}_1 * log(ELR) - \\mathrm{coef}_2 * log(ELR)^2) # noqa: E501, W605 """ if len(self.elr) != len(self.mafe) != 3: raise ValueError( "Length of ELR and MAFE should be equal to 3 for fitting the " "refined curve") coef = np.zeros(len(self.elr)) r1 = np.zeros(len(self.elr)) r2 = np.zeros(len(self.elr)) r3 = np.zeros(len(self.elr)) r1[0] = 1 r2[0] = 1 r3[0] = 1 r1[1] = -np.log(self.elr[0]) r2[1] = -np.log(self.elr[1]) r3[1] = -np.log(self.elr[2]) r1[2] = -np.log(self.elr[0]) ** 2 r2[2] = -np.log(self.elr[1]) ** 2 r3[2] = -np.log(self.elr[2]) ** 2 temp1 = np.log(self.mafe) temp2 = np.array([r1, r2, r3]) temp3 = np.linalg.inv(temp2).dot(temp1) temp3 = temp3.tolist() coef[0] = np.exp(temp3[0]) coef[1] = temp3[1] coef[2] = temp3[2] elr_fit = np.linspace(0.01, 1., 100) mafe_fit = coef[0] * np.exp( -coef[1] * np.log(elr_fit) - coef[2] * np.log(elr_fit) ** 2) elr_fit = np.insert(elr_fit, 0, 0.0) mafe_fit = np.insert(mafe_fit, 0, mafe_fit[0]) return coef, elr_fit, mafe_fit
[docs] def get_eal( self, im_range, elr: List[float] = None, mafe: List[float] = None ) -> float: """Computes approximate EAL using the ELRs and MAFEs associated with the performance limit states Parameters ------- elr : List[float], optional Expected loss ratio (ELR), by default EAL.elr mafe : List[float], optional Mean annual frequency of exceedance (MAFE) of a limit state, by default EAL.mafe Returns ------- float EAL """ if mafe is None: mafe = np.array(self.mafe) if elr is None: elr = np.array(self.elr) # Hazard IML tests diml = np.diff(im_range) # dMAFEdIML, logarithmic gradient divided by IML step dmafe_diml = np.log(mafe[1:] / mafe[:-1]) / diml # Loss ratio step dmdf = np.diff(elr) # EAL contributions of each subgroup eal_bins = np.zeros(dmafe_diml.shape) eal_bins[dmafe_diml != 0] = elr[:-1] * mafe[:-1] * ( 1 - np.exp(dmafe_diml * diml)) - dmdf / diml * mafe[:-1] * ( np.exp(dmafe_diml * diml) * (diml - 1 / dmafe_diml) + 1 / dmafe_diml) eal = sum(eal_bins) + mafe[-1] # eal = mafe[0] * elr[0] # eals = (mafe[:-1] + mafe[1:]) / 2 * np.diff(elr) # return eal + sum(eals) return eal