Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/atcf class #24

Merged
9 commits merged into from
Jun 15, 2021
72 changes: 32 additions & 40 deletions ensembleperturbation/perturbation/make_storm_ensemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
from pyproj.enums import TransformDirection
from shapely.geometry import LineString

from ensembleperturbation.tropicalcyclone.atcf import BestTrackForcing
from ensembleperturbation.tropicalcyclone.atcf import VortexForcing
from ensembleperturbation.utilities import units

AIR_DENSITY = 1.15 * units.kilogram / units.meters ** 3
Expand All @@ -71,7 +71,7 @@ class PerturbationType(Enum):
LINEAR = 'linear'


class BestTrackPerturbedVariable(ABC):
class VortexPerturbedVariable(ABC):
name: str
perturbation_type: PerturbationType

Expand Down Expand Up @@ -188,34 +188,34 @@ def default(self, default: float):
self.__default = default

def perturb(
self, besttrack_dataframe: DataFrame, values: [float], times: [datetime],
self, vortex_dataframe: DataFrame, values: [float], times: [datetime],
) -> DataFrame:
"""
perturb the variable within physical bounds

:param besttrack_dataframe: ATCF dataframe containing track info
:param vortex_dataframe: ATCF dataframe containing track info
:param values: values for each forecast time (VT)
:param times: forecast times (VT)
:return: updated ATCF dataframe with perturbed values
"""

all_values = besttrack_dataframe[self.name].values + values
besttrack_dataframe[self.name] = [
all_values = vortex_dataframe[self.name].values + values
vortex_dataframe[self.name] = [
min(self.upper_bound, max(value, self.lower_bound)).magnitude
for value in all_values
] * self.unit

return besttrack_dataframe
return vortex_dataframe

def __repr__(self) -> str:
return f'{self.__class__.__name__}(lower_bound={repr(self.lower_bound)}, upper_bound={repr(self.upper_bound)}, historical_forecast_errors={repr(self.historical_forecast_errors)}, default={repr(self.default)}, unit={repr(self.unit)})'


class CentralPressure(BestTrackPerturbedVariable):
class CentralPressure(VortexPerturbedVariable):
name = 'central_pressure'


class BackgroundPressure(BestTrackPerturbedVariable):
class BackgroundPressure(VortexPerturbedVariable):
name = 'background_pressure'

def __init__(self):
Expand All @@ -224,7 +224,7 @@ def __init__(self):
)


class MaximumSustainedWindSpeed(BestTrackPerturbedVariable):
class MaximumSustainedWindSpeed(VortexPerturbedVariable):
name = 'max_sustained_wind_speed'
perturbation_type = PerturbationType.GAUSSIAN

Expand Down Expand Up @@ -261,7 +261,7 @@ def __init__(self):
)


class RadiusOfMaximumWinds(BestTrackPerturbedVariable):
class RadiusOfMaximumWinds(VortexPerturbedVariable):
name = 'radius_of_maximum_winds'
perturbation_type = PerturbationType.LINEAR

Expand Down Expand Up @@ -361,7 +361,7 @@ def __init__(self):
)


class CrossTrack(BestTrackPerturbedVariable):
class CrossTrack(VortexPerturbedVariable):
name = 'cross_track'
perturbation_type = PerturbationType.GAUSSIAN

Expand Down Expand Up @@ -398,20 +398,20 @@ def __init__(self):
)

def perturb(
self, besttrack_dataframe: DataFrame, values: [float], times: [datetime],
self, vortex_dataframe: DataFrame, values: [float], times: [datetime],
) -> DataFrame:
"""
offset_track(df_,VT,cross_track_errors)
- Offsets points by a given perpendicular error/distance from the original track

:param besttrack_dataframe: ATCF dataframe containing track info
:param vortex_dataframe: ATCF dataframe containing track info
:param values: cross-track errors [nm] for each forecast time (VT)
:param times: forecast times (VT)
:return: updated ATCF dataframe with different longitude latitude locations based on perpendicular offsets set by the cross_track_errors
"""

# Get the coordinates of the track
track_coords = besttrack_dataframe[['longitude', 'latitude']].values.tolist()
track_coords = vortex_dataframe[['longitude', 'latitude']].values.tolist()

# get the utm projection for the reference coordinate
utm_crs = utm_crs_from_longitude(numpy.mean(track_coords[0]))
Expand Down Expand Up @@ -485,20 +485,16 @@ def perturb(
)

degree = PintType(units.degree)
besttrack_dataframe['longitude'], besttrack_dataframe['latitude'] = zip(
*new_coordinates
)
besttrack_dataframe['longitude'] = besttrack_dataframe['longitude'].astype(
degree, copy=False
)
besttrack_dataframe['latitude'] = besttrack_dataframe['latitude'].astype(
vortex_dataframe['longitude'], vortex_dataframe['latitude'] = zip(*new_coordinates)
vortex_dataframe['longitude'] = vortex_dataframe['longitude'].astype(
degree, copy=False
)
vortex_dataframe['latitude'] = vortex_dataframe['latitude'].astype(degree, copy=False)

return besttrack_dataframe
return vortex_dataframe


class AlongTrack(BestTrackPerturbedVariable):
class AlongTrack(VortexPerturbedVariable):
name = 'along_track'
perturbation_type = PerturbationType.GAUSSIAN

Expand Down Expand Up @@ -535,13 +531,13 @@ def __init__(self):
)

def perturb(
self, besttrack_dataframe: DataFrame, values: [float], times: [datetime],
self, vortex_dataframe: DataFrame, values: [float], times: [datetime],
) -> DataFrame:
"""
interpolate_along_track(df_,VT,along_track_errros)
Offsets points by a given error/distance by interpolating along the track

:param besttrack_dataframe: ATCF dataframe containing track info
:param vortex_dataframe: ATCF dataframe containing track info
:param values: along-track errors for each forecast time (VT)
:param times: forecast timed (VT)
:return: updated ATCF dataframe with different longitude latitude locations based on interpolated errors along track
Expand All @@ -550,7 +546,7 @@ def perturb(
max_interpolated_points = 5 # maximum number of pts along line for each interpolation

# Get the coordinates of the track
coordinates = besttrack_dataframe[['longitude', 'latitude']].values
coordinates = vortex_dataframe[['longitude', 'latitude']].values

# get the utm projection for the reference coordinate
utm_crs = utm_crs_from_longitude(numpy.mean(coordinates[:, 0]))
Expand Down Expand Up @@ -627,20 +623,16 @@ def perturb(
)

degree = PintType(units.degree)
besttrack_dataframe['longitude'], besttrack_dataframe['latitude'] = zip(
*new_coordinates
)
besttrack_dataframe['longitude'] = besttrack_dataframe['longitude'].astype(
degree, copy=False
)
besttrack_dataframe['latitude'] = besttrack_dataframe['latitude'].astype(
vortex_dataframe['longitude'], vortex_dataframe['latitude'] = zip(*new_coordinates)
vortex_dataframe['longitude'] = vortex_dataframe['longitude'].astype(
degree, copy=False
)
vortex_dataframe['latitude'] = vortex_dataframe['latitude'].astype(degree, copy=False)

return besttrack_dataframe
return vortex_dataframe


class BestTrackPerturber:
class VortexPerturber:
def __init__(
self, storm: str, start_date: datetime = None, end_date: datetime = None,
):
Expand Down Expand Up @@ -688,7 +680,7 @@ def end_date(self, end_date: datetime):
self.__end_date = end_date

@property
def forcing(self) -> BestTrackForcing:
def forcing(self) -> VortexForcing:

configuration = {
'storm': self.storm,
Expand All @@ -697,7 +689,7 @@ def forcing(self) -> BestTrackForcing:
}

if configuration != self.__previous_configuration:
self.__forcing = BestTrackForcing(**configuration)
self.__forcing = VortexForcing(**configuration)
self.__previous_configuration = configuration

self.__storm = self.__forcing.storm_id
Expand All @@ -707,7 +699,7 @@ def forcing(self) -> BestTrackForcing:
def write(
self,
number_of_perturbations: int,
variables: [BestTrackPerturbedVariable],
variables: [VortexPerturbedVariable],
directory: PathLike = None,
alpha: float = None,
):
Expand Down Expand Up @@ -979,7 +971,7 @@ def get_offset(x1: float, y1: float, x2: float, y2: float, d: float) -> (float,
CrossTrack,
]

perturber = BestTrackPerturber(
perturber = VortexPerturber(
storm=arguments.storm_code,
start_date=arguments.start_date,
end_date=arguments.end_date,
Expand Down
Loading