Skip to content

Commit 59f8a78

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feature/interpolated-trajectories
2 parents 39d557c + 5ae3f7f commit 59f8a78

38 files changed

Lines changed: 761 additions & 1096 deletions

.gitignore

Lines changed: 5 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,11 @@ coverage.xml
5151
.hypothesis/
5252

5353
# Temporary data generated by unit tests
54-
climada/engine/test/data/supplychain/*
55-
climada/engine/test/data/test.csv
56-
climada/engine/test/data/test_imp_mat.npz
57-
climada/engine/test/data/test.xlsx
58-
climada/entity/impact_funcs/test/data/test_write.xlsx
59-
climada/hazard/test/data/tc_tracks_nc/*
60-
climada/hazard/test/data/tc_tracks.h5
61-
climada/hazard/test/data/test_centr.h5
62-
climada/hazard/test/data/test_haz.h5
63-
climada/hazard/test/data/test_unsupported.h5
64-
climada/test/data/1988234N13299.nc
65-
climada/test/data/exposure_buildings_default_47_7.h5
66-
climada/test/data/exposure_high_47_8.h5
67-
climada/test/data/test_write_hazard.tif
68-
climada/util/test/data/save_test.pkl
69-
climada/util/test/data/test_write_raster.tif
54+
climada/engine/test/data/
55+
climada/entity/impact_funcs/test/data/
56+
climada/hazard/test/data/
57+
climada/test/data/
58+
climada/util/test/data/
7059

7160
# Translations
7261
*.mo
@@ -128,51 +117,6 @@ venv.bak/
128117
# mac finder files
129118
.DS_Store
130119

131-
# climada system data files
132-
# they get downloaded separately from the repo
133-
data/system/global_coast*
134-
data/system/global_country*
135-
data/system/tmp_elevation.tif
136-
137-
# climada system data files: Nightlight and population data
138-
data/system/BlackMarble*tif
139-
data/system/*stable_lights*.*
140-
data/system/gpw_v4_population_count_rev11_2015_30_sec.tif
141-
142-
# climada system data files: IBTrACS data
143-
data/system/IBTrACS.ALL.v04r00.nc
144-
data/system/data_master_sl_short/
145-
146-
# climada system data files: SPEI data
147-
data/system/spei06.nc
148-
149-
# climada system data files: World Bank data
150-
data/system/OGHIST.xls
151-
data/system/Wealth-Accounts_CSV/
152-
153-
# climada system data files: Credit Suisse Research institute data:
154-
data/system/GDP2Asset_factors_CRI_2016
155-
156-
# climada system data files: SPAM data
157-
data/system/cell5m_allockey_xy.csv
158-
data/system/LicenseV*.pdf
159-
data/system/readme_global_v3r2.txt
160-
data/system/spam*csv
161-
162-
# climada system data files: GPW data
163-
data/system/gpw-v*
164-
data/system/gpw_v*.tif
165-
166-
# climada system data files: NASA distance to coast
167-
data/system/GMT_intermediate_coast*
168-
169-
# climada system data files: folders for hazard and exposure data
170-
data/system/hazard/
171-
data/system/litpop/
172-
data/system/litpop_2014/
173-
174-
# climada data: ISIMIP crop data folder:
175-
data/ISIMIP_crop/
176120

177121
# climada data results folder:
178122
results/
@@ -183,5 +127,3 @@ results/
183127
!.pylintrc
184128
!.readthedocs.yml
185129
!.github
186-
climada_petals/
187-
climada_python/

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@ Code freeze date: YYYY-MM-DD
1313
### Added
1414

1515
- Better type hints and overloads signatures for ImpactFuncSet [#1250](https://github.com/CLIMADA-project/climada_python/pull/1250)
16+
- Add inter- and extrapolation options to `ImpactFreqCurve` with method `interpolate` [#1252](https://github.com/CLIMADA-project/climada_python/pull/1252)
1617

1718
### Changed
1819
- Updated Impact Calculation Tutorial (`doc.climada_engine_Impact.ipynb`) [#1095](https://github.com/CLIMADA-project/climada_python/pull/1095).
20+
- Makes current `measure` module a legacy module, moving it to `_legacy_measure`, to retain compatibility with `CostBenefit` class and various tests. [#1274](https://github.com/CLIMADA-project/climada_python/pull/1274)
1921

2022
### Fixed
2123

2224
- Fixed asset count in impact logging message [#1195](https://github.com/CLIMADA-project/climada_python/pull/1195).
25+
- `Hazard.from_raster_xarray` now returns a sparse matrix instead of a sparse array [#1261](https://github.com/CLIMADA-project/climada_python/pull/1261).
26+
- Fix TCTracks.from_FAST duplicate loading from year loop [#1269](github.com/CLIMADA-project/climada_python/pull/1269)
2327

2428
### Deprecated
29+
- `Impact.calc_freq_curve()` should not be given the parameter `return_per`. Use the parameter `return_periods` in `Impact.calc_freq_curve().interpolate()` instead.
2530

2631
### Removed
2732
- `climada.util.earth_engine.py` Google Earth Engine methods did not facilitate direct use of GEE data in CLIMADA. Code was relocated to [climada-snippets](https://github.com/CLIMADA-project/climada-snippets). [#1109](https://github.com/CLIMADA-project/climada_python/pull/1109)

climada/engine/impact.py

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -576,15 +576,6 @@ def local_exceedance_impact(
576576
self.frequency_unit
577577
)
578578

579-
# check method
580-
if method not in [
581-
"interpolate",
582-
"extrapolate",
583-
"extrapolate_constant",
584-
"stepfunction",
585-
]:
586-
raise ValueError(f"Unknown method: {method}")
587-
588579
# calculate local exceedance impact
589580
test_frequency = 1 / np.array(return_periods)
590581

@@ -732,15 +723,6 @@ def local_return_period(
732723
self.frequency_unit
733724
)
734725

735-
# check method
736-
if method not in [
737-
"interpolate",
738-
"extrapolate",
739-
"extrapolate_constant",
740-
"stepfunction",
741-
]:
742-
raise ValueError(f"Unknown method: {method}")
743-
744726
return_periods = np.full((self.imp_mat.shape[1], len(threshold_impact)), np.nan)
745727

746728
nonzero_centroids = np.where(self.imp_mat.getnnz(axis=0) > 0)[0]
@@ -804,6 +786,12 @@ def calc_freq_curve(self, return_per=None):
804786
ifc_impact = self.at_event[sort_idxs][::-1]
805787

806788
if return_per is not None:
789+
warnings.warn(
790+
"Calculating the frequency curve on user-specified return periods is deprecated. "
791+
"Use ImpactFreqCurve.calc_freq_curve().interpolate() instead.",
792+
DeprecationWarning,
793+
stacklevel=2,
794+
)
807795
interp_imp = np.interp(return_per, ifc_return_per, ifc_impact)
808796
ifc_return_per = return_per
809797
ifc_impact = interp_imp
@@ -2299,15 +2287,108 @@ def plot(self, axis=None, log_frequency=False, **kwargs):
22992287
-------
23002288
matplotlib.axes.Axes
23012289
"""
2290+
# check frequency unit
2291+
return_period_unit = u_dt.convert_frequency_unit_to_time_unit(
2292+
self.frequency_unit
2293+
)
2294+
23022295
if not axis:
23032296
_, axis = plt.subplots(1, 1)
23042297
axis.set_title(self.label)
23052298
axis.set_ylabel("Impact (" + self.unit + ")")
2299+
23062300
if log_frequency:
23072301
axis.set_xlabel(f"Exceedance frequency ({self.frequency_unit})")
23082302
axis.set_xscale("log")
23092303
axis.plot(self.return_per**-1, self.impact, **kwargs)
2304+
23102305
else:
2311-
axis.set_xlabel("Return period (year)")
2306+
axis.set_xlabel(f"Return period ({return_period_unit})")
23122307
axis.plot(self.return_per, self.impact, **kwargs)
2308+
23132309
return axis
2310+
2311+
def interpolate(
2312+
self,
2313+
return_periods,
2314+
*,
2315+
method="interpolate",
2316+
log_frequency=True,
2317+
log_impact=True,
2318+
min_impact=0,
2319+
bin_decimals=None,
2320+
y_asymptotic=0.0,
2321+
):
2322+
"""Interpolate and extrapolate impact frequency curve using different methods.
2323+
2324+
Parameters
2325+
----------
2326+
return_periods : Iterable[float]
2327+
return periods for which to evaluate the impact frequency curve
2328+
2329+
method : str, optional
2330+
Method to interpolate to new return periods. Currently available are "interpolate",
2331+
"extrapolate", "extrapolate_constant" and "stepfunction". If set to "interpolate",
2332+
return periods outside the range of the Impact object's observed return periods
2333+
will be assigned NaN. If set to "extrapolate_constant" or "stepfunction",
2334+
return periods larger than the Impact object's observed return periods will be
2335+
assigned the largest impact, and return periods smaller than the Impact object's
2336+
observed return periods will be assigned 0. If set to "extrapolate",
2337+
exceedance impacts will be extrapolated (and interpolated). The extrapolation to
2338+
large return periods uses the two highest impacts of the centroid and their return
2339+
periods and extends the interpolation between these points to the given return period
2340+
(similar for small return periods). Defauls to "interpolate".
2341+
min_impact : float, optional
2342+
Minimum threshold to filter the impact. Defaults to 0.
2343+
log_frequency : bool, optional
2344+
If set to True, (cummulative) frequency values are converted to log scale before
2345+
inter- and extrapolation. Defaults to True.
2346+
log_impact : bool, optional
2347+
If set to True, impact values are converted to log scale before
2348+
inter- and extrapolation. Defaults to True.
2349+
bin_decimals : int, optional
2350+
Number of decimals to group and bin impact values. Binning results in smoother (and
2351+
coarser) interpolation and more stable extrapolation. For more details and sensible
2352+
values for bin_decimals, see Notes. If None, values are not binned. Defaults to None.
2353+
y_asymptotic : float, optional
2354+
Has no effect if method is "interpolate". Else, if data size < 2 or if method
2355+
is set to "extrapolate_constant" or "stepfunction", it provides return value for
2356+
exceeded impact for return periods smaller than the data range. Defaults to 0.
2357+
2358+
Returns
2359+
-------
2360+
ImpactFreqCurve
2361+
impact frequency curve with inter- and extrapolated values
2362+
2363+
See Also
2364+
--------
2365+
util.interpolation.preprocess_and_interpolate_ev :
2366+
inter- and extrapolation method
2367+
"""
2368+
exceedance_frequency = 1 / np.array(return_periods)
2369+
2370+
# sort return periods of ImpactFreqCurve
2371+
sorted_idxs = np.argsort(self.return_per)
2372+
impacts = np.squeeze(np.array(self.impact)[sorted_idxs])
2373+
rps = np.asarray(self.return_per)[sorted_idxs]
2374+
frequency = np.diff(1 / np.array(rps)[::-1], prepend=0)[::-1]
2375+
impact_interpolated = u_interp.preprocess_and_interpolate_ev(
2376+
exceedance_frequency,
2377+
None,
2378+
frequency,
2379+
impacts,
2380+
log_frequency=log_frequency,
2381+
log_values=log_impact,
2382+
value_threshold=min_impact,
2383+
method=method,
2384+
y_asymptotic=y_asymptotic,
2385+
bin_decimals=bin_decimals,
2386+
)
2387+
2388+
return ImpactFreqCurve(
2389+
return_per=return_periods,
2390+
impact=impact_interpolated,
2391+
unit=self.unit,
2392+
frequency_unit=self.frequency_unit,
2393+
label=self.label,
2394+
)

climada/engine/test/test_cost_benefit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
risk_rp_100,
3434
risk_rp_250,
3535
)
36+
from climada.entity._legacy_measures import Measure
37+
from climada.entity._legacy_measures.base import LOGGER as ILOG
3638
from climada.entity.disc_rates import DiscRates
3739
from climada.entity.entity_def import Entity
38-
from climada.entity.measures import Measure
39-
from climada.entity.measures.base import LOGGER as ILOG
4040
from climada.hazard.base import Hazard
4141
from climada.test import get_test_file
4242
from climada.util.api_client import Client

climada/engine/test/test_impact.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,61 @@ def test_ref_value_rp_pass(self):
321321
self.assertEqual("USD", ifc.unit)
322322
self.assertEqual("1/week", ifc.frequency_unit)
323323

324+
def test_interpolate_freq_curve(self):
325+
"""Test inter- and extrapolate method of freq curve"""
326+
imp = Impact()
327+
imp.frequency = np.array([0.2, 0.1, 0.1, 0.1])
328+
imp.at_event = np.array([0.0, 100.0, 50.0, 110.0])
329+
imp.unit = "USD"
330+
imp.frequency_unit = "1/year"
331+
332+
ifc = imp.calc_freq_curve()
333+
# the ifc has values
334+
# impacts [0, 50, 100, 110]
335+
# exceedance frequencies [.5, .3, .2, .1]
336+
# return periods [2, 3.3, 5, 10]
337+
338+
# stepfunction assigns zero return periods below data and max(impact) for those above
339+
npt.assert_array_almost_equal(
340+
ifc.interpolate([1, 5, 20], method="stepfunction").impact,
341+
[0.0, 100.0, 110.0],
342+
)
343+
344+
# interpolate assigns nan to return periods outside of data
345+
npt.assert_array_almost_equal(
346+
ifc.interpolate([1, 5, 20], method="interpolate").impact,
347+
[np.nan, 100.0, np.nan],
348+
)
349+
350+
# extrapolate_constant assigns zero return periods below data and max(impact) for those above
351+
npt.assert_array_almost_equal(
352+
ifc.interpolate([1, 5, 20], method="extrapolate_constant").impact,
353+
[0.0, 100.0, 110.0],
354+
)
355+
356+
# by binning the last two digits, 100 and 110 are rounded to 100
357+
npt.assert_array_almost_equal(
358+
ifc.interpolate(
359+
[1, 5, 20], method="extrapolate_constant", bin_decimals=-2
360+
).impact,
361+
[0.0, 100.0, 100.0],
362+
)
363+
364+
# extrapolation is done by neglecting 0 impacts (min_impact=0)
365+
# rp=1: extrapolate impacts [50, 100] and ex_freqs [.3, .2] to ex_freq=1 --> 0
366+
# rp=2.5: extrapolate impacts [50, 100] and ex_freqs [.3, .2] to ex_freq=0.4 --> 0
367+
# rp=4: extrapolate impacts [50, 100] and ex_freqs [.3, .2] to ex_freq=0.25 --> 75
368+
# rp=1: extrapolate impacts [100, 110] and ex_freqs [.2, .1] to ex_freq=0.05 --> 115
369+
npt.assert_array_almost_equal(
370+
ifc.interpolate(
371+
[1.0, 2.5, 4, 20],
372+
method="extrapolate",
373+
log_frequency=False,
374+
log_impact=False,
375+
).impact,
376+
[-300.0, 0.0, 75.0, 115.0],
377+
)
378+
324379

325380
class TestImpactPerYear(unittest.TestCase):
326381
"""Test calc_impact_year_set method"""

climada/engine/unsequa/input_var.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ def ent(
518518
exp_list : [climada.entity.exposures.base.Exposure]
519519
The list of base exposure. Can be one or many to uniformly sample
520520
from.
521-
meas_set : climada.entity.measures.measure_set.MeasureSet
521+
meas_set : climada.entity._legacy_measures.measure_set.MeasureSet
522522
The base measures.
523523
haz_id_dict : dict
524524
Dictionary of the impact functions affected by uncertainty.
@@ -660,7 +660,7 @@ def entfut(
660660
exp_list : [climada.entity.exposures.base.Exposure]
661661
The list of base exposure. Can be one or many to uniformly sample
662662
from.
663-
meas_set : climada.entity.measures.measure_set.MeasureSet
663+
meas_set : climada.entity._legacy_measures.measure_set.MeasureSet
664664
The base measures.
665665
haz_id_dict : dict
666666
Dictionary of the impact functions affected by uncertainty.

climada/entity/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
init entity
2020
"""
2121

22+
from ._legacy_measures import *
2223
from .disc_rates import *
2324
from .entity_def import *
2425
from .exposures import *
2526
from .impact_funcs import *
26-
from .measures import *
File renamed without changes.

climada/entity/measures/measure_set.py renamed to climada/entity/_legacy_measures/measure_set.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
from matplotlib import colormaps as cm
3333

3434
import climada.util.hdf5_handler as u_hdf5
35-
from climada.entity.measures.base import Measure
35+
36+
from .base import Measure
3637

3738
LOGGER = logging.getLogger(__name__)
3839

0 commit comments

Comments
 (0)