Skip to content

Commit 852c54d

Browse files
committed
Merge remote-tracking branch 'origin/feature/fix_knutson_scaling' into feature/tc-synth-new-model-euler
2 parents 48c2682 + 9b32e93 commit 852c54d

File tree

12 files changed

+5733
-589
lines changed

12 files changed

+5733
-589
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ Release date: 2024-07-19
66

77
### Dependency Changes
88

9+
### Added
10+
11+
### Changed
12+
13+
### Fixed
14+
15+
### Deprecated
16+
17+
### Removed
18+
19+
## 5.0.0
20+
21+
Release date: 2024-07-19
22+
23+
### Dependency Changes
24+
925
Added:
1026

1127
- `bayesian-optimization`

climada/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '5.0.0'
1+
__version__ = '5.0.1-dev'

climada/hazard/tc_clim_change.py

Lines changed: 359 additions & 144 deletions
Large diffs are not rendered by default.

climada/hazard/tc_tracks.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,11 @@ def from_ibtracs_netcdf(cls, provider=None, rescale_windspeeds=True, storm_id=No
334334
335335
When using data from IBTrACS, make sure to be familiar with the scope and limitations of
336336
IBTrACS, e.g. by reading the official documentation
337-
(https://www.ncdc.noaa.gov/ibtracs/pdf/IBTrACS_version4_Technical_Details.pdf). Reading the
338-
CLIMADA documentation can't replace a thorough understanding of the underlying data. This
339-
function only provides a (hopefully useful) interface for the data input, but cannot
340-
provide any guidance or make recommendations about if and how to use IBTrACS data for your
341-
particular project.
337+
(https://www.ncei.noaa.gov/sites/default/files/2021-07/IBTrACS_version4_Technical_Details.pdf).
338+
Reading the CLIMADA documentation can't replace a thorough understanding of the underlying
339+
data. This function only provides a (hopefully useful) interface for the data input, but
340+
cannot provide any guidance or make recommendations about if and how to use IBTrACS data
341+
for your particular project.
342342
343343
Resulting tracks are required to have both pressure and wind speed information at all time
344344
steps. Therefore, all track positions where one of wind speed or pressure are missing are
@@ -377,8 +377,8 @@ def from_ibtracs_netcdf(cls, provider=None, rescale_windspeeds=True, storm_id=No
377377
rescale_windspeeds : bool, optional
378378
If True, all wind speeds are linearly rescaled to 1-minute sustained winds.
379379
Note however that the IBTrACS documentation (Section 5.2,
380-
https://www.ncdc.noaa.gov/ibtracs/pdf/IBTrACS_version4_Technical_Details.pdf) includes
381-
a warning about this kind of conversion: "While a multiplicative factor can describe
380+
https://www.ncei.noaa.gov/sites/default/files/2021-07/IBTrACS_version4_Technical_Details.pdf)
381+
includes a warning about this kind of conversion: "While a multiplicative factor can
382382
the numerical differences, there are procedural and observational differences between
383383
agencies that can change through time, which confounds the simple multiplicative
384384
factor." Default: True

climada/hazard/test/test_tc_cc.py

Lines changed: 116 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,45 +21,127 @@
2121

2222
import unittest
2323

24+
import unittest
25+
from math import log
26+
import pandas as pd
27+
import numpy as np
2428
import climada.hazard.tc_clim_change as tc_cc
2529

2630
class TestKnutson(unittest.TestCase):
27-
"""Test loading funcions from the TropCyclone class"""
2831

29-
def test_get_pass(self):
32+
def test_get_knutson_scaling_calculations(self):
33+
34+
basin = 'NA'
35+
variable = 'cat05'
36+
percentile = '5/10'
37+
base_start, base_end = 1950, 2018
38+
yearly_steps = 5
39+
40+
predicted_changes = tc_cc.get_knutson_scaling_factor(
41+
percentile=percentile,
42+
variable=variable,
43+
basin=basin,
44+
baseline=(base_start, base_end),
45+
yearly_steps=yearly_steps
46+
)
47+
48+
## Test computations of future changes
49+
# Load data
50+
gmst_info = tc_cc.get_gmst_info()
51+
52+
var_id, basin_id, perc_id = (tc_cc.MAP_VARS_NAMES[variable],
53+
tc_cc.MAP_BASINS_NAMES[basin],
54+
tc_cc.MAP_PERC_NAMES[percentile])
55+
56+
knutson_data = tc_cc.get_knutson_data()
57+
knutson_value = knutson_data[var_id, basin_id, perc_id]
58+
59+
start_ind = base_start - gmst_info['gmst_start_year']
60+
end_ind = base_end - gmst_info['gmst_start_year']
61+
62+
# Apply model
63+
beta = 0.5 * log(0.01 * knutson_value + 1)
64+
tc_properties = np.exp(beta * gmst_info['gmst_data'])
65+
66+
# Assess baseline value
67+
baseline = np.mean(tc_properties[:, start_ind:end_ind + 1], 1)
68+
69+
# Assess future value and test predicted change from baseline is
70+
# the same as given by function
71+
smoothing = 5
72+
73+
for target_year in [2030, 2050, 2070, 2090]:
74+
target_year_ind = target_year - gmst_info['gmst_start_year']
75+
ind1 = target_year_ind - smoothing
76+
ind2 = target_year_ind + smoothing + 1
77+
78+
prediction = np.mean(tc_properties[:, ind1:ind2], 1)
79+
predicted_change = ((prediction - baseline) / baseline) * 100
80+
81+
np.testing.assert_array_almost_equal(predicted_changes.loc[target_year, '2.6'], predicted_change[0])
82+
np.testing.assert_array_almost_equal(predicted_changes.loc[target_year, '4.5'], predicted_change[1])
83+
np.testing.assert_array_almost_equal(predicted_changes.loc[target_year, '6.0'], predicted_change[2])
84+
np.testing.assert_array_almost_equal(predicted_changes.loc[target_year, '8.5'], predicted_change[3])
85+
86+
def test_get_knutson_scaling_structure(self):
3087
"""Test get_knutson_criterion function."""
31-
criterion = tc_cc.get_knutson_criterion()
32-
self.assertTrue(len(criterion), 20)
33-
for crit_val in criterion:
34-
self.assertTrue('year' in crit_val)
35-
self.assertTrue('change' in crit_val)
36-
self.assertTrue('variable' in crit_val)
37-
self.assertEqual(criterion[0]['variable'], "frequency")
38-
self.assertEqual(criterion[0]['change'], 1)
39-
self.assertEqual(criterion[4]['variable'], "intensity")
40-
self.assertEqual(criterion[4]['change'], 1.045)
41-
self.assertEqual(criterion[-10]['basin'], "SP")
42-
self.assertEqual(criterion[-10]['variable'], "frequency")
43-
self.assertEqual(criterion[-10]['change'], 1 - 0.583)
44-
45-
def test_scale_pass(self):
46-
"""Test calc_scale_knutson function."""
47-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2050, rcp_scenario=45),
48-
0.759630751756698)
49-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2070, rcp_scenario=45),
50-
0.958978483788876)
51-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2060, rcp_scenario=60),
52-
0.825572149523299)
53-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2080, rcp_scenario=60),
54-
1.309882943406079)
55-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2090, rcp_scenario=85),
56-
2.635069196605717)
57-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2100, rcp_scenario=85),
58-
2.940055236533517)
59-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2066, rcp_scenario=26),
60-
0.341930203294547)
61-
self.assertAlmostEqual(tc_cc.calc_scale_knutson(ref_year=2078, rcp_scenario=26),
62-
0.312383928930456)
88+
89+
yearly_steps = 8
90+
predicted_changes = tc_cc.get_knutson_scaling_factor(yearly_steps=yearly_steps)
91+
92+
np.testing.assert_equal(predicted_changes.columns, np.array(['2.6', '4.5', '6.0', '8.5']))
93+
94+
simulated_years = np.arange(tc_cc.YEAR_WINDOWS_PROPS['start'],
95+
tc_cc.YEAR_WINDOWS_PROPS['end']+1,
96+
yearly_steps)
97+
np.testing.assert_equal(predicted_changes.index, simulated_years)
98+
99+
def test_get_knutson_scaling_valid_inputs(self):
100+
df = tc_cc.get_knutson_scaling_factor()
101+
self.assertIsInstance(df, pd.DataFrame)
102+
np.testing.assert_equal(df.shape, (21, 4))
103+
104+
def test_get_knutson_scaling_invalid_baseline_start_year(self):
105+
with self.assertRaises(ValueError):
106+
tc_cc.get_knutson_scaling_factor(baseline=(1870, 2022))
107+
108+
def test_get_knutson_scaling_invalid_baseline_end_year(self):
109+
with self.assertRaises(ValueError):
110+
tc_cc.get_knutson_scaling_factor(baseline=(1982, 2110))
111+
112+
def test_get_knutson_scaling_no_scaling_factors_for_unknonw_basin(self):
113+
df = tc_cc.get_knutson_scaling_factor(basin='ZZZZZ')
114+
self.assertIsInstance(df, pd.DataFrame)
115+
np.testing.assert_equal(df.values, np.ones_like(df.values))
116+
117+
def test_get_gmst(self):
118+
"""Test get_gmst_info function."""
119+
gmst_info = tc_cc.get_gmst_info()
120+
121+
self.assertAlmostEqual(gmst_info['gmst_start_year'], 1880)
122+
self.assertAlmostEqual(gmst_info['gmst_end_year'], 2100)
123+
self.assertAlmostEqual(len(gmst_info['rcps']), 4)
124+
125+
self.assertAlmostEqual(gmst_info['gmst_data'].shape,
126+
(len(gmst_info['rcps']),
127+
gmst_info['gmst_end_year']-gmst_info['gmst_start_year']+1))
128+
self.assertAlmostEqual(gmst_info['gmst_data'][0,0], -0.16)
129+
self.assertAlmostEqual(gmst_info['gmst_data'][0,-1], 1.27641, 4)
130+
self.assertAlmostEqual(gmst_info['gmst_data'][-1,0], -0.16)
131+
self.assertAlmostEqual(gmst_info['gmst_data'][-1,-1], 4.477764, 4)
132+
133+
def test_get_knutson_data_pass(self):
134+
"""Test get_knutson_data function."""
135+
136+
data_knutson = tc_cc.get_knutson_data()
137+
138+
self.assertAlmostEqual(data_knutson.shape, (4,6,5))
139+
self.assertAlmostEqual(data_knutson[0,0,0], -34.49)
140+
self.assertAlmostEqual(data_knutson[-1,-1,-1], 15.419)
141+
self.assertAlmostEqual(data_knutson[0,-1,-1], 4.689)
142+
self.assertAlmostEqual(data_knutson[-1,0,0], 5.848)
143+
self.assertAlmostEqual(data_knutson[-1,0,-1], 22.803)
144+
self.assertAlmostEqual(data_knutson[2,3,2], 4.324)
63145

64146
if __name__ == "__main__":
65147
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestKnutson)

0 commit comments

Comments
 (0)