Skip to content

Commit 99e22de

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feature/docbuild-fixes
2 parents bedb537 + 8895be3 commit 99e22de

37 files changed

Lines changed: 4006 additions & 39 deletions
-335 Bytes
Binary file not shown.

climada/engine/unsequa/test/test_unsequa.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,18 @@
4141
from climada.entity import Exposures, ImpactFunc, ImpactFuncSet
4242
from climada.entity.entity_def import Entity
4343
from climada.hazard import Hazard
44-
from climada.util.api_client import Client
44+
from climada.test import get_test_file
4545
from climada.util.constants import (
4646
ENT_DEMO_FUTURE,
4747
ENT_DEMO_TODAY,
48-
EXP_DEMO_H5,
4948
HAZ_DEMO_H5,
5049
TEST_UNC_OUTPUT_COSTBEN,
5150
TEST_UNC_OUTPUT_IMPACT,
5251
)
5352

54-
test_unc_output_impact = Client().get_dataset_file(
55-
name=TEST_UNC_OUTPUT_IMPACT, status="test_dataset"
56-
)
57-
test_unc_output_costben = Client().get_dataset_file(
58-
name=TEST_UNC_OUTPUT_COSTBEN, status="test_dataset"
59-
)
53+
EXP_DEMO_H5 = get_test_file("exp_demo_today", file_format="hdf5")
54+
test_unc_output_impact = get_test_file(TEST_UNC_OUTPUT_IMPACT)
55+
test_unc_output_costben = get_test_file(TEST_UNC_OUTPUT_COSTBEN)
6056

6157

6258
def impf_dem(x_paa=1, x_mdd=1):
@@ -578,7 +574,7 @@ def test_calc_sensitivity_all_pass(self):
578574
"sensitivity_kwargs": {"S": 10, "seed": 12345},
579575
"test_param_name": ["x_exp", 0],
580576
"test_si_name": ["CV", 16],
581-
"test_si_value": [0.25000, 2],
577+
"test_si_value": [0.250000, 2],
582578
},
583579
"hdmr": {
584580
"sampling_method": "saltelli",
@@ -587,7 +583,7 @@ def test_calc_sensitivity_all_pass(self):
587583
"sensitivity_kwargs": {},
588584
"test_param_name": ["x_exp", 2],
589585
"test_si_name": ["Sa", 4],
590-
"test_si_value": [0.004658, 3],
586+
"test_si_value": [0.004649, 3],
591587
},
592588
"ff": {
593589
"sampling_method": "ff",
@@ -618,7 +614,7 @@ def test_calc_sensitivity_all_pass(self):
618614
},
619615
"test_param_name": ["x_exp", 0],
620616
"test_si_name": ["dgsm", 8],
621-
"test_si_value": [1.697516e-01, 9],
617+
"test_si_value": [0.1697516, 9],
622618
},
623619
"fast": {
624620
"sampling_method": "fast_sampler",
@@ -627,7 +623,7 @@ def test_calc_sensitivity_all_pass(self):
627623
"sensitivity_kwargs": {"M": 4, "seed": 12345},
628624
"test_param_name": ["x_exp", 0],
629625
"test_si_name": ["S1_conf", 8],
630-
"test_si_value": [0.671396, 1],
626+
"test_si_value": [0.671546, 1],
631627
},
632628
"rbd_fast": {
633629
"sampling_method": "saltelli",
@@ -636,7 +632,7 @@ def test_calc_sensitivity_all_pass(self):
636632
"sensitivity_kwargs": {"M": 4, "seed": 12345},
637633
"test_param_name": ["x_exp", 0],
638634
"test_si_name": ["S1_conf", 4],
639-
"test_si_value": [0.152609, 4],
635+
"test_si_value": [0.129919, 4],
640636
},
641637
"morris": {
642638
"sampling_method": "morris",
@@ -645,7 +641,7 @@ def test_calc_sensitivity_all_pass(self):
645641
"sensitivity_kwargs": {},
646642
"test_param_name": ["x_exp", 0],
647643
"test_si_name": ["mu", 1],
648-
"test_si_value": [5066460029.63911, 8],
644+
"test_si_value": [7935400297.813827, 8],
649645
},
650646
}
651647

@@ -700,7 +696,7 @@ def test_sensitivity_method(
700696
haz_unc,
701697
sensitivity_method,
702698
method_params,
703-
places=2 if sensitivity_method == "rbd_fast" else 5,
699+
places=5,
704700
)
705701

706702

climada/entity/exposures/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,9 @@ def __init__(
403403

404404
self.description = self._consolidate(meta, "description", description)
405405
self.ref_year = self._consolidate(meta, "ref_year", ref_year, DEF_REF_YEAR)
406+
407+
if geodata.shape[0] > 0:
408+
value_unit = self._consolidate(geodata.iloc[0], "value_unit", value_unit)
406409
self.value_unit = self._consolidate(
407410
meta, "value_unit", value_unit, DEF_VALUE_UNIT
408411
)

climada/entity/exposures/test/test_base.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def test__init__mda_in_kwargs(self):
182182
def test_read_raster_pass(self):
183183
"""from_raster"""
184184
exp = Exposures.from_raster(
185-
HAZ_DEMO_FL, window=Window(10, 20, 50, 60), attrs={"value_unit": "USD"}
185+
HAZ_DEMO_FL, window=Window(10, 20, 50, 60), attrs={"value_unit": "PKR"}
186186
)
187187
exp.check()
188188
self.assertTrue(u_coord.equal_crs(exp.crs, DEF_CRS))
@@ -204,7 +204,7 @@ def test_read_raster_pass(self):
204204
self.assertAlmostEqual(
205205
exp.gdf["value"].values.reshape((60, 50))[25, 12], 0.056825936
206206
)
207-
self.assertEqual(exp.value_unit, "USD")
207+
self.assertEqual(exp.value_unit, "PKR")
208208

209209
def test_assign_raster_pass(self):
210210
"""Test assign_centroids with raster hazard"""
@@ -429,16 +429,23 @@ def test_read_template_pass(self):
429429
exp_df = Exposures(df)
430430
# set metadata
431431
exp_df.ref_year = 2020
432-
exp_df.value_unit = "XSD"
432+
exp_df.value_unit = "PAK"
433433
exp_df.check()
434434

435+
def test_handling_unit_conflicts_pass(self):
436+
"""Check that the value_unit is correctly set when there are conflicting value_unit definitions in the data frame and the meta attribute."""
437+
df = pd.read_excel(ENT_TEMPLATE_XLS)
438+
exp_df = Exposures(df, meta={"value_unit": "XSD"}, value_unit="XSD")
439+
exp_df.check()
440+
self.assertEqual(exp_df.value_unit, "XSD")
441+
with self.assertRaises(ValueError) as cm:
442+
exp_df = Exposures(df, meta={"value_unit": "XSD"}, value_unit="PAK")
443+
435444
def test_io_hdf5_pass(self):
436445
"""write and read hdf5"""
437-
exp = Exposures(pd.read_excel(ENT_TEMPLATE_XLS), crs="epsg:32632")
438-
439-
# set metadata
440-
exp.ref_year = 2020
441-
exp.value_unit = "XSD"
446+
exp = Exposures(
447+
pd.read_excel(ENT_TEMPLATE_XLS), crs="epsg:32632", ref_year=2020
448+
)
442449

443450
# add another geometry column
444451
exp.data["geocol2"] = exp.data.geometry.copy(deep=True)

climada/entity/measures/test/test_base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@
3636
from climada.entity.measures.measure_set import MeasureSet
3737
from climada.hazard.base import Hazard
3838
from climada.test import get_test_file
39-
from climada.util.constants import EXP_DEMO_H5, HAZ_DEMO_H5
39+
from climada.util.constants import HAZ_DEMO_H5
4040

4141
DATA_DIR = CONFIG.measures.test_data.dir()
4242

43+
EXP_DEMO_H5 = get_test_file("exp_demo_today", file_format="hdf5")
44+
4345
HAZ_TEST_TC: Path = get_test_file("test_tc_florida", file_format="hdf5")
4446
"""
4547
Hazard test file from Data API: Hurricanes from 1851 to 2011 over Florida with 100 centroids.

climada/hazard/tc_tracks_synth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ def _apply_decay_coeffs(track, v_rel, p_rel, land_geom, s_rel):
10491049
# if there is no further landfall, correct until the end of
10501050
# the track
10511051
end_cor = track["time"].size
1052-
rndn = 0.1 * float(np.abs(np.random.normal(size=1) * 5) + 6)
1052+
rndn = 0.1 * float(np.abs(np.random.default_rng().normal() * 5) + 6)
10531053
r_diff = (
10541054
track["central_pressure"][land_sea].values
10551055
- track["central_pressure"][land_sea - 1].values
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
"""
2+
This file is part of CLIMADA.
3+
4+
Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.
5+
6+
CLIMADA is free software: you can redistribute it and/or modify it under the
7+
terms of the GNU General Public License as published by the Free
8+
Software Foundation, version 3.
9+
10+
CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
11+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12+
PARTICULAR PURPOSE. See the GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License along
15+
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.
16+
---
17+
18+
A set of reusable objects for testing purpose.
19+
20+
The objective of this file is to provide minimalistic, understandable and consistent
21+
default objects for unit and integration testing.
22+
23+
"""
24+
25+
import geopandas as gpd
26+
import numpy as np
27+
from scipy.sparse import csr_matrix
28+
from shapely.geometry import Point
29+
30+
from climada.entity import Exposures, ImpactFunc, ImpactFuncSet
31+
from climada.hazard import Centroids, Hazard
32+
from climada.trajectories.snapshot import Snapshot
33+
34+
# ---------------------------------------------------------------------------
35+
# Coordinate system and metadata
36+
# ---------------------------------------------------------------------------
37+
CRS_WGS84 = "EPSG:4326"
38+
39+
# ---------------------------------------------------------------------------
40+
# Exposure attributes
41+
# ---------------------------------------------------------------------------
42+
EXP_DESC = "Test exposure dataset"
43+
EXP_DESC_LATLON = "Test exposure dataset (lat/lon)"
44+
EXPOSURE_REF_YEAR = 2020
45+
EXPOSURE_VALUE_UNIT = "USD"
46+
VALUES = np.array([0, 1000, 2000, 3000])
47+
REGIONS = np.array(["A", "A", "B", "B"])
48+
CATEGORIES = np.array([1, 1, 2, 1])
49+
50+
# Exposure coordinates
51+
EXP_LONS = np.array([4, 4.5, 4, 4.5])
52+
EXP_LATS = np.array([45, 45, 45.5, 45.5])
53+
54+
# ---------------------------------------------------------------------------
55+
# Hazard definition
56+
# ---------------------------------------------------------------------------
57+
HAZARD_TYPE = "TEST_HAZARD_TYPE"
58+
HAZARD_UNIT = "TEST_HAZARD_UNIT"
59+
60+
# Hazard centroid positions
61+
HAZ_JITTER = 0.1 # To test centroid matching
62+
HAZ_LONS = EXP_LONS + HAZ_JITTER
63+
HAZ_LATS = EXP_LATS + HAZ_JITTER
64+
65+
# Hazard events
66+
EVENT_IDS = np.array([1, 2, 3, 4])
67+
EVENT_NAMES = ["ev1", "ev2", "ev3", "ev4"]
68+
DATES = np.array([1, 2, 3, 4])
69+
70+
# Frequency are choosen so that they cumulate nicely
71+
# to correspond to 100, 50, and 20y return periods (for impacts)
72+
FREQUENCY = np.array([0.1, 0.03, 0.01, 0.01])
73+
FREQUENCY_UNIT = "1/year"
74+
75+
# Hazard maximum intensity
76+
# 100 to match 0 to 100% idea
77+
# also in line with linear 1:1 impact function
78+
# for easy mental calculus
79+
HAZARD_MAX_INTENSITY = 100
80+
81+
# ---------------------------------------------------------------------------
82+
# Impact function
83+
# ---------------------------------------------------------------------------
84+
IMPF_ID = 1
85+
IMPF_NAME = "IMPF_1"
86+
87+
# ---------------------------------------------------------------------------
88+
# Future years
89+
# ---------------------------------------------------------------------------
90+
EXPOSURE_FUTURE_YEAR = 2040
91+
92+
93+
def reusable_minimal_exposures(
94+
values=VALUES,
95+
regions=REGIONS,
96+
group_id=None,
97+
lon=EXP_LONS,
98+
lat=EXP_LATS,
99+
crs=CRS_WGS84,
100+
desc=EXP_DESC,
101+
ref_year=EXPOSURE_REF_YEAR,
102+
value_unit=EXPOSURE_VALUE_UNIT,
103+
assign_impf=IMPF_ID,
104+
increase_value_factor=1,
105+
) -> Exposures:
106+
data = gpd.GeoDataFrame(
107+
{
108+
"value": values * increase_value_factor,
109+
"region_id": regions,
110+
f"impf_{HAZARD_TYPE}": assign_impf,
111+
"geometry": [Point(lon, lat) for lon, lat in zip(lon, lat)],
112+
},
113+
crs=crs,
114+
)
115+
if group_id is not None:
116+
data["group_id"] = group_id
117+
return Exposures(
118+
data=data,
119+
description=desc,
120+
ref_year=ref_year,
121+
value_unit=value_unit,
122+
)
123+
124+
125+
def reusable_intensity_mat(max_intensity=HAZARD_MAX_INTENSITY):
126+
# Choosen such that:
127+
# - 1st event has 0 intensity
128+
# - 2nd event has max intensity in first exposure point (defaulting to 0 value)
129+
# - 3rd event has 1/2* of max intensity in second centroid
130+
# - 4th event has 1/4* of max intensity everywhere
131+
# *: So that you can double intensity of the hazard and expect double impacts
132+
return csr_matrix(
133+
[
134+
[0, 0, 0, 0],
135+
[max_intensity, 0, 0, 0],
136+
[0, max_intensity / 2, 0, 0],
137+
[
138+
max_intensity / 4,
139+
max_intensity / 4,
140+
max_intensity / 4,
141+
max_intensity / 4,
142+
],
143+
]
144+
)
145+
146+
147+
def reusable_minimal_hazard(
148+
haz_type=HAZARD_TYPE,
149+
units=HAZARD_UNIT,
150+
lat=HAZ_LATS,
151+
lon=HAZ_LONS,
152+
crs=CRS_WGS84,
153+
event_id=EVENT_IDS,
154+
event_name=EVENT_NAMES,
155+
date=DATES,
156+
frequency=FREQUENCY,
157+
frequency_unit=FREQUENCY_UNIT,
158+
intensity=None,
159+
intensity_factor=1,
160+
) -> Hazard:
161+
intensity = reusable_intensity_mat() if intensity is None else intensity
162+
intensity *= intensity_factor
163+
return Hazard(
164+
haz_type=haz_type,
165+
units=units,
166+
centroids=Centroids(lat=lat, lon=lon, crs=crs),
167+
event_id=event_id,
168+
event_name=event_name,
169+
date=date,
170+
frequency=frequency,
171+
frequency_unit=frequency_unit,
172+
intensity=intensity,
173+
)
174+
175+
176+
def reusable_minimal_impfset(
177+
hazard=None, name=IMPF_NAME, impf_id=IMPF_ID, max_intensity=HAZARD_MAX_INTENSITY
178+
):
179+
hazard = reusable_minimal_hazard() if hazard is None else hazard
180+
return ImpactFuncSet(
181+
[
182+
ImpactFunc(
183+
haz_type=hazard.haz_type,
184+
intensity_unit=hazard.units,
185+
name=name,
186+
intensity=np.array([0, max_intensity / 2, max_intensity]),
187+
mdd=np.array([0, 0.5, 1]),
188+
paa=np.array([1, 1, 1]),
189+
id=impf_id,
190+
)
191+
]
192+
)
193+
194+
195+
def reusable_snapshot(
196+
hazard_intensity_increase_factor=1,
197+
exposure_value_increase_factor=1,
198+
date=EXPOSURE_REF_YEAR,
199+
):
200+
exposures = reusable_minimal_exposures(
201+
increase_value_factor=exposure_value_increase_factor
202+
)
203+
hazard = reusable_minimal_hazard(intensity_factor=hazard_intensity_increase_factor)
204+
impfset = reusable_minimal_impfset()
205+
return Snapshot(exposure=exposures, hazard=hazard, impfset=impfset, date=str(date))

0 commit comments

Comments
 (0)