Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
3692813
Implements equality methods for impf and impfset
spjuhel Mar 18, 2025
1606896
adds a few tests
spjuhel Mar 18, 2025
a8b45c6
Merge remote-tracking branch 'origin/develop' into feature/eq_methods…
spjuhel Mar 18, 2025
3776228
updates changelog
spjuhel Mar 18, 2025
f6d9feb
Improves dict comparison
spjuhel Mar 24, 2025
222644d
Applies suggestion from Lukas (1)
spjuhel Mar 24, 2025
29ecee4
init commit
spjuhel Apr 2, 2025
d7c0f23
Pushes tutorial notebook
spjuhel Apr 2, 2025
31ef976
Working tutorial
spjuhel Apr 2, 2025
10ca243
fixes a typo from mass edit
spjuhel Apr 2, 2025
389787e
Refactor to a more object-oriented design
spjuhel Apr 15, 2025
4ea685d
fixes some bugs
spjuhel Apr 15, 2025
0d6335b
fixses some linter issues
spjuhel Apr 16, 2025
31ea96d
feat(trajectory): interpolation and impactcalc files, eai_gdf metric
spjuhel Apr 17, 2025
3f13c2e
impact func __eq__
spjuhel Apr 24, 2025
519c9af
feat(test): adds test for ImpactFuncSet
spjuhel Apr 24, 2025
adfc7e2
fix(format): removes trailing whitespace
spjuhel Apr 24, 2025
6856117
Merge branch 'feature/eq_methods_for_impf' into feature/risk_trajectory
spjuhel Apr 24, 2025
9c358ca
fix: npv arg not considered in .all_risk_metrics()
spjuhel Apr 24, 2025
b7bf68b
refactor: enforce kwargs in RiskTrajectory init
spjuhel Apr 24, 2025
b75ab91
refactor: adds consistency between attribute
spjuhel Apr 24, 2025
e12b2c4
refactor(risktraj): lays better foundations for MeasureAppraiser
spjuhel Apr 25, 2025
8666197
minor additions, refactors and fixes
spjuhel May 7, 2025
9711379
format,doc: cleans up code, adds docstrings
spjuhel May 14, 2025
9aec92f
test: adds lots of unittests, and placeholders
spjuhel May 14, 2025
7387165
feature(Traj): fixes metrics generic and removes storing values
spjuhel May 19, 2025
20befdf
updates tutorial
spjuhel May 19, 2025
e06a3e7
doc(risk_traj): adds some docstrings
spjuhel May 21, 2025
924bb35
doc(module desc): improves top file desc
spjuhel May 21, 2025
6595e13
fix(log) : Removes endless logs
spjuhel May 21, 2025
94bd00e
fix(risk_traj): Fixes problems with >2 snapshots
spjuhel May 21, 2025
4921528
doc(notebook): adds disclaimers
spjuhel May 21, 2025
02244cb
fix(CalcRiskPeriod): fixes problem with undefined group_id
spjuhel May 22, 2025
b0a9f6d
fix(calcriskperiod): further fix for no group defined
spjuhel May 22, 2025
06afe2f
fix(groups): still fixing groups
spjuhel May 22, 2025
eceeba0
fix(groups): final fix (on the correct branch)
spjuhel May 22, 2025
a7dd0f7
Applies suggestion from Lucas (j/n)
spjuhel May 26, 2025
7989c71
Applies suggestion from Lucas (j/n)
spjuhel May 26, 2025
399572e
Applies suggestion from Lucas (j/n)
spjuhel May 26, 2025
3db32ca
Applies suggestion from Lucas (j/n)
spjuhel May 26, 2025
d2b0f6b
refactor(impact strat): Clarifies/Simplified computation
spjuhel Jun 5, 2025
3f0dfc6
feat(interpolation): Vastly improves interpolation of metrics
spjuhel Jun 5, 2025
d24a5b8
test(naming): updates names
spjuhel Jun 5, 2025
304a641
Squashed commit of the following:
spjuhel Jun 5, 2025
4ef643f
fix(plot, groups): improves plot, fixes group in eai
spjuhel Jun 23, 2025
bc38525
Merge remote-tracking branch 'origin/develop' into feature/risk_traje…
spjuhel Jun 23, 2025
33cfff4
fix(RiskPCalc): sorts the snapshots
spjuhel Jun 24, 2025
dd21493
doc(risk transfer): Improves clarity of risk transfer mechanism
spjuhel Jun 24, 2025
f4db1d0
format(docstring)
spjuhel Jun 24, 2025
c6e147e
test: fix for coverage
spjuhel Jul 1, 2025
25d0d0a
fix,doc(interpolation): minor improvements and fixes
spjuhel Jul 1, 2025
9634bd9
fix: minor fixes from testing
spjuhel Jul 1, 2025
d4af1a7
test: Updates test (98pct coverage o7)
spjuhel Jul 1, 2025
7e8df85
fix(group id): fix categories problem
spjuhel Jul 1, 2025
1bae9ad
still fixing groups
spjuhel Jul 1, 2025
b946f9b
group fixing
spjuhel Jul 1, 2025
622c8f6
fix(tests): fix tests with groups_id attr
spjuhel Jul 2, 2025
9c94c9f
tests: cleaning up
spjuhel Sep 9, 2025
953a5cf
updates notebook
spjuhel Sep 10, 2025
59bb831
more logical placement
spjuhel Sep 10, 2025
0a3a301
removes risk transfer mechanism (for now)
spjuhel Sep 10, 2025
b5f0bfc
doc(docstrings): Massive docstrings update + some renaming
spjuhel Sep 11, 2025
1fbe437
test: updates with new names and removing of risk transfer
spjuhel Sep 11, 2025
56966ca
Polishes names, plots, fixes minor bugs, fixes multiple snapshots
spjuhel Sep 12, 2025
a11a630
Updates tutorial
spjuhel Sep 12, 2025
6c8dd15
fix(RP impacts for periods): fixes aggregation (sum -> mean)
spjuhel Sep 16, 2025
3609471
drops unwanted col
spjuhel Sep 16, 2025
6eaf095
less confusing naming
spjuhel Sep 16, 2025
5ab8bda
improves structure
spjuhel Sep 16, 2025
21cc433
Improves for CB
spjuhel Sep 16, 2025
0a04ab9
Use of PeriodIndex instead of DateTimeIndex
spjuhel Sep 22, 2025
2bf7768
Big refactoring to include "static" (non-interpolated) trajectories
spjuhel Sep 23, 2025
e2fd678
Refactoring continued (Also now includes the new files)
spjuhel Sep 24, 2025
252e68e
fixes attachement/cover comment
spjuhel Sep 24, 2025
d694c64
Refactoring continued
spjuhel Sep 24, 2025
eadcb97
typing consistency
spjuhel Sep 24, 2025
fa17a48
fix apply_measure
spjuhel Sep 24, 2025
b4316d7
fixes Snapshot doc, makes measure and date immutable properties
spjuhel Sep 25, 2025
3a026cd
risk_disc -> risk_disc_rates, fixes return period mechanism
spjuhel Sep 25, 2025
3f0c5f9
Improves docstrings, typehint
spjuhel Sep 25, 2025
26bb85d
adds list comprehensions, fixes typo
spjuhel Sep 25, 2025
6e12608
improves docstring for calc strategies
spjuhel Sep 25, 2025
67641e0
first part of fix for cash_flows
spjuhel Sep 25, 2025
a68b79a
fixes cash_flow problem
spjuhel Sep 25, 2025
cbaca3c
force keyword args to avoid errors
spjuhel Sep 25, 2025
56f3715
fixes risk_contribution problem with multiple snapshot
spjuhel Sep 25, 2025
0234238
fix aggressive caching
spjuhel Sep 29, 2025
195a50f
fix missing group in exposure
spjuhel Sep 29, 2025
f14c9c1
Merge remote-tracking branch 'origin/develop' into feature/risk_traje…
spjuhel Oct 1, 2025
fe50ac1
adds risk metric unit based on exposure `value_unit`
spjuhel Oct 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions climada/test/test_trajectories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# 100% Coverage goal:
## Integration test for risk period with periods and freq defined
18 changes: 18 additions & 0 deletions climada/trajectories/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
This file is part of CLIMADA.

Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.

CLIMADA is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free
Software Foundation, version 3.

CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.

---
"""
267 changes: 267 additions & 0 deletions climada/trajectories/impact_calc_strat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
"""
This file is part of CLIMADA.

Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.

CLIMADA is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free
Software Foundation, version 3.

CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.

---

This modules implements the impact computation strategy objects for trajectories.

"""

import copy
from abc import ABC, abstractmethod
from typing import Any, Optional, Union

import numpy as np
from scipy import sparse

from climada.engine.impact import Impact
from climada.engine.impact_calc import ImpactCalc
from climada.entity.exposures.base import Exposures
from climada.entity.impact_funcs.impact_func_set import ImpactFuncSet
from climada.hazard.base import Hazard


class ImpactComputationStrategy(ABC):

Check warning on line 37 in climada/trajectories/impact_calc_strat.py

View check run for this annotation

Jenkins - WCR / Pylint

too-few-public-methods

LOW: Too few public methods (1/2)
Raw output
Used when class has too few public methods, so be sure it's really worth it.
"""
Interface for impact computation strategies.

This abstract class defines the contract for all concrete strategies
responsible for calculating and optionally modifying the total impact
based on a set of inputs (exposure, hazard, vulnerability).
"""

@abstractmethod
def compute_impacts(

Check warning on line 47 in climada/trajectories/impact_calc_strat.py

View check run for this annotation

Jenkins - WCR / Pylint

too-many-positional-arguments

LOW: Too many positional arguments (7/5)
Raw output
no description found
self,
exp: Exposures,
haz: Hazard,
vul: ImpactFuncSet,
risk_transf_attach: Optional[float] = None,
risk_transf_cover: Optional[float] = None,
calc_residual: bool = True,
) -> Impact:
"""
Calculates the total impact, including optional risk transfer application.

Parameters
----------
exp : Exposures
The exposure data.
haz : Hazard
The hazard data (e.g., event intensity).
vul : ImpactFuncSet
The set of vulnerability functions.
risk_transf_attach : float, optional
The attachment point (deductible) for the global risk transfer mechanism.
If None, default to 0.
risk_transf_cover : float, optional
The cover limit for the risk transfer mechanism. If None, the cover
is assumed to be infinite (only the attachment applies).
calc_residual : bool, default=True
If True, the function returns the residual impact (after risk transfer).
If False, it returns the transferred impact (the part covered by the
risk transfer).

Returns
-------
Impact
An object containing the computed total impact matrix and metrics.

See Also
--------
ImpactCalcComputation : The default implementation of this interface.
"""
pass

Check warning on line 87 in climada/trajectories/impact_calc_strat.py

View check run for this annotation

Jenkins - WCR / Pylint

unnecessary-pass

NORMAL: Unnecessary pass statement
Raw output
Used when a "pass" statement that can be avoided is encountered.


class ImpactCalcComputation(ImpactComputationStrategy):
"""
Default impact computation strategy.

This strategy first calculates the raw impact using the standard
:class:`ImpactCalc` logic and then applies a global risk transfer mechanism.
"""

def compute_impacts(

Check warning on line 98 in climada/trajectories/impact_calc_strat.py

View check run for this annotation

Jenkins - WCR / Pylint

too-many-positional-arguments

LOW: Too many positional arguments (7/5)
Raw output
no description found
self,
exp: Exposures,
haz: Hazard,
vul: ImpactFuncSet,
risk_transf_attach: Optional[float] = None,
risk_transf_cover: Optional[float] = None,
calc_residual: bool = False,
) -> Impact:
"""
Calculates the impact and applies the risk transfer mechanism.

This overrides the abstract method to implement the default strategy.

Parameters
----------
exp : Exposures
The exposure data.
haz : Hazard
The hazard data.
vul : ImpactFuncSet
The set of vulnerability functions.
risk_transf_attach : float, optional
The attachment point (deductible) for the risk transfer mechanism.
risk_transf_cover : float, optional
The cover limit for the risk transfer mechanism.
calc_residual : bool, default=False
If True, returns the residual impact. If False, returns the transferred impact.

Returns
-------
Impact
The final impact object (either residual or transferred).
"""
impact = self.compute_impacts_pre_transfer(exp, haz, vul)
self._apply_risk_transfer(
impact, risk_transf_attach, risk_transf_cover, calc_residual
)
return impact

def compute_impacts_pre_transfer(
self,
exp: Exposures,
haz: Hazard,
vul: ImpactFuncSet,
) -> Impact:
"""
Calculates the raw impact matrix before any risk transfer is applied.

Parameters
----------
exp : Exposures
The exposure data.
haz : Hazard
The hazard data.
vul : ImpactFuncSet
The set of vulnerability functions.

Returns
-------
Impact
An Impact object containing the raw, pre-transfer impact matrix.
"""
return ImpactCalc(exposures=exp, impfset=vul, hazard=haz).impact()

def _apply_risk_transfer(
self,
impact: Impact,
risk_transf_attach: Optional[float],
risk_transf_cover: Optional[float],
calc_residual: bool,
) -> None:
"""
Applies risk transfer logic and modifies the Impact object in-place.

Parameters
----------
impact : Impact
The Impact object whose impact matrix will be modified.
risk_transf_attach : float, optional
The attachment point.
risk_transf_cover : float, optional
The cover limit.
calc_residual : bool
Determines whether to set the matrix to the residual or transferred impact.
"""
if risk_transf_attach is not None or risk_transf_cover is not None:
# Assuming impact.imp_mat is a sparse matrix, which is a common pattern
impact.imp_mat = self.calculate_residual_or_risk_transfer_impact_matrix(
impact.imp_mat, risk_transf_attach, risk_transf_cover, calc_residual
)

def calculate_residual_or_risk_transfer_impact_matrix(

Check warning on line 190 in climada/trajectories/impact_calc_strat.py

View check run for this annotation

Jenkins - WCR / Pylint

invalid-name

LOW: Method name "calculate_residual_or_risk_transfer_impact_matrix" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
Raw output
Used when the name doesn't match the regular expression associated to its type(constant, variable, class...).
self,
imp_mat: Union[
sparse.csr_matrix, Any
], # Use Any if sparse.csr_matrix is too restrictive
attachement: Optional[float],
cover: Optional[float],
calc_residual: bool,
) -> Union[sparse.csr_matrix, Any]:
"""
Calculates either the residual or the risk transfer impact matrix
based on a global risk transfer mechanism.

This function modifies the original impact matrix values proportionally
based on the total event impact relative to the attachment and cover.

Parameters
----------
imp_mat : scipy.sparse.csr_matrix or object with .data
The original impact matrix (events x exposure points).
attachement : float, optional
The attachment point (deductible).
cover : float, optional
The cover limit.
calc_residual : bool
If True, the function returns the residual impact matrix.
If False, it returns the transferred risk impact matrix.

Returns
-------
scipy.sparse.csr_matrix or object with .data
The adjusted impact matrix (residual or transferred).

Notes
-----
The calculation is performed event-wise:

1. **Total Impact**: Calculate the total impact for each event
(sum of impacts across all exposure points).
2. **Transferred Risk per Event**: Defined as:
$$\min(\max(0, \text{Total Impact} - \text{attachement}), \text{cover})$$

Check warning on line 230 in climada/trajectories/impact_calc_strat.py

View check run for this annotation

Jenkins - WCR / Pylint

anomalous-backslash-in-string

NORMAL: Anomalous backslash in string: '\m'. String constant might be missing an r prefix.
Raw output
Used when a backslash is in a literal string but not as an escape.
3. **Residual Risk per Event**:
$$\text{Total Impact} - \text{Transferred Risk per Event}$$
4. **Adjustment**: The original impact per exposure point is scaled
by the ratio of (Residual Risk / Total Impact) or
(Transferred Risk / Total Impact) for that event.
This ensures the risk transfer is shared proportionally among all
impacted exposure points.
"""
imp_mat = copy.deepcopy(imp_mat)
# Calculate the total impact per event
total_at_event = imp_mat.sum(axis=1).A1
# Risk layer at event
attachement = 0 if attachement is None else attachement
cover = np.inf if cover is None else cover
transfer_at_event = np.minimum(
np.maximum(total_at_event - attachement, 0), cover
)
residual_at_event = np.maximum(total_at_event - transfer_at_event, 0)

# Calculate either the residual or transfer impact matrix
# Choose the denominator to rescale the impact values
if calc_residual:
numerator = residual_at_event
else:
numerator = transfer_at_event

rescale_impact_values = np.divide(
numerator,
total_at_event,
out=np.zeros_like(numerator, dtype=float),
where=total_at_event != 0,
)

# The multiplication is broadcasted across the columns for each row
result_matrix = imp_mat.multiply(rescale_impact_values[:, np.newaxis])

return result_matrix
Loading
Loading