Skip to content

Commit 32a7cff

Browse files
first version of method from_netcdf_fast and set up the test
1 parent f198a6a commit 32a7cff

2 files changed

Lines changed: 138 additions & 7 deletions

File tree

climada/hazard/tc_tracks.py

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@
162162
"SI": 1005,
163163
"WP": 1005,
164164
"SP": 1004,
165+
"AU": 1004,
165166
}
166167
"""Basin-specific default environmental pressure"""
167168

@@ -1619,6 +1620,92 @@ def from_netcdf(cls, folder_name):
16191620
data.append(track)
16201621
return cls(data)
16211622

1623+
@classmethod
1624+
def from_netcdf_fast(cls, folder_name):
1625+
"""Create new TCTracks object from NetCDF files created with the FAST model
1626+
of Jonathan Lin.
1627+
1628+
GitHub Repository: https://github.com/linjonathan/tropical_cyclone_risk?
1629+
tab=readme-ov-file
1630+
Publication: https://agupubs.onlinelibrary.wiley.com/doi/epdf/10.1029/2023MS003686
1631+
1632+
Parameters:
1633+
----------
1634+
folder_name : str
1635+
Folder name from where to read files.
1636+
1637+
Returns:
1638+
-------
1639+
tracks : TCTracks
1640+
TCTracks obecjt with tracks data from the given directory of NetCDF files.
1641+
"""
1642+
1643+
file_tr = get_file_names(folder_name)
1644+
LOGGER.info("Reading %s files.", len(file_tr))
1645+
data = []
1646+
for file in file_tr:
1647+
if Path(file).suffix != ".nc":
1648+
continue
1649+
with xr.open_dataset(file) as ds:
1650+
for i in ds.n_trk:
1651+
# Select track
1652+
track = ds.sel(n_trk=i.item())
1653+
1654+
# Define coordinates
1655+
lat = track.lat_trks.data
1656+
lon = track.lon_trks.data
1657+
time = track.time.data
1658+
1659+
# Define variables
1660+
time_step_vector = np.full(time.shape[0], track.time.data[1])
1661+
max_sustained_wind = track.v_trks.data
1662+
basin_vector = np.full(time.shape[0], track.tc_basins.data.item())
1663+
central_pressure = np.nan # work in progress: get them from model
1664+
radius_max_wind = np.nan # work in progress: get them from model
1665+
env_pressure = BASIN_ENV_PRESSURE[track.tc_basins.data.item()]
1666+
env_pressure_vect = np.full(time.shape[0], env_pressure)
1667+
1668+
# Define hurricaine category and other attributes
1669+
max_sustained_wind_kn = np.nanmax(
1670+
max_sustained_wind * 1.943844
1671+
) # convert from m/s to knots
1672+
category_test = np.full(
1673+
len(SAFFIR_SIM_CAT), max_sustained_wind_kn
1674+
) < np.array(SAFFIR_SIM_CAT)
1675+
category = np.argmax(category_test) - 1
1676+
track_name = track.n_trk.item()
1677+
id_no = track.n_trk.item()
1678+
1679+
data.append(
1680+
xr.Dataset(
1681+
{
1682+
"time_step": ("time", time_step_vector),
1683+
"max_sustained_wind": ("time", max_sustained_wind),
1684+
# "central_pressure": ("time", central_pressure),
1685+
# "radius_max_wind": ("time", radius_max_wind),
1686+
"environmental_pressure": ("time", env_pressure_vect),
1687+
"basin": ("time", basin_vector),
1688+
},
1689+
coords={
1690+
"time": ("time", time),
1691+
"lat": ("time", lat),
1692+
"lon": ("time", lon),
1693+
},
1694+
attrs={
1695+
"max_sustained_wind_unit": "kn",
1696+
"central_pressure_unit": "mb",
1697+
"name": track_name,
1698+
"sid": track_name,
1699+
"orig_event_flag": False,
1700+
"data_provider": "FAST",
1701+
"id_no": id_no,
1702+
"category": category,
1703+
},
1704+
)
1705+
)
1706+
1707+
return cls(data)
1708+
16221709
def write_hdf5(self, file_name, complevel=5):
16231710
"""Write TC tracks in NetCDF4-compliant HDF5 format.
16241711
@@ -2665,20 +2752,20 @@ def ibtracs_fit_param(explained, explanatory, year_range=(1980, 2019), order=1):
26652752
return sm_results
26662753

26672754

2668-
def ibtracs_track_agency(ds_sel):
2755+
def ibtracs_track_agency(track):
26692756
"""Get preferred IBTrACS agency for each entry in the dataset.
26702757
26712758
Parameters
26722759
----------
2673-
ds_sel : xarray.Dataset
2760+
track : xarray.Dataset
26742761
Subselection of original IBTrACS NetCDF dataset.
26752762
26762763
Returns
26772764
-------
26782765
agency_pref : list of str
26792766
Names of IBTrACS agencies in order of preference.
26802767
track_agency_ix : xarray.DataArray of ints
2681-
For each entry in `ds_sel`, the agency to use, given as an index into `agency_pref`.
2768+
For each entry in `track`, the agency to use, given as an index into `agency_pref`.
26822769
"""
26832770
agency_pref = ["wmo"] + IBTRACS_AGENCIES
26842771
agency_map = {a.encode("utf-8"): i for i, a in enumerate(agency_pref)}
@@ -2687,11 +2774,11 @@ def ibtracs_track_agency(ds_sel):
26872774
)
26882775
agency_map[b""] = agency_map[b"wmo"]
26892776
agency_fun = lambda x: agency_map[x]
2690-
if "track_agency" not in ds_sel.data_vars.keys():
2691-
ds_sel["track_agency"] = ds_sel["wmo_agency"].where(
2692-
ds_sel["wmo_agency"] != b"", ds_sel["usa_agency"]
2777+
if "track_agency" not in track.data_vars.keys():
2778+
track["track_agency"] = track["wmo_agency"].where(
2779+
track["wmo_agency"] != b"", track["usa_agency"]
26932780
)
2694-
track_agency_ix = xr.apply_ufunc(agency_fun, ds_sel["track_agency"], vectorize=True)
2781+
track_agency_ix = xr.apply_ufunc(agency_fun, track["track_agency"], vectorize=True)
26952782
return agency_pref, track_agency_ix
26962783

26972784

climada/hazard/test/test_tc_tracks.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Test tc_tracks module.
2020
"""
2121

22+
import os
2223
import unittest
2324
from datetime import datetime as dt
2425

@@ -48,6 +49,37 @@
4849
TEST_TRACKS_ANTIMERIDIAN = DATA_DIR.joinpath("tracks-antimeridian")
4950
TEST_TRACKS_LEGACY_HDF5 = DATA_DIR.joinpath("tctracks_hdf5_legacy.nc")
5051

52+
TEST_TRACKS_FAST_dummy = xr.Dataset(
53+
data_vars={
54+
"lon_trks": (("n_trk", "time"), np.random.uniform(-180, 180, size=(20, 361))),
55+
"lat_trks": (("n_trk", "time"), np.random.uniform(-90, 90, size=(20, 361))),
56+
"u250_trks": (("n_trk", "time"), np.random.randn(20, 361)),
57+
"v250_trks": (("n_trk", "time"), np.random.randn(20, 361)),
58+
"u850_trks": (("n_trk", "time"), np.random.randn(20, 361)),
59+
"v850_trks": (("n_trk", "time"), np.random.randn(20, 361)),
60+
"v_trks": (("n_trk", "time"), np.random.randn(20, 361)),
61+
"m_trks": (("n_trk", "time"), np.random.randn(20, 361)),
62+
"vmax_trks": (("n_trk", "time"), np.random.randn(20, 361)),
63+
"tc_month": (("n_trk",), np.random.randint(1, 13, size=20)),
64+
"tc_basins": (
65+
("n_trk",),
66+
np.random.choice(["AU", "EP", "NA", "NI", "SI", "SP", "WP"], size=20),
67+
),
68+
"tc_years": (("n_trk",), np.full(20, 2025)),
69+
"seeds_per_month": (
70+
("year", "basin", "month"),
71+
np.random.randint(0, 5, size=(1, 7, 12)),
72+
),
73+
},
74+
coords={
75+
"n_trk": np.arange(20),
76+
"time": np.linspace(0, 1.296e6, 361),
77+
"year": [2025],
78+
"basin": ["AU", "EP", "NA", "NI", "SI", "SP", "WP"],
79+
"month": np.arange(1, 13),
80+
},
81+
)
82+
5183

5284
class TestIbtracs(unittest.TestCase):
5385
"""Test reading and model of TC from IBTrACS files"""
@@ -609,6 +641,18 @@ def test_from_simulations_storm(self):
609641
tc_track = tc.TCTracks.from_simulations_storm(TEST_TRACK_STORM, years=[7])
610642
self.assertEqual(len(tc_track.data), 0)
611643

644+
def test_from_netcdf_fast(self):
645+
"""test the import of netcdf files from fast model"""
646+
647+
# create dummy .nc file to read, delate it in the end
648+
file_path = DATA_DIR.joinpath("fast_test_tracks.nc")
649+
TEST_TRACKS_FAST_dummy.to_netcdf(file_path)
650+
tc_track = tc.TCTracks.from_netcdf_fast(file_path)
651+
# test various instances
652+
653+
# remove file
654+
os.remove(file_path)
655+
612656
def test_to_geodataframe_points(self):
613657
"""Conversion of TCTracks to GeoDataFrame using Points."""
614658
tc_track = tc.TCTracks.from_processed_ibtracs_csv(TEST_TRACK)

0 commit comments

Comments
 (0)