1. Studying a specific cyclone#

In this example, we want to study hurricane Wilma (the deepest Atlantic hurricane on record).

[1]:
import huracanpy
import matplotlib.pyplot as plt

1.1. Load IBTrACS and subset the specific hurricane#

Two subsets of IBTrACS are embedded within HuracanPy: WMO and JTWC. Default behavior is loading the embedded WMO subset. You can also retrieve the full and latest IBTrACS files by specifying a different subset. For more information, see the huracanpy.load guide.

The tracks are loaded as an xarray.Dataset, with one dimension “record” corresponding to each point. Variables indicate position in space and time, as well as additional attributes such as maximum wind speed and minimum sea-level pressure (SLP).

[2]:
# Here we load the WMO subset. This raises a warning that reminds you of the main
# caveats.
ib = huracanpy.load(source="ibtracs")
ib
/home/docs/checkouts/readthedocs.org/user_builds/huracanpy/envs/stable/lib/python3.12/site-packages/huracanpy/_data/ibtracs.py:119: UserWarning: This offline function loads a light version of IBTrACS which is embedded within the package, based on a file produced manually by the developers.
 It was last updated on the 15th Nov 2024, based on the IBTrACS file at that date.
 It contains only data from 1980 up to the last year with no provisional tracks. All spur tracks were removed. Only 6-hourly time steps were kept.
  warnings.warn(
/home/docs/checkouts/readthedocs.org/user_builds/huracanpy/envs/stable/lib/python3.12/site-packages/huracanpy/_data/ibtracs.py:128: UserWarning: You are loading the IBTrACS-WMO subset. This dataset contains the positions and intensity reported by the WMO agency responsible for each basin
 Be aware of the fact that wind and pressure data is provided as they are in IBTrACS, which means in particular that wind speeds are in knots and averaged over different time periods.
 For more information, see the IBTrACS column documentation at https://www.ncei.noaa.gov/sites/default/files/2021-07/IBTrACS_v04_column_documentation.pdf
  warnings.warn(
[2]:
<xarray.Dataset> Size: 15MB
Dimensions:   (record: 143287)
Dimensions without coordinates: record
Data variables:
    track_id  (record) <U13 7MB '1980001S13173' ... '2022356N09085'
    season    (record) int64 1MB 1980 1980 1980 1980 ... 2022 2022 2022 2022
    basin     (record) <U2 1MB 'SP' 'SP' 'SP' 'SP' 'SP' ... 'NI' 'NI' 'NI' 'NI'
    time      (record) datetime64[s] 1MB 1980-01-01 ... 2022-12-25T06:00:00
    lon       (record) float64 1MB 172.5 172.4 172.5 172.8 ... 82.5 82.2 81.6
    lat       (record) float64 1MB -12.5 -11.9 -11.5 -11.2 ... 9.7 9.3 9.0 8.5
    wind      (record) float64 1MB nan nan nan nan 30.0 ... 25.0 25.0 25.0 25.0
    slp       (record) float64 1MB nan nan nan ... 1.004e+03 1.004e+03 1.004e+03

Wilma corresponds to index 2005289N18282, so we subset this storm. There are two ways of doing this:

  1. Use xarray’s where

[3]:
wilma = ib.where(ib.track_id == "2005289N18282", drop=True)
  1. Use huracanpy’s sel_id method (more efficient and shorter, but does the same thing)

Note: the .hrcn is called an accessor, and allows you to call HuracanPy functions as methods on the xarray objects.

[4]:
wilma = ib.hrcn.sel_id("2005289N18282")
# The Wilma object contains only the data for Wilma:
wilma
[4]:
<xarray.Dataset> Size: 5kB
Dimensions:   (record: 45)
Dimensions without coordinates: record
Data variables:
    track_id  (record) <U13 2kB '2005289N18282' ... '2005289N18282'
    season    (record) int64 360B 2005 2005 2005 2005 ... 2005 2005 2005 2005
    basin     (record) <U2 360B 'NA' 'NA' 'NA' 'NA' 'NA' ... 'NA' 'NA' 'NA' 'NA'
    time      (record) datetime64[s] 360B 2005-10-15T18:00:00 ... 2005-10-26T...
    lon       (record) float64 360B -78.5 -78.8 -79.0 ... -57.5 -55.0 -52.0
    lat       (record) float64 360B 17.6 17.6 17.5 17.5 ... 42.5 44.0 45.0 45.5
    wind      (record) float64 360B 25.0 25.0 30.0 30.0 ... 60.0 55.0 50.0 40.0
    slp       (record) float64 360B 1.004e+03 1.004e+03 ... 986.0 990.0

1.2. Add category info#

You can add the Saffir-Simpson and/or the pressure category of Wilma to the tracks (for full list of available info, see huracanpy.info).

1.2.1. Add Saffir-Simpson Category#

This is stored in the saffir_simpson_category variable

[5]:
wilma = wilma.hrcn.add_saffir_simpson_category(wind_name="wind", wind_units="knots")
wilma.saffir_simpson_category
[5]:
<xarray.DataArray 'saffir_simpson_category' (record: 45)> Size: 360B
array([-1, -1, -1, -1, -1, -1,  0,  0,  0,  0,  1,  1,  2,  5,  5,  5,  5,
        5,  5,  5,  5,  5,  5,  5,  4,  4,  4,  3,  2,  2,  2,  2,  3,  3,
        4,  3,  4,  4,  3,  3,  2,  1,  0,  0,  0])
Dimensions without coordinates: record

1.2.2. Add pressure category#

[6]:
wilma = wilma.hrcn.add_pressure_category(slp_name="slp")
wilma.pressure_category
[6]:
<xarray.DataArray 'pressure_category' (record: 45)> Size: 360B
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 5, 5, 5, 5, 5, 5, 5, 5,
       4, 4, 4, 4, 4, 3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 1, 1, 1, 1,
       1])
Dimensions without coordinates: record
Attributes:
    units:    dimensionless

Note: Most of the accessor methods have a get_ and an add_ version. get_ returns the values of what you ask for as a DataArray, while add_ adds it directly to the dataset with a default name. In the previous case, we could have called get_pressure_category and then save it as a variable, and potentially add it to the dataset separately

[7]:
wilma.hrcn.get_pressure_category(slp_name="slp")
[7]:
<xarray.DataArray (record: 45)> Size: 360B
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 5, 5, 5, 5, 5, 5, 5, 5,
       4, 4, 4, 4, 4, 3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 1, 1, 1, 1,
       1])
Dimensions without coordinates: record
Attributes:
    units:    dimensionless

1.3. Plot the track and its evolution#

1.3.1. Plot the track on a map, colored by Saffir-Simpson category#

[8]:
wilma.hrcn.plot_tracks(
    intensity_var_name="saffir_simpson_category", scatter_kws={"palette": "turbo"}
)
[8]:
(<Figure size 1000x1000 with 1 Axes>, <GeoAxes: xlabel='lon', ylabel='lat'>)
../../_images/user_guide_demos_single_cyclone_15_1.png

1.3.2. Plot intensity time series using matplotlib#

[9]:
fig, axs = plt.subplots(2, sharex=True)
axs[0].plot(wilma.time, wilma.wind)
axs[1].plot(wilma.time, wilma.slp)
axs[0].set_ylabel("Wind / kn")
axs[1].set_ylabel("SLP / hPa")
[9]:
Text(0, 0.5, 'SLP / hPa')
../../_images/user_guide_demos_single_cyclone_17_1.png

1.4. Calculate properties#

1.4.1. Duration#

Note duration is in hours

[10]:
wilma.hrcn.get_track_duration()
[10]:
<xarray.DataArray 'duration' (track_id: 1)> Size: 8B
array([264.])
Coordinates:
  * track_id  (track_id) object 8B '2005289N18282'
Attributes:
    units:    hours

1.4.2. ACE#

Accumulated cyclone energy (ACE) is a commonly used measure of cyclone activity that combines the energy and duration of cyclones.

1.4.2.1. Compute ACE for each point#

[11]:
wilma = wilma.hrcn.add_ace(wind_units="knots")
wilma.ace
[11]:
<xarray.DataArray 'ace' (record: 45)> Size: 360B
array([0.    , 0.    , 0.    , 0.    , 0.    , 0.    , 0.1225, 0.16  ,
       0.2025, 0.3025, 0.36  , 0.4225, 0.5625, 1.69  , 2.25  , 2.56  ,
       1.96  , 1.8225, 1.69  , 1.69  , 1.69  , 1.69  , 1.69  , 1.5625,
       1.44  , 1.44  , 1.21  , 1.    , 0.7225, 0.7225, 0.7225, 0.7225,
       0.81  , 0.9025, 1.21  , 0.9025, 1.1025, 1.21  , 1.    , 0.81  ,
       0.5625, 0.36  , 0.3025, 0.25  , 0.16  ])
Dimensions without coordinates: record
Attributes:
    units:    knot ** 2

1.4.2.2. Plot cumulated ACE#

[12]:
plt.plot(wilma.time, wilma.ace.cumsum())
[12]:
[<matplotlib.lines.Line2D at 0x7b938f2b6bd0>]
../../_images/user_guide_demos_single_cyclone_24_1.png

1.4.3. Translation speed#

1.4.3.1. Compute translation speed#

[13]:
wilma = wilma.hrcn.add_translation_speed()

1.4.3.2. Plot translation speed against latitude#

[14]:
plt.plot(wilma.lat, wilma.translation_speed)
plt.xlabel("Latitude / °")
plt.ylabel("Translation speed / m/s")
[14]:
Text(0, 0.5, 'Translation speed / m/s')
../../_images/user_guide_demos_single_cyclone_28_1.png

1.4.4. Intensification rate#

1.4.4.1. Add intensification rate in wind and pressure#

NB: The rates will be in unit/s, where unit is the unit of the variable.

[15]:
wilma = wilma.hrcn.add_rate(var_name="wind")
wilma = wilma.hrcn.add_rate(var_name="slp")

1.4.4.2. Plot intensity time series#

[16]:
fig, axs = plt.subplots(2, sharex=True)
# Convert to kn/h and hPa/h
axs[0].plot(wilma.time, wilma.rate_wind * 3600)
axs[1].plot(wilma.time, wilma.rate_slp * 3600)
axs[0].set_ylabel("kn/h")
axs[1].set_ylabel("hPa/h")
[16]:
Text(0, 0.5, 'hPa/h')
../../_images/user_guide_demos_single_cyclone_32_1.png