示例#1
0
    def test_estimate_params_pass(self):
        """Test track parameter estimation functions."""
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        v_max = np.array([45, np.nan, 50, 55, 0, 60, 75])
        lat = np.array([13.8, 13.9, 14, 14.1, 14.1, np.nan, -999])
        lon = np.array([np.nan, -52.8, -54.4, -56, -58.4, -59.7, -61.1])
        ref_pres = np.array([np.nan, 993, 990, 986, np.nan, 1004, np.nan])
        out_pres = tc._estimate_pressure(cen_pres, lat, lon, v_max)
        np.testing.assert_array_almost_equal(ref_pres, out_pres, decimal=0)

        v_max = np.array([45, np.nan, 50, 55, 0, 60, 75])
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        lat = np.array([13.8, 13.9, 14, 14.1, 14.1, np.nan, -999])
        lon = np.array([np.nan, -52.8, -54.4, -56, -58.4, -59.7, -61.1])
        ref_vmax = np.array([45, 46, 50, 55, np.nan, 60, 75])
        out_vmax = tc._estimate_vmax(v_max, lat, lon, cen_pres)
        np.testing.assert_array_almost_equal(ref_vmax, out_vmax, decimal=0)

        roci = np.array([np.nan, -1, 145, 170, 180, 0, -5])
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        ref_roci = np.array(
            [np.nan, 182.792715, 145, 170, 180, 161.5231086, np.nan])
        out_roci = tc.estimate_roci(roci, cen_pres)
        np.testing.assert_array_almost_equal(ref_roci, out_roci)

        rmw = np.array([17, 33, -1, 25, np.nan, -5, 13])
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        ref_rmw = np.array([17, 33, np.nan, 25, np.nan, 43.95543761, 13])
        out_rmw = tc.estimate_rmw(rmw, cen_pres)
        np.testing.assert_array_almost_equal(ref_rmw, out_rmw)
示例#2
0
    def test_estimate_params_pass(self):
        """Test track parameter estimation functions."""
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        v_max = np.array([45, np.nan, 50, 55, 0, 60, 75])
        lat = np.array([13.8, 13.9, 14, 14.1, 14.1, np.nan, -999])
        lon = np.array([np.nan, -52.8, -54.4, -56, -58.4, -59.7, -61.1])
        ref_pres = np.array(
            [np.nan, 993, 990.2324, 986.6072, np.nan, 1004, np.nan])
        out_pres = tc._estimate_pressure(cen_pres, lat, lon, v_max)
        self.assertTrue(np.allclose(ref_pres, out_pres, equal_nan=True))

        v_max = np.array([45, np.nan, 50, 55, 0, 60, 75])
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        lat = np.array([13.8, 13.9, 14, 14.1, 14.1, np.nan, -999])
        lon = np.array([np.nan, -52.8, -54.4, -56, -58.4, -59.7, -61.1])
        ref_vmax = np.array([45, 46.38272, 50, 55, np.nan, 60, 75])
        out_vmax = tc._estimate_vmax(v_max, lat, lon, cen_pres)
        self.assertTrue(np.allclose(ref_vmax, out_vmax, equal_nan=True))

        roci = np.array([np.nan, -1, 145, 170, 180, 0, -5])
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        ref_roci = np.array(
            [np.nan, 182.792715, 145, 170, 180, 161.5231086, np.nan])
        out_roci = tc.estimate_roci(roci, cen_pres)
        self.assertTrue(np.allclose(ref_roci, out_roci, equal_nan=True))

        rmw = np.array([17, 33, -1, 25, np.nan, -5, 13])
        cen_pres = np.array([-999, 993, np.nan, -1, 0, 1004, np.nan])
        ref_rmw = np.array([17, 33, np.nan, 25, np.nan, 43.95543761, 13])
        out_rmw = tc.estimate_rmw(rmw, cen_pres)
        self.assertTrue(np.allclose(ref_rmw, out_rmw, equal_nan=True))
示例#3
0
def track_data_force_HRS(forcast_df, HRS_SPEED):
    track = xr.Dataset(
        data_vars={
            'max_sustained_wind':
            ('time', pd.Series(HRS_SPEED).interpolate().tolist()),
            'environmental_pressure':
            ('time', forcast_df.environmental_pressure.values),
            'central_pressure':
            ('time', pd.Series(
                forcast_df.central_pressure.values).interpolate().tolist()),
            'lat': ('time',
                    pd.Series(forcast_df.lat.values).interpolate().tolist()),
            'lon': ('time',
                    pd.Series(forcast_df.lon.values).interpolate().tolist()),
            'radius_max_wind':
            ('time', pd.Series(
                forcast_df.radius_max_wind.values).interpolate().tolist()),
            'radius_max_wind':
            ('time',
             estimate_rmw(forcast_df.radius_max_wind.values,
                          forcast_df.central_pressure.values)),
            'radius_oci': ('time',
                           estimate_roci(forcast_df.radius_max_wind.values,
                                         forcast_df.central_pressure.values)),
            'time_step': ('time', forcast_df.time_step.values),
        },
        coords={
            'time': forcast_df.time.values,
        },
        attrs={
            'max_sustained_wind_unit': 'm/s',
            'central_pressure_unit': 'mb',
            'name': forcast_df.name,
            'sid': forcast_df.sid,  #+str(forcast_df.ensemble_number),
            'orig_event_flag': forcast_df.orig_event_flag,
            'data_provider': forcast_df.data_provider,
            'id_no': forcast_df.id_no,
            'ensemble_number': forcast_df.ensemble_number,
            'is_ensemble': forcast_df.is_ensemble,
            'forecast_time': forcast_df.forecast_time,
            'basin': forcast_df.basin,
            'category': forcast_df.category,
        })

    track = track.set_coords(['lat', 'lon'])
    return track
示例#4
0
    def test_estimate_rmw_pass(self):
        """Test estimate_rmw function."""
        NM_TO_KM = (1.0 * ureg.nautical_mile).to(ureg.kilometer).magnitude

        tc_track = tc.TCTracks()
        tc_track.read_processed_ibtracs_csv(TEST_TRACK)
        tc_track.equal_timestep()
        rad_max_wind = tc.estimate_rmw(
            tc_track.data[0].radius_max_wind.values,
            tc_track.data[0].central_pressure.values) * NM_TO_KM

        self.assertAlmostEqual(rad_max_wind[0], 87, places=0)
        self.assertAlmostEqual(rad_max_wind[10], 87, places=0)
        self.assertAlmostEqual(rad_max_wind[128], 56, places=0)
        self.assertAlmostEqual(rad_max_wind[129], 55, places=0)
        self.assertAlmostEqual(rad_max_wind[130], 54, places=0)
        self.assertAlmostEqual(rad_max_wind[189], 53, places=0)
        self.assertAlmostEqual(rad_max_wind[190], 55, places=0)
        self.assertAlmostEqual(rad_max_wind[191], 57, places=0)
        self.assertAlmostEqual(rad_max_wind[192], 58, places=0)
        self.assertAlmostEqual(rad_max_wind[200], 71, places=0)
示例#5
0
    def test_estimate_rmw_pass(self):
        """Test estimate_rmw function."""
        NM_TO_KM = (1.0 * ureg.nautical_mile).to(ureg.kilometer).magnitude

        tc_track = tc.TCTracks()
        tc_track.read_processed_ibtracs_csv(TEST_TRACK)
        tc_track.equal_timestep()
        rad_max_wind = tc.estimate_rmw(
            tc_track.data[0].radius_max_wind.values,
            tc_track.data[0].central_pressure.values) * NM_TO_KM

        self.assertAlmostEqual(rad_max_wind[0], 86.4471340900, places=5)
        self.assertAlmostEqual(rad_max_wind[10], 86.525605570, places=5)
        self.assertAlmostEqual(rad_max_wind[128], 55.25462781, places=5)
        self.assertAlmostEqual(rad_max_wind[129], 54.40164284, places=5)
        self.assertAlmostEqual(rad_max_wind[130], 53.54865787, places=5)
        self.assertAlmostEqual(rad_max_wind[189], 52.62700450, places=5)
        self.assertAlmostEqual(rad_max_wind[190], 54.36738477, places=5)
        self.assertAlmostEqual(rad_max_wind[191], 56.10776504, places=5)
        self.assertAlmostEqual(rad_max_wind[192], 57.84814530, places=5)
        self.assertAlmostEqual(rad_max_wind[200], 70.00942075, places=5)
示例#6
0
def compute_windfields(track, centroids, model, metric="equirect"):
    """Compute 1-minute sustained winds (in m/s) at 10 meters above ground

    In a first step, centroids within reach of the track are determined so that wind fields will
    only be computed and returned for those centroids.

    Parameters
    ----------
    track : xr.Dataset
        Track infomation.
    centroids : 2d np.array
        Each row is a centroid [lat, lon].
        Centroids that are not within reach of the track are ignored.
    model : int
        Holland model selection according to MODEL_VANG.
    metric : str, optional
        Specify an approximation method to use for earth distances: "equirect" (faster) or
        "geosphere" (more accurate). See `dist_approx` function in `climada.util.coordinates`.
        Default: "equirect".

    Returns
    -------
    windfields : np.array of shape (npositions, nreachable, 2)
        Directional wind fields for each track position on those centroids within reach
        of the TC track.
    reachable_centr_idx : np.array of shape (nreachable,)
        List of indices of input centroids within reach of the TC track.
    """
    # copies of track data
    # Note that max wind records are not used in the Holland wind field models!
    t_lat, t_lon, t_tstep, t_rad, t_env, t_cen = [
        track[ar].values.copy() for ar in [
            'lat', 'lon', 'time_step', 'radius_max_wind',
            'environmental_pressure', 'central_pressure'
        ]
    ]

    # start with the assumption that no centroids are within reach
    npositions = t_lat.shape[0]
    reachable_centr_idx = np.zeros((0, ), dtype=np.int64)
    windfields = np.zeros((npositions, 0, 2), dtype=np.float64)

    # the wind field model requires at least two track positions because translational speed
    # as well as the change in pressure are required
    if npositions < 2:
        return windfields, reachable_centr_idx

    # normalize longitude values (improves performance of `dist_approx` and `_close_centroids`)
    mid_lon = 0.5 * sum(u_coord.lon_bounds(t_lon))
    u_coord.lon_normalize(t_lon, center=mid_lon)
    u_coord.lon_normalize(centroids[:, 1], center=mid_lon)

    # restrict to centroids within rectangular bounding boxes around track positions
    track_centr_msk = _close_centroids(t_lat, t_lon, centroids)
    track_centr = centroids[track_centr_msk]
    nreachable = track_centr.shape[0]
    if nreachable == 0:
        return windfields, reachable_centr_idx

    # compute distances and vectors to all centroids
    [d_centr], [v_centr] = u_coord.dist_approx(t_lat[None],
                                               t_lon[None],
                                               track_centr[None, :, 0],
                                               track_centr[None, :, 1],
                                               log=True,
                                               normalize=False,
                                               method=metric)

    # exclude centroids that are too far from or too close to the eye
    close_centr_msk = (d_centr < CENTR_NODE_MAX_DIST_KM) & (d_centr > 1e-2)
    if not np.any(close_centr_msk):
        return windfields, reachable_centr_idx
    v_centr_normed = np.zeros_like(v_centr)
    v_centr_normed[close_centr_msk] = v_centr[close_centr_msk] / d_centr[
        close_centr_msk, None]

    # make sure that central pressure never exceeds environmental pressure
    pres_exceed_msk = (t_cen > t_env)
    t_cen[pres_exceed_msk] = t_env[pres_exceed_msk]

    # extrapolate radius of max wind from pressure if not given
    t_rad[:] = estimate_rmw(t_rad, t_cen) * NM_TO_KM

    # translational speed of track at every node
    v_trans = _vtrans(t_lat, t_lon, t_tstep, metric=metric)
    v_trans_norm = v_trans[0]

    # adjust pressure at previous track point
    prev_pres = t_cen[:-1].copy()
    msk = (prev_pres < 850)
    prev_pres[msk] = t_cen[1:][msk]

    # compute b-value and derive (absolute) angular velocity
    if model == MODEL_VANG['H1980']:
        # convert recorded surface winds to gradient-level winds without translational influence
        t_vmax = track.max_sustained_wind.values.copy()
        t_gradient_winds = np.fmax(
            0, t_vmax - v_trans_norm) / GRADIENT_LEVEL_TO_SURFACE_WINDS
        hol_b = _B_holland_1980(t_gradient_winds[1:], t_env[1:], t_cen[1:])
        v_ang_norm = _stat_holland_1980(d_centr[1:], t_rad[1:], hol_b,
                                        t_env[1:], t_cen[1:], t_lat[1:],
                                        close_centr_msk[1:])
        v_ang_norm *= GRADIENT_LEVEL_TO_SURFACE_WINDS
    elif model == MODEL_VANG['H08']:
        # this model doesn't use the recorded surface winds
        hol_b = _bs_holland_2008(v_trans_norm[1:], t_env[1:], t_cen[1:],
                                 prev_pres, t_lat[1:], t_tstep[1:])
        v_ang_norm = _stat_holland_1980(d_centr[1:], t_rad[1:], hol_b,
                                        t_env[1:], t_cen[1:], t_lat[1:],
                                        close_centr_msk[1:])
    elif model == MODEL_VANG['H10']:
        # this model doesn't use the recorded surface winds
        hol_b = _bs_holland_2008(v_trans_norm[1:], t_env[1:], t_cen[1:],
                                 prev_pres, t_lat[1:], t_tstep[1:])
        t_vmax = _v_max_s_holland_2008(t_env[1:], t_cen[1:], hol_b)
        hol_x = _x_holland_2010(d_centr[1:], t_rad[1:], t_vmax, hol_b,
                                close_centr_msk[1:])
        v_ang_norm = _stat_holland_2010(d_centr[1:], t_vmax, t_rad[1:], hol_b,
                                        close_centr_msk[1:], hol_x)
    else:
        raise NotImplementedError

    # vectorial angular velocity
    hemisphere = 'N'
    if np.count_nonzero(t_lat < 0) > np.count_nonzero(t_lat > 0):
        hemisphere = 'S'
    v_ang_rotate = [1.0, -1.0] if hemisphere == 'N' else [-1.0, 1.0]
    v_ang_dir = np.array(v_ang_rotate)[..., :] * v_centr_normed[1:, :, ::-1]
    v_ang = np.zeros_like(v_ang_dir)
    v_ang[close_centr_msk[1:]] = v_ang_norm[close_centr_msk[1:], None] \
                                 * v_ang_dir[close_centr_msk[1:]]

    # Influence of translational speed decreases with distance from eye.
    # The "absorbing factor" is according to the following paper (see Fig. 7):
    #
    #   Mouton, F., & Nordbeck, O. (1999). Cyclone Database Manager. A tool
    #   for converting point data from cyclone observations into tracks and
    #   wind speed profiles in a GIS. UNED/GRID-Geneva.
    #   https://unepgrid.ch/en/resource/19B7D302
    #
    t_rad_bc = np.broadcast_arrays(t_rad[:, None], d_centr)[0]
    v_trans_corr = np.zeros_like(d_centr)
    v_trans_corr[close_centr_msk] = np.fmin(
        1, t_rad_bc[close_centr_msk] / d_centr[close_centr_msk])

    # add angular and corrected translational velocity vectors
    v_full = v_trans[1][1:, None, :] * v_trans_corr[1:, :, None] + v_ang
    v_full[np.isnan(v_full)] = 0

    windfields = np.zeros((npositions, nreachable, 2), dtype=np.float64)
    windfields[1:, :, :] = v_full
    [reachable_centr_idx] = track_centr_msk.nonzero()
    return windfields, reachable_centr_idx
示例#7
0
def compute_windfields(track, centroids, model):
    """Compute 1-minute sustained winds (in m/s) at 10 meters above ground

    Parameters:
        track (xr.Dataset): track infomation
        centroids (2d np.array): each row is a centroid [lat, lon]
        model (int): Holland model selection according to MODEL_VANG

    Returns:
        np.array
    """
    # copies of track data
    t_lat, t_lon, t_tstep, t_rad, t_env, t_cen = [
        track[ar].values.copy() for ar in [
            'lat', 'lon', 'time_step', 'radius_max_wind',
            'environmental_pressure', 'central_pressure'
        ]
    ]

    ncentroids = centroids.shape[0]
    npositions = t_lat.shape[0]
    windfields = np.zeros((npositions, ncentroids, 2))

    if t_lon.size < 2:
        return windfields

    # never use longitudes at -180 degrees or below
    t_lon[t_lon <= -180] += 360

    # only use longitudes above 180, if 180 degree border is crossed
    if t_lon.min() > 180:
        t_lon -= 360

    # restrict to centroids in rectangular bounding box around track
    track_centr_msk = _close_centroids(t_lat, t_lon, centroids)
    track_centr_idx = track_centr_msk.nonzero()[0]
    track_centr = centroids[track_centr_msk]

    if track_centr.shape[0] == 0:
        return windfields

    # compute distances and vectors to all centroids
    d_centr, v_centr = [
        ar[0] for ar in dist_approx(t_lat[None],
                                    t_lon[None],
                                    track_centr[None, :, 0],
                                    track_centr[None, :, 1],
                                    log=True,
                                    method="geosphere")
    ]

    # exclude centroids that are too far from or too close to the eye
    close_centr = (d_centr < CENTR_NODE_MAX_DIST_KM) & (d_centr > 1e-2)
    if not np.any(close_centr):
        return windfields
    v_centr_normed = np.zeros_like(v_centr)
    v_centr_normed[close_centr] = v_centr[close_centr] / d_centr[close_centr,
                                                                 None]

    # make sure that central pressure never exceeds environmental pressure
    pres_exceed_msk = (t_cen > t_env)
    t_cen[pres_exceed_msk] = t_env[pres_exceed_msk]

    # extrapolate radius of max wind from pressure if not given
    t_rad[:] = estimate_rmw(t_rad, t_cen) * NM_TO_KM

    # translational speed of track at every node
    v_trans = _vtrans(t_lat, t_lon, t_tstep)
    v_trans_norm = v_trans[0]

    # adjust pressure at previous track point
    prev_pres = t_cen[:-1].copy()
    msk = (prev_pres < 850)
    prev_pres[msk] = t_cen[1:][msk]

    # compute b-value
    if model == 0:
        hol_b = _bs_hol08(v_trans_norm[1:], t_env[1:], t_cen[1:], prev_pres,
                          t_lat[1:], t_tstep[1:])
    else:
        raise NotImplementedError

    # derive angular velocity
    v_ang_norm = _stat_holland(d_centr[1:], t_rad[1:], hol_b, t_env[1:],
                               t_cen[1:], t_lat[1:], close_centr[1:])
    hemisphere = 'N'
    if np.count_nonzero(t_lat < 0) > np.count_nonzero(t_lat > 0):
        hemisphere = 'S'
    v_ang_rotate = [1.0, -1.0] if hemisphere == 'N' else [-1.0, 1.0]
    v_ang_dir = np.array(v_ang_rotate)[..., :] * v_centr_normed[1:, :, ::-1]
    v_ang = np.zeros_like(v_ang_dir)
    v_ang[close_centr[1:]] = v_ang_norm[close_centr[1:], None] \
                             * v_ang_dir[close_centr[1:]]

    # Influence of translational speed decreases with distance from eye.
    # The "absorbing factor" is according to the following paper (see Fig. 7):
    #
    #   Mouton, F., & Nordbeck, O. (1999). Cyclone Database Manager. A tool
    #   for converting point data from cyclone observations into tracks and
    #   wind speed profiles in a GIS. UNED/GRID-Geneva.
    #   https://unepgrid.ch/en/resource/19B7D302
    #
    t_rad_bc = np.broadcast_arrays(t_rad[:, None], d_centr)[0]
    v_trans_corr = np.zeros_like(d_centr)
    v_trans_corr[close_centr] = np.fmin(
        1, t_rad_bc[close_centr] / d_centr[close_centr])

    # add angular and corrected translational velocity vectors
    v_full = v_trans[1][1:, None, :] * v_trans_corr[1:, :, None] + v_ang
    v_full[np.isnan(v_full)] = 0

    windfields[1:, track_centr_idx, :] = v_full
    return windfields