Skip to content

Commit 117ddef

Browse files
committed
Merge branch 'develop' into ruff-lint-format
2 parents e19d0ac + cb38e22 commit 117ddef

65 files changed

Lines changed: 5253 additions & 1133 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,23 @@ Code freeze date: YYYY-MM-DD
1212

1313
### Added
1414

15+
- 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)
17+
1518
### Changed
1619
- Updated Impact Calculation Tutorial (`doc.climada_engine_Impact.ipynb`) [#1095](https://github.com/CLIMADA-project/climada_python/pull/1095).
1720

1821
### Fixed
1922

23+
- Fixed asset count in impact logging message [#1195](https://github.com/CLIMADA-project/climada_python/pull/1195).
24+
- `Hazard.from_raster_xarray` now returns a sparse matrix instead of a sparse array [#1261](https://github.com/CLIMADA-project/climada_python/pull/1261).
25+
2026
### Deprecated
27+
- `Impact.calc_freq_curve()` should not be given the parameter `return_per`. Use the parameter `return_periods` in `Impact.calc_freq_curve().interpolate()` instead.
2128

2229
### Removed
30+
- `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)
31+
- `doc.climada_util_earth_engine.ipynb` Tutorial about GEE not relevant to CLIMADA Core. Tutorial notebook was relocated to [climada-snippets](https://github.com/CLIMADA-project/climada-snippets). [#1109](https://github.com/CLIMADA-project/climada_python/pull/1109)
2332

2433
## 6.1.0
2534

@@ -152,9 +161,9 @@ Removed:
152161
- `climada.entity.impact_funcs.trop_cyclone.ImpfSetTropCyclone.get_impf_id_regions_per_countries` function [#1034](https://github.com/CLIMADA-project/climada_python/pull/1034)
153162
- `climada.hazard.tc_tracks.BasinBoundsStorm` Enum class `climada.hazard.tc_tracks.subset_by_basin` function [#1031](https://github.com/CLIMADA-project/climada_python/pull/1031)
154163
- `climada.hazard.tc_tracks.TCTracks.subset_years` function [#1023](https://github.com/CLIMADA-project/climada_python/pull/1023)
155-
-`climada.hazard.tc_tracks.compute_track_density` function, `climada.hazard.tc_tracks.compute_genesis_density` function, `climada.hazard.plot.plot_track_density` function
164+
- `climada.hazard.tc_tracks.compute_track_density` function, `climada.hazard.tc_tracks.compute_genesis_density` function, `climada.hazard.plot.plot_track_density` function
156165
[#1003](https://github.com/CLIMADA-project/climada_python/pull/1003)
157-
-`climada.hazard.tc_tracks.TCTracks.from_FAST` function, add Australia basin (AU) [#993](https://github.com/CLIMADA-project/climada_python/pull/993)
166+
- `climada.hazard.tc_tracks.TCTracks.from_FAST` function, add Australia basin (AU) [#993](https://github.com/CLIMADA-project/climada_python/pull/993)
158167
- Add `osm-flex` package to CLIMADA core [#981](https://github.com/CLIMADA-project/climada_python/pull/981)
159168
- `doc.tutorial.climada_entity_Exposures_osm.ipynb` tutorial explaining how to use `osm-flex` with CLIMADA
160169
- `climada.util.coordinates.bounding_box_global` function [#980](https://github.com/CLIMADA-project/climada_python/pull/980)
-335 Bytes
Binary file not shown.

climada/engine/impact.py

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

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

@@ -730,15 +721,6 @@ def local_return_period(
730721
self.frequency_unit
731722
)
732723

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

744726
nonzero_centroids = np.where(self.imp_mat.getnnz(axis=0) > 0)[0]
@@ -802,6 +784,12 @@ def calc_freq_curve(self, return_per=None):
802784
ifc_impact = self.at_event[sort_idxs][::-1]
803785

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

climada/engine/impact_calc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def impact(
186186
return self._return_empty(save_mat)
187187
LOGGER.info(
188188
"Calculating impact for %s assets (>0) and %s events.",
189-
exp_gdf.size,
189+
len(exp_gdf),
190190
self.n_events,
191191
)
192192
imp_mat_gen = self.imp_mat_gen(exp_gdf, impf_col)

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/test/test_impact_calc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ def test_calc_insured_impact_no_insurance(self):
440440
self.assertEqual(
441441
logs.output,
442442
[
443-
"INFO:climada.engine.impact_calc:Calculating impact for 150 assets (>0) and 14450 events."
443+
"INFO:climada.engine.impact_calc:Calculating impact for 50 assets (>0) and 14450 events."
444444
],
445445
)
446446
self.assertEqual(icalc.n_events, len(impact.at_event))

0 commit comments

Comments
 (0)