Source code for huracanpy.tc._ace

"""
Module containing functions to compute ACE
"""

import numpy as np
from numpy.polynomial.polynomial import Polynomial
import pint
from metpy.xarray import preprocess_and_wrap
from metpy.units import units

from .._metpy import dequantify_results


[docs] def ace( wind, sum_by=None, threshold=34 * units("knots"), wind_units="m s-1", ): r"""Calculate accumulate cyclone energy (ACE) .. math:: \mathrm{ACE} = 10^{-4} \sum v_\mathrm{max}^2 \quad (v_\mathrm{max} \ge 34 \mathrm{kn}) By default, this function will return the "ACE" for each individual point in `wind`. To calculate more useful quantities of ACE, use the `sum_by` keyword. For example, to calculate the ACE of each individual track, doing >>> ace_by_track = huracanpy.tc.ace(tracks.wind, sum_by=tracks.track_id) will return a DataArray with track_id as a coordinate and the sum of ACE for each track as the data. Note that this is equivalent to using groupby: >>> ace_by_point = huracanpy.tc.ace(tracks.wind) >>> ace_by_track = ace_by_point.groupby(tracks.track_id).sum() To calculate the average ACE by track, you can do >>> ace_by_track_mean = ace_by_track.mean() Similarly to calculate a climatological mean ACE by year, run >>> climatological_ace = huracanpy.tc.ace(tracks.wind,sum_by=tracks.time.dt.year).mean() Parameters ---------- wind : array_like Maximum velocity of a tropical cyclone associated with the tracks dataset sum_by : array_like Variable to take the sum of ACE values across. Must have the same length as wind threshold : scalar, default=34 knots ACE is set to zero below this threshold wind speed. The default argument is in knots. To pass an argument with units, use :py:mod:`metpy.units`, otherwise any non-default argument will be assumed to have the units of "wind_units" which is "m s-1" by default. wind_units : str, default="m s-1" If the units of wind are not specified in the attributes then the function will assume it is in these units before converting to knots Returns ------- array_like The ACE for each track in wind """ ace_values = get_ace(wind, threshold, wind_units) if sum_by is not None: ace_values = ace_values.groupby(sum_by).sum() return ace_values
[docs] def pace( pressure, wind=None, model=None, sum_by=None, threshold_wind=None, threshold_pressure=None, wind_units="m s-1", **kwargs, ): """Calculate a pressure-based accumulated cyclone energy (PACE) for each individual point PACE is calculated the same way as ACE, but the wind is derived from fitting a pressure-wind relationship and calculating wind values from pressure using this fit Example ------- This function can be called in two ways 1. Pass the pressure and wind to fit a pressure-wind relationship to the data and then calculate pace from the winds derived from this fit >>> pace, pw_model = get_pace(pressure, wind) The default model to fit is a quadratic polynomial (:py:class:`numpy.polynomial.polynomial.Polynomial` with `deg=2`) 2. Pass just the pressure and an already fit model to calculate the wind speeds from this model >>> pace, _ = get_pace(pressure, model=pw_model) Parameters ---------- pressure : array_like wind : array_like, optional model : str, class, or object, optional sum_by : array_like Variable to take the sum of PACE values across. Must have the same length as pressure/wind. For examples, see the documentation for `huracanpy.tc.ace` threshold_wind : scalar, optional threshold_pressure : scalar, optional wind_units : str, default="m s-1" **kwargs Returns ------- pace_values : array_like model : object """ pace_values, model = get_pace( pressure, wind=wind, model=model, threshold_wind=threshold_wind, threshold_pressure=threshold_pressure, wind_units=wind_units, **kwargs, ) if sum_by is not None: pace_values = pace_values.groupby(sum_by).sum() return pace_values, model
@dequantify_results @preprocess_and_wrap(wrap_like="wind") def get_ace(wind, threshold=34 * units("knots"), wind_units="m s-1"): """Calculate accumulate cyclone energy (ACE) for each individual point Parameters ---------- wind : array_like Maximum velocity of a tropical cyclone threshold : scalar, default=34 knots ACE is set to zero below this threshold wind speed. The default argument is in knots. To pass an argument with units, use :py:mod:`metpy.units`, otherwise any non-default argument will be assumed to have the units of "wind_units" which is "m s-1" by default. wind_units : str, default="m s-1" If the units of wind are not specified in the attributes then the function will assume it is in these units before converting to knots Returns ------- array_like The ACE at each point in wind """ if not isinstance(wind, pint.Quantity) or wind.unitless: wind = wind * units(wind_units) wind = wind.to(units("knots")) if threshold is not None: if not isinstance(threshold, pint.Quantity) or threshold.unitless: threshold = threshold * units(wind_units) wind[wind < threshold] = 0 * units("knots") ace_values = (wind**2.0) * 1e-4 return ace_values def get_pace( pressure, wind=None, model=None, threshold_wind=None, threshold_pressure=None, wind_units="m s-1", **kwargs, ): """Calculate a pressure-based accumulated cyclone energy (PACE) for each individual point PACE is calculated the same way as ACE, but the wind is derived from fitting a pressure-wind relationship and calculating wind values from pressure using this fit Example ------- This function can be called in two ways 1. Pass the pressure and wind to fit a pressure-wind relationship to the data and then calculate pace from the winds derived from this fit >>> pace, pw_model = get_pace(pressure, wind) The default model to fit is a quadratic polynomial (:py:class:`numpy.polynomial.polynomial.Polynomial` with `deg=2`) 2. Pass just the pressure and an already fit model to calculate the wind speeds from this model >>> pace, _ = get_pace(pressure, model=pw_model) Parameters ---------- pressure : array_like wind : array_like, optional model : str or class, optional threshold_wind : scalar, optional threshold_pressure : scalar, optional wind_units : str, default="m s-1" **kwargs Returns ------- pace_values : array_like model : object """ model_wind, model = get_pressure_wind_relation( pressure, wind=wind, model=model, **kwargs ) pace_values = get_ace(model_wind, threshold=threshold_wind, wind_units=wind_units) if threshold_pressure is not None: pace_values[pressure > threshold_pressure] = 0.0 return pace_values, model def get_pressure_wind_relation(pressure, wind=None, model=None, **kwargs): if isinstance(model, str): if model.lower() == "z2021": model = pw_z2021 elif model.lower() == "holland": model = pw_holland elif wind is not None: if model is None: # Here, we calculate a quadratic P/W fit based off of the "control" if "deg" not in kwargs: kwargs["deg"] = 2 model = Polynomial.fit(pressure, wind, **kwargs) else: model = model.fit(pressure, wind, **kwargs) elif model is None: raise ValueError( "Need to specify either wind or model to calculate pressure-wind relation" ) wind_from_fit = _get_wind_from_model(pressure, model) return wind_from_fit, model @preprocess_and_wrap(wrap_like="pressure") def _get_wind_from_model(pressure, model): return np.array(model(pressure)) # Pre-determined pressure-wind relationships _z2021 = Polynomial([1.43290190e01, 5.68356519e-01, -1.05371378e-03]) def pw_z2021(pressure): return _z2021(1010.0 - pressure) def pw_holland(pressure): return 2.3 * (1010.0 - pressure) ** 0.76