def test_read_with_basin(self): """Filter TCs by (genesis) basin.""" # South Atlantic (not usually a TC location at all) tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(basin="SA") self.assertEqual(tc_track.size, 3) # the basin is not necessarily the genesis basin tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(year_range=(1995, 1995), basin="SP", estimate_missing=True) self.assertEqual(tc_track.size, 6) self.assertEqual(tc_track.data[0].basin[0], 'SP') self.assertEqual(tc_track.data[5].basin[0], 'SI') # genesis in NI tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf( year_range=(1994, 1994), genesis_basin="NI", estimate_missing=True) self.assertEqual(tc_track.size, 5) for tr in tc_track.data: self.assertEqual(tr.basin[0], "NI") # genesis in EP, but crosses WP at some point tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(year_range=(2002, 2003), basin="WP", genesis_basin="EP") self.assertEqual(tc_track.size, 3) for tr in tc_track.data: self.assertEqual(tr.basin[0], "EP") self.assertIn("WP", tr.basin)
def test_decay_end_ocean(self): """Test decay is applied after landfall if the track ends over the ocean""" tracks_synth_nodecay_example = tc.TCTracks() # this track was generated without applying landfall decay # (i.e. with decay=False in tc_synth.calc_perturbed_trajectories) tracks_synth_nodecay_example.read_netcdf(TEST_TRACK_DECAY_END_OCEAN) # apply landfall decay extent = tracks_synth_nodecay_example.get_extent() land_geom = climada.util.coordinates.get_land_geometry(extent=extent, resolution=10) tracks_synth_nodecay_example.data = tc_synth._apply_land_decay( tracks_synth_nodecay_example.data, tc_synth.LANDFALL_DECAY_V, tc_synth.LANDFALL_DECAY_P, land_geom) track = tracks_synth_nodecay_example.data[0] # read its corresponding historical track track_hist = tc.TCTracks() track_hist.read_netcdf(TEST_TRACK_DECAY_END_OCEAN_HIST) track_hist = track_hist.data[0] # Part 1: is landfall applied after going back to the ocean? # get that last strip over the ocean lf_idx = tc._get_landfall_idx(track) last_lf_idx = lf_idx[-1][1] # only suitable if track ends over the ocean self.assertTrue(last_lf_idx < track.time.size - 2, 'This test should be re-written, data not suitable') # check pressure and wind values p_hist_end = track_hist.central_pressure.values[last_lf_idx:] p_synth_end = track.central_pressure.values[last_lf_idx:] self.assertTrue(np.all(p_synth_end > p_hist_end)) v_hist_end = track_hist.max_sustained_wind.values[last_lf_idx:] v_synth_end = track.max_sustained_wind.values[last_lf_idx:] self.assertTrue(np.all(v_synth_end < v_hist_end)) # Part 2: is landfall applied in all landfalls? p_hist_lf = np.concatenate([ track_hist.central_pressure.values[lfs:lfe] for lfs, lfe in zip(*lf_idx) ]) p_synth_lf = np.concatenate([ track.central_pressure.values[lfs:lfe] for lfs, lfe in zip(*lf_idx) ]) v_hist_lf = np.concatenate([ track_hist.max_sustained_wind.values[lfs:lfe] for lfs, lfe in zip(*lf_idx) ]) v_synth_lf = np.concatenate([ track.max_sustained_wind.values[lfs:lfe] for lfs, lfe in zip(*lf_idx) ]) self.assertTrue(np.all(p_synth_lf > p_hist_lf)) self.assertTrue(np.all(v_synth_lf < v_hist_lf)) self.assertTrue( np.all(track.central_pressure.values <= track.environmental_pressure.values))
def test_interp_track_pass(self): """Interpolate track to min_time_step. Compare to MATLAB reference.""" tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) tc_track.equal_timestep(time_step_h=1) self.assertEqual(tc_track.data[0].time.size, 223) self.assertAlmostEqual(tc_track.data[0].lon.values[11], -27.426151640151684) self.assertAlmostEqual(float(tc_track.data[0].lat[23]), 12.300006169591480) self.assertEqual(tc_track.data[0].time_step[7], 1) self.assertEqual(np.max(tc_track.data[0].radius_max_wind), 0) self.assertEqual(np.min(tc_track.data[0].radius_max_wind), 0) self.assertEqual(tc_track.data[0].max_sustained_wind[21], 25) self.assertTrue(np.isfinite(tc_track.data[0].central_pressure.values).all()) self.assertAlmostEqual(tc_track.data[0].central_pressure.values[29], 1008, places=0) self.assertEqual(np.max(tc_track.data[0].environmental_pressure), 1010) self.assertEqual(np.min(tc_track.data[0].environmental_pressure), 1010) self.assertEqual(tc_track.data[0]['time.year'][13], 1951) self.assertEqual(tc_track.data[0]['time.month'][26], 8) self.assertEqual(tc_track.data[0]['time.day'][7], 27) self.assertEqual(tc_track.data[0].max_sustained_wind_unit, 'kn') self.assertEqual(tc_track.data[0].central_pressure_unit, 'mb') self.assertEqual(tc_track.data[0].orig_event_flag, 1) self.assertEqual(tc_track.data[0].name, '1951239N12334') self.assertEqual(tc_track.data[0].data_provider, 'hurdat_atl') np.testing.assert_array_equal(tc_track.data[0].basin, 'NA') self.assertEqual(tc_track.data[0].id_no, 1951239012334) self.assertEqual(tc_track.data[0].category, 1) # test some "generic floats" for time_step_h in [0.6663545049172093, 2.509374054925788, 8.175754471661111]: # artifically create data that doesn't start at full hour for loffset in [0, 22, 30]: tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) tc_track.data[0].time.values[:] += np.timedelta64(loffset, "m") tc_track.equal_timestep(time_step_h=time_step_h) np.testing.assert_array_equal(tc_track.data[0].time_step, time_step_h) self.assertTrue(np.isfinite(tc_track.data[0].central_pressure.values).all()) tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) tc_track.equal_timestep(time_step_h=0.16667) self.assertEqual(tc_track.data[0].time.size, 1332) self.assertTrue(np.all(tc_track.data[0].time_step == 0.16667)) self.assertTrue(np.isfinite(tc_track.data[0].central_pressure.values).all()) self.assertAlmostEqual(tc_track.data[0].lon.values[65], -27.397636528537127) for time_step_h in [0, -0.5, -1]: tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) msg = f"time_step_h is not a positive number: {time_step_h}" with self.assertRaises(ValueError, msg=msg) as _cm: tc_track.equal_timestep(time_step_h=time_step_h)
def test_get_track_pass(self): """Test get_track.""" tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK_SHORT) self.assertIsInstance(tc_track.get_track(), xr.Dataset) self.assertIsInstance(tc_track.get_track('1951239N12334'), xr.Dataset) tc_track_bis = tc.TCTracks() tc_track_bis.read_processed_ibtracs_csv(TEST_TRACK_SHORT) tc_track.append(tc_track_bis) self.assertIsInstance(tc_track.get_track(), list) self.assertIsInstance(tc_track.get_track('1951239N12334'), xr.Dataset)
def test_write_read_netcdf(self): """Test writting and reading netcdf4 TCTracks instances""" path = DATA_DIR.joinpath("tc_tracks_nc") path.mkdir(exist_ok=True) tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', storm_id='1988234N13299', estimate_missing=True) tc_track.write_netcdf(str(path)) tc_read = tc.TCTracks() tc_read.read_netcdf(str(path)) self.assertEqual(tc_track.get_track().sid, tc_read.get_track().sid)
def test_raw_ibtracs_invalid_pass(self): """Test reading invalid/non-existing TC from IBTrACS files""" tc_track = tc.TCTracks() with self.assertRaises(ValueError) as cm: tc_track.read_ibtracs_netcdf(storm_id='INVALID') self.assertIn("IDs are invalid", str(cm.exception)) self.assertIn("INVALID", str(cm.exception)) tc_track = tc.TCTracks() with self.assertRaises(ValueError) as cm: tc_track.read_ibtracs_netcdf(storm_id='1988234N13298') self.assertIn("IDs are not in IBTrACS", str(cm.exception)) self.assertIn("1988234N13298", str(cm.exception))
def test_write_read_pass(self): """Test writting and reading netcdf4 TCTracks instances""" path = os.path.join(DATA_DIR, "tc_tracks_nc") os.makedirs(path, exist_ok=True) tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', storm_id='1988234N13299', estimate_missing=True) tc_track.write_netcdf(path) tc_read = tc.TCTracks() tc_read.read_netcdf(path) self.assertEqual(tc_track.get_track().sid, tc_read.get_track().sid)
def test_discard_single_points(self): """Check discard_single_points option""" tc_track_singlept = tc.TCTracks() passed = False for year in range(1863, 1981): tc_track_singlept.read_ibtracs_netcdf(provider='usa', year_range=(year,year), discard_single_points=False) n_singlepts = np.sum([x.time.size == 1 for x in tc_track_singlept.data]) if n_singlepts > 0: tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', year_range=(year,year)) if tc_track.size == tc_track_singlept.size - n_singlepts: passed = True break self.assertTrue(passed)
def test_raw_ibtracs_empty_pass(self): """Test reading empty TC from IBTrACS files""" tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf( provider='usa', storm_id='1988234N13299') self.assertEqual(tc_track.size, 0) self.assertEqual(tc_track.get_track(), [])
def test_random_no_landfall_pass(self): """Test calc_random_walk with decay and no historical tracks with landfall""" tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK_SHORT) with self.assertLogs('climada.hazard.tc_tracks_synth', level='INFO') as cm: tc_track.calc_random_walk() self.assertIn('No historical track with landfall.', cm.output[1])
def test_read_simulations_chaz(self): """Test reading NetCDF output from CHAZ simulations""" tc_track = tc.TCTracks() tc_track.read_simulations_chaz(TEST_TRACK_CHAZ) self.assertEqual(len(tc_track.data), 13) self.assertEqual(tc_track.data[0].time.size, 5) self.assertEqual(tc_track.data[0].lon[3], 74.1388328911036) self.assertEqual(tc_track.data[0].lat[4], -9.813585651475156) self.assertEqual(tc_track.data[0].time_step[3], 6) self.assertEqual(tc_track.data[0].max_sustained_wind[2], 20.188325232226354) self.assertAlmostEqual(tc_track.data[0].central_pressure.values[1], 1008, places=0) self.assertTrue(np.all(tc_track.data[0].time.dt.year == 1991)) self.assertEqual(tc_track.data[0].time.dt.month[2], 1) self.assertEqual(tc_track.data[0].time.dt.day[3], 15) self.assertEqual(tc_track.data[0].max_sustained_wind_unit, 'kn') self.assertEqual(tc_track.data[0].central_pressure_unit, 'mb') self.assertEqual(tc_track.data[0].sid, 'chaz_test_tracks.nc-1-0') self.assertEqual(tc_track.data[0].name, 'chaz_test_tracks.nc-1-0') self.assertTrue(np.all([np.all(d.basin == 'GB') for d in tc_track.data])) self.assertEqual(tc_track.data[4].category, 0) self.assertEqual(tc_track.data[3].category, -1) tc_track.read_simulations_chaz(TEST_TRACK_CHAZ, year_range=(1990, 1991)) self.assertEqual(len(tc_track.data), 3) tc_track.read_simulations_chaz(TEST_TRACK_CHAZ, year_range=(1950, 1955)) self.assertEqual(len(tc_track.data), 0) tc_track.read_simulations_chaz(TEST_TRACK_CHAZ, ensemble_nums=[0, 2]) self.assertEqual(len(tc_track.data), 9)
def test_read_one_gettelman(self): """Test reading and model of TC from Gettelman track files""" tc_track_G = tc.TCTracks() # populate tracks by loading data from NetCDF: nc_data = nc.Dataset(TEST_TRACK_GETTELMAN) nstorms = nc_data.dimensions['storm'].size for i in range(nstorms): tc_track_G.read_one_gettelman(nc_data, i) self.assertEqual(tc_track_G.data[0].time.size, 29) self.assertEqual(tc_track_G.data[0].lon[11], 60.0) self.assertEqual(tc_track_G.data[0].lat[23], 10.20860481262207) self.assertEqual(tc_track_G.data[0].time_step[7], 3.) self.assertEqual(np.max(tc_track_G.data[0].radius_max_wind), 65) self.assertEqual(np.min(tc_track_G.data[0].radius_max_wind), 65) self.assertEqual(tc_track_G.data[0].max_sustained_wind[21], 39.91877223718089) self.assertEqual(tc_track_G.data[0].central_pressure[27], 1005.969482421875) self.assertEqual(np.max(tc_track_G.data[0].environmental_pressure), 1015) self.assertEqual(np.min(tc_track_G.data[0].environmental_pressure), 1015) self.assertEqual(tc_track_G.data[0].maximum_precipitation[14], 219.10108947753906) self.assertEqual(tc_track_G.data[0].average_precipitation[12], 101.43893432617188) self.assertEqual(tc_track_G.data[0].time.dt.year[13], 1979) self.assertEqual(tc_track_G.data[0].time.dt.month[26], 1) self.assertEqual(tc_track_G.data[0].time.dt.day[7], 2) self.assertEqual(tc_track_G.data[0].max_sustained_wind_unit, 'kn') self.assertEqual(tc_track_G.data[0].central_pressure_unit, 'mb') self.assertEqual(tc_track_G.data[0].sid, '0') self.assertEqual(tc_track_G.data[0].name, '0') np.testing.assert_array_equal(tc_track_G.data[0].basin, 'NI') self.assertEqual(tc_track_G.data[0].category, 0)
def test_read_with_provider(self): """Read a tropical cyclone with and without explicit provider.""" tc_track = tc.TCTracks() storm_id = '2012152N12130' tc_track.read_ibtracs_netcdf(storm_id=storm_id, provider='usa') track_ds = tc_track.get_track() self.assertEqual(track_ds.time.size, 51) self.assertEqual(track_ds.data_provider, 'ibtracs_usa') self.assertAlmostEqual(track_ds.lat.values[50], 34.3, places=5) self.assertAlmostEqual(track_ds.central_pressure.values[50], 989, places=5) self.assertAlmostEqual(track_ds.radius_max_wind.values[46], 20, places=5) tc_track.read_ibtracs_netcdf(storm_id=storm_id) track_ds = tc_track.get_track() self.assertEqual(track_ds.time.size, 35) self.assertEqual(track_ds.data_provider, 'ibtracs_official_3h_mixed') self.assertAlmostEqual(track_ds.lat.values[-1], 31.40, places=5) self.assertAlmostEqual(track_ds.central_pressure.values[-1], 980, places=5)
def test_interp_track_pass(self): """Interpolate track to min_time_step. Compare to MATLAB reference.""" tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) tc_track.equal_timestep(time_step_h=1) self.assertEqual(tc_track.data[0].time.size, 223) self.assertAlmostEqual(tc_track.data[0].lon.values[11], -27.426151640151684) self.assertAlmostEqual(float(tc_track.data[0].lat[23]), 12.300006169591480) self.assertEqual(tc_track.data[0].time_step[7], 1) self.assertEqual(np.max(tc_track.data[0].radius_max_wind), 0) self.assertEqual(np.min(tc_track.data[0].radius_max_wind), 0) self.assertEqual(tc_track.data[0].max_sustained_wind[21], 25) self.assertAlmostEqual(tc_track.data[0].central_pressure.values[29], 1.0077614e+03) self.assertEqual(np.max(tc_track.data[0].environmental_pressure), 1010) self.assertEqual(np.min(tc_track.data[0].environmental_pressure), 1010) self.assertEqual(tc_track.data[0]['time.year'][13], 1951) self.assertEqual(tc_track.data[0]['time.month'][26], 8) self.assertEqual(tc_track.data[0]['time.day'][7], 27) self.assertEqual(tc_track.data[0].max_sustained_wind_unit, 'kn') self.assertEqual(tc_track.data[0].central_pressure_unit, 'mb') self.assertEqual(tc_track.data[0].orig_event_flag, 1) self.assertEqual(tc_track.data[0].name, '1951239N12334') self.assertEqual(tc_track.data[0].data_provider, 'hurdat_atl') self.assertTrue(np.isnan(tc_track.data[0].basin)) self.assertEqual(tc_track.data[0].id_no, 1951239012334) self.assertEqual(tc_track.data[0].category, 1)
def test_random_walk_decay_pass(self): """Test land decay is called from calc_perturbed_trajectories.""" tc_track = tc.TCTracks() assert TC_ANDREW_FL.is_file() tc_track.read_processed_ibtracs_csv(TC_ANDREW_FL) nb_synth_tracks = 2 with self.assertLogs('climada.hazard.tc_tracks_synth', level='DEBUG') as cm: tc_track.calc_perturbed_trajectories( nb_synth_tracks=nb_synth_tracks, seed=25, decay=True) self.assertIn( 'No historical track of category Tropical Depression ' 'with landfall.', cm.output[1]) self.assertIn('Decay parameters from category Hurricane Cat. 4 taken.', cm.output[2]) self.assertIn( 'No historical track of category Hurricane Cat. 1 with ' 'landfall.', cm.output[3]) self.assertIn('Decay parameters from category Hurricane Cat. 4 taken.', cm.output[4]) self.assertIn( 'No historical track of category Hurricane Cat. 3 with ' 'landfall. Decay parameters from category Hurricane Cat. ' '4 taken.', cm.output[5]) self.assertIn( 'No historical track of category Hurricane Cat. 5 with ' 'landfall.', cm.output[6])
def test_random_walk_identical_pass(self): """Test 0 perturbation leads to identical tracks.""" tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TC_ANDREW_FL) nb_synth_tracks = 2 tc_track.calc_perturbed_trajectories(nb_synth_tracks=nb_synth_tracks, max_shift_ini=0, max_dspeed_rel=0, max_ddirection=0, decay=False) orig_track = tc_track.data[0] for syn_track in tc_track.data[1:]: np.testing.assert_allclose(orig_track.lon.values, syn_track.lon.values, atol=1e-4) np.testing.assert_allclose(orig_track.lat.values, syn_track.lat.values, atol=1e-4) for varname in [ "time", "time_step", "radius_max_wind", "max_sustained_wind", "central_pressure", "environmental_pressure" ]: np.testing.assert_array_equal(orig_track[varname].values, syn_track[varname].values)
def test_read_processed_ibtracs_csv(self): tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) self.assertEqual(tc_track.data[0].time.size, 38) self.assertEqual(tc_track.data[0].lon[11], -39.60) self.assertEqual(tc_track.data[0].lat[23], 14.10) self.assertEqual(tc_track.data[0].time_step[7], 6) self.assertEqual(np.max(tc_track.data[0].radius_max_wind), 0) self.assertEqual(np.min(tc_track.data[0].radius_max_wind), 0) self.assertEqual(tc_track.data[0].max_sustained_wind[21], 55) self.assertAlmostEqual(tc_track.data[0].central_pressure.values[29], 976, places=0) self.assertEqual(np.max(tc_track.data[0].environmental_pressure), 1010) self.assertEqual(np.min(tc_track.data[0].environmental_pressure), 1010) self.assertEqual(tc_track.data[0].time.dt.year[13], 1951) self.assertEqual(tc_track.data[0].time.dt.month[26], 9) self.assertEqual(tc_track.data[0].time.dt.day[7], 29) self.assertEqual(tc_track.data[0].max_sustained_wind_unit, 'kn') self.assertEqual(tc_track.data[0].central_pressure_unit, 'mb') self.assertEqual(tc_track.data[0].orig_event_flag, 1) self.assertEqual(tc_track.data[0].name, '1951239N12334') self.assertEqual(tc_track.data[0].sid, '1951239N12334') self.assertEqual(tc_track.data[0].id_no, 1951239012334) self.assertEqual(tc_track.data[0].data_provider, 'hurdat_atl') self.assertTrue(np.isnan(tc_track.data[0].basin)) self.assertEqual(tc_track.data[0].id_no, 1951239012334) self.assertEqual(tc_track.data[0].category, 1)
def test_read_simulations_storm(self): """Test reading NetCDF output from STORM simulations""" tc_track = tc.TCTracks() tc_track.read_simulations_storm(TEST_TRACK_STORM) self.assertEqual(len(tc_track.data), 6) self.assertEqual(tc_track.data[0].time.size, 15) self.assertEqual(tc_track.data[0].lon[3], 245.3) self.assertEqual(tc_track.data[0].lat[4], 11.9) self.assertEqual(tc_track.data[0].time_step[3], 3) self.assertEqual(tc_track.data[0].max_sustained_wind[2], 37.127429805615556) self.assertEqual(tc_track.data[0].radius_max_wind[5], 19.07407454551836) self.assertEqual(tc_track.data[0].central_pressure[1], 999.4) self.assertTrue(np.all(tc_track.data[0].time.dt.year == 1980)) self.assertEqual(tc_track.data[0].time.dt.month[2].item(), 6) self.assertEqual(tc_track.data[0].time.dt.day[3].item(), 1) self.assertEqual(tc_track.data[0].max_sustained_wind_unit, 'kn') self.assertEqual(tc_track.data[0].central_pressure_unit, 'mb') self.assertEqual(tc_track.data[0].sid, 'storm_test_tracks.txt-0-0') self.assertEqual(tc_track.data[0].name, 'storm_test_tracks.txt-0-0') self.assertTrue(np.all([d.basin == 'EP' for d in tc_track.data])) self.assertEqual(tc_track.data[4].category, 0) self.assertEqual(tc_track.data[3].category, 1) tc_track.read_simulations_storm(TEST_TRACK_STORM, years=[0, 2]) self.assertEqual(len(tc_track.data), 4) tc_track.read_simulations_storm(TEST_TRACK_STORM, years=[7]) self.assertEqual(len(tc_track.data), 0)
def test_read_estimate_missing(self): """Read a tropical cyclone and estimate missing values.""" tc_track = tc.TCTracks() storm_id = '2012152N12130' tc_track.read_ibtracs_netcdf(storm_id=storm_id, estimate_missing=True) track_ds = tc_track.get_track() # less time steps are discarded, leading to a larger total size self.assertEqual(track_ds.time.size, 99) self.assertEqual(track_ds.data_provider, 'ibtracs_official_3h_mixed') self.assertAlmostEqual(track_ds.lat.values[44], 33.30, places=5) self.assertAlmostEqual(track_ds.central_pressure.values[44], 976, places=5) self.assertAlmostEqual(track_ds.central_pressure.values[42], 980, places=5) # the wind speed at position 44 is missing in the original data self.assertAlmostEqual(track_ds.max_sustained_wind.values[44], 58, places=0) self.assertAlmostEqual(track_ds.radius_oci.values[40], 160, places=0) # after position 42, ROCI is missing in the original data self.assertAlmostEqual(track_ds.radius_oci.values[42], 200, places=-1) self.assertAlmostEqual(track_ds.radius_oci.values[85], 165, places=-1) self.assertAlmostEqual(track_ds.radius_oci.values[95], 155, places=-1)
def test_read_interpolate_missing(self): """Read a tropical cyclone with and without interpolating missing values.""" tc_track = tc.TCTracks() storm_id = '2010066S19050' tc_track.read_ibtracs_netcdf(storm_id=storm_id, interpolate_missing=False) track_ds = tc_track.get_track() self.assertEqual(track_ds.time.size, 50) self.assertAlmostEqual(track_ds.central_pressure.values[30], 992, places=5) self.assertAlmostEqual(track_ds.central_pressure.values[31], 1006, places=5) tc_track.read_ibtracs_netcdf(storm_id=storm_id, interpolate_missing=True) track_ds = tc_track.get_track() self.assertEqual(track_ds.time.size, 65) self.assertAlmostEqual(track_ds.central_pressure.values[30], 992, places=5) self.assertAlmostEqual(track_ds.central_pressure.values[38], 999, places=5) self.assertAlmostEqual(track_ds.central_pressure.values[46], 1006, places=5)
def test_ibtracs_correct_pass(self): """Check estimate_missing option""" tc_try = tc.TCTracks() tc_try.read_ibtracs_netcdf(provider='usa', storm_id='1982267N25289', estimate_missing=True) self.assertAlmostEqual(tc_try.data[0].central_pressure.values[0], 1013, places=0) self.assertAlmostEqual(tc_try.data[0].central_pressure.values[5], 1008, places=0) self.assertAlmostEqual(tc_try.data[0].central_pressure.values[-1], 1012, places=0)
def test_to_geodataframe_line(self): """Conversion of TCTracks to GeoDataFrame using LineStrings.""" tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) gdf_line = tc_track.to_geodataframe() self.assertIsInstance(gdf_line.basin[0], np.float64) self.assertEqual(gdf_line.size, 10) self.assertAlmostEqual(gdf_line.geometry[0].length, 54.0634224372971) self.assertIsInstance(gdf_line.bounds.minx, pd.core.series.Series) anti_track = tc.TCTracks() # test data set with two tracks: # * 1980052S16155: crosses the antimeridian # * 2018079S09162: close, but doesn't cross antimeridian; has self-intersections anti_track.read_netcdf(TEST_TRACKS_ANTIMERIDIAN) split = anti_track.to_geodataframe(split_lines_antimeridian=True) split.set_index('sid', inplace=True) self.assertIsInstance(split.loc['1980052S16155'].geometry, MultiLineString) self.assertIsInstance(split.loc['2018079S09162'].geometry, LineString) self.assertEqual(len(split.loc['1980052S16155'].geometry), 8) self.assertFalse(split.loc['2018079S09162'].geometry.is_simple) nosplit = anti_track.to_geodataframe(split_lines_antimeridian=False) nosplit.set_index('sid', inplace=True) self.assertIsInstance(nosplit.loc['1980052S16155'].geometry, LineString) self.assertIsInstance(nosplit.loc['2018079S09162'].geometry, LineString) self.assertFalse(nosplit.loc['2018079S09162'].geometry.is_simple) np.testing.assert_array_almost_equal( split.loc['1980052S16155'].geometry.bounds, (-180, -38.583244, 180, -17)) np.testing.assert_array_almost_equal( nosplit.loc['1980052S16155'].geometry.bounds, (150.800003, -38.583244, 185, -17)) np.testing.assert_array_almost_equal( split.loc['2018079S09162'].geometry.bounds, (148.600006, -22.41, 162.399994, -13.4)) np.testing.assert_array_almost_equal( nosplit.loc['2018079S09162'].geometry.bounds, (148.600006, -22.41, 162.399994, -13.4))
def test_random_walk_decay_pass(self): """Test land decay is called from calc_perturbed_trajectories.""" tc_track = tc.TCTracks() assert TC_ANDREW_FL.is_file() tc_track.read_processed_ibtracs_csv(TC_ANDREW_FL) nb_synth_tracks = 2 # should work if using global parameters with self.assertLogs('climada.hazard.tc_tracks_synth', level='DEBUG') as cm0: tc_track.calc_perturbed_trajectories( nb_synth_tracks=nb_synth_tracks, seed=25, decay=True, use_global_decay_params=True) self.assertEqual(len(cm0), 2) self.assertEqual(tc_track.size, 3) # but alert the user otherwise tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TC_ANDREW_FL) with self.assertLogs('climada.hazard.tc_tracks_synth', level='DEBUG') as cm: tc_track.calc_perturbed_trajectories( nb_synth_tracks=nb_synth_tracks, seed=25, decay=True, use_global_decay_params=False) self.assertIn( 'No historical track of category Tropical Depression ' 'with landfall.', cm.output[2]) self.assertIn('Decay parameters from category Hurricane Cat. 4 taken.', cm.output[3]) self.assertIn( 'No historical track of category Hurricane Cat. 1 with ' 'landfall.', cm.output[4]) self.assertIn('Decay parameters from category Hurricane Cat. 4 taken.', cm.output[5]) self.assertIn( 'No historical track of category Hurricane Cat. 3 with ' 'landfall. Decay parameters from category Hurricane Cat. ' '4 taken.', cm.output[6]) self.assertIn( 'No historical track of category Hurricane Cat. 5 with ' 'landfall.', cm.output[7])
def test_cutoff_tracks(self): tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(storm_id='1986226N30276') tc_track.equal_timestep() with self.assertLogs('climada.hazard.tc_tracks_synth', level='DEBUG') as cm: tc_track.calc_perturbed_trajectories(nb_synth_tracks=10) self.assertIn( 'The following generated synthetic tracks moved beyond ' 'the range of [-70, 70] degrees latitude', cm.output[1])
def test_read_raw_pass(self): """Read a tropical cyclone.""" tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', storm_id='2017242N16333') self.assertEqual(len(tc_track.data), 1) self.assertEqual(tc_track.get_track().time.dt.year.values[0], 2017) self.assertEqual(tc_track.get_track().time.dt.month.values[0], 8) self.assertEqual(tc_track.get_track().time.dt.day.values[0], 30) self.assertEqual(tc_track.get_track().time.dt.hour.values[0], 0) self.assertAlmostEqual(tc_track.get_track().lat.values[0], 16.1 + 3.8146972514141453e-07) self.assertAlmostEqual(tc_track.get_track().lon.values[0], -26.9 + 3.8146972514141453e-07) self.assertAlmostEqual( tc_track.get_track().max_sustained_wind.values[0], 30) self.assertAlmostEqual(tc_track.get_track().central_pressure.values[0], 1008) self.assertAlmostEqual( tc_track.get_track().environmental_pressure.values[0], 1012) self.assertAlmostEqual(tc_track.get_track().radius_max_wind.values[0], 60) self.assertEqual(tc_track.get_track().time.size, 123) self.assertAlmostEqual(tc_track.get_track().lat.values[-1], 36.8 - 7.629394502828291e-07) self.assertAlmostEqual(tc_track.get_track().lon.values[-1], -90.100006, 5) self.assertAlmostEqual( tc_track.get_track().central_pressure.values[-1], 1005) self.assertAlmostEqual( tc_track.get_track().max_sustained_wind.values[-1], 15) self.assertAlmostEqual( tc_track.get_track().environmental_pressure.values[-1], 1008) self.assertAlmostEqual(tc_track.get_track().radius_max_wind.values[-1], 60) self.assertFalse( np.isnan(tc_track.get_track().radius_max_wind.values).any()) self.assertFalse( np.isnan(tc_track.get_track().environmental_pressure.values).any()) self.assertFalse( np.isnan(tc_track.get_track().max_sustained_wind.values).any()) self.assertFalse( np.isnan(tc_track.get_track().central_pressure.values).any()) self.assertFalse(np.isnan(tc_track.get_track().lat.values).any()) self.assertFalse(np.isnan(tc_track.get_track().lon.values).any()) self.assertEqual(tc_track.get_track().basin, 'NA') self.assertEqual(tc_track.get_track().max_sustained_wind_unit, 'kn') self.assertEqual(tc_track.get_track().central_pressure_unit, 'mb') self.assertEqual(tc_track.get_track().sid, '2017242N16333') self.assertEqual(tc_track.get_track().name, 'IRMA') self.assertEqual(tc_track.get_track().orig_event_flag, True) self.assertEqual(tc_track.get_track().data_provider, 'usa') self.assertEqual(tc_track.get_track().category, 5)
def test_read_legacy_netcdf(self): """Test reading from NetCDF files with legacy basin attributes""" anti_track = tc.TCTracks() # test data set with two tracks: # * 1980052S16155: crosses the antimeridian # * 2018079S09162: close, but doesn't cross antimeridian; has self-intersections anti_track.read_netcdf(TEST_TRACKS_ANTIMERIDIAN) for tr in anti_track.data: self.assertEqual(tr.basin.shape, tr.time.shape) np.testing.assert_array_equal(tr.basin, "SP")
def test_penv_rmax_penv_pass(self): """read_ibtracs_netcdf""" tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', storm_id='1992230N11325') penv_ref = np.ones(97) * 1010 penv_ref[26:36] = [1011, 1012, 1013, 1014, 1015, 1014, 1014, 1014, 1014, 1012] self.assertTrue(np.allclose( tc_track.get_track().environmental_pressure.values, penv_ref)) self.assertTrue(np.allclose( tc_track.get_track().radius_max_wind.values, np.zeros(97)))
def test_get_extent(self): """Test extent/bounds attributes.""" storms = ['1988169N14259', '2002073S16161', '2002143S07157'] tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(storm_id=storms, provider=["usa", "bom"]) bounds = (153.585022, -23.200001, 258.714996, 17.514986) extent = (bounds[0], bounds[2], bounds[1], bounds[3]) bounds_buf = (153.485022, -23.300001, 258.814996, 17.614986) np.testing.assert_array_almost_equal(tc_track.bounds, bounds) np.testing.assert_array_almost_equal(tc_track.get_bounds(deg_buffer=0.1), bounds_buf) np.testing.assert_array_almost_equal(tc_track.extent, extent)
def test_read_range(self): """Read several TCs.""" tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(year_range=(2100, 2150)) self.assertEqual(tc_track.size, 0) tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', storm_id=None, year_range=(1915, 1916), basin='WP') self.assertEqual(tc_track.size, 0) tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', year_range=(1993, 1994), basin='EP', estimate_missing=False) self.assertEqual(tc_track.size, 33) tc_track = tc.TCTracks() tc_track.read_ibtracs_netcdf(provider='usa', year_range=(1993, 1994), basin='EP', estimate_missing=True) self.assertEqual(tc_track.size, 45)
def test_to_geodataframe_points(self): """Conversion of TCTracks to GeoDataFrame using Points.""" tc_track = tc.TCTracks() tc_track.read_processed_ibtracs_csv(TEST_TRACK) gdf_points = tc_track.to_geodataframe(as_points=True) self.assertIsInstance(gdf_points.unary_union.bounds, tuple) self.assertEqual(gdf_points.shape[0], len(tc_track.data[0].time)) self.assertEqual(gdf_points.shape[1], len(tc_track.data[0].variables)+len(tc_track.data[0].attrs)-1) self.assertAlmostEqual(gdf_points.buffer(3).unary_union.area, 348.79972062947854) self.assertIsInstance(gdf_points.iloc[0].time, pd._libs.tslibs.timestamps.Timestamp)