Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 74 additions & 7 deletions stormevents/stormevent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import pandas
import xarray
from pandas import Series
from searvey.coops import COOPS_Interval
from searvey.coops import COOPS_Product
from searvey.coops import COOPS_Station
Expand Down Expand Up @@ -49,12 +50,15 @@ def __init__(
year: int,
start_date: datetime = None,
end_date: datetime = None,
synthetic: bool = False,
**kwargs,
):
"""
:param name: storm name
:param year: storm year
:param start_date: starting time
:param end_date: ending time
:param synthetic: whether the storm actually exists; `True` will skip lookup in the storm table

>>> StormEvent('florence', 2018)
StormEvent(name='FLORENCE', year=2018, start_date=Timestamp('2018-08-30 06:00:00'), end_date=Timestamp('2018-09-18 12:00:00'))
Expand All @@ -72,17 +76,30 @@ def __init__(
StormEvent(name='IDA', year=2021, start_date=Timestamp('2021-08-27 18:00:00'), end_date=Timestamp('2021-08-29 18:00:00'))
"""

storms = nhc_storms(year=year)
storms = storms[storms["name"].str.contains(name.upper())]
if len(storms) > 0:
self.__entry = storms.iloc[0]
if not synthetic:
storms = nhc_storms(year=year)
storms = storms[storms["name"].str.contains(name.upper())]
if len(storms) > 0:
self.__entry = storms.iloc[0]
else:
raise ValueError(f'storm "{name} {year}" not found in NHC database')
else:
raise ValueError(f'storm "{name} {year}" not found in NHC database')
self.__entry = Series(
{
"name": name,
"year": year,
"start_date": start_date,
"end_date": end_date,
**kwargs,
},
index=None,
)

self.__usgs_id = None
self.__is_usgs_flood_event = True
self.__high_water_marks = None
self.__previous_configuration = {"name": self.name, "year": self.year}
self.__synthetic = synthetic

self.start_date = start_date
self.end_date = end_date
Expand Down Expand Up @@ -227,7 +244,7 @@ def start_date(self, start_date: datetime):
@lru_cache(maxsize=None)
def __data_start(self) -> datetime:
data_start = self.__entry["start_date"]
if pandas.isna(data_start):
if pandas.isna(data_start) and not self.synthetic:
data_start = VortexTrack.from_storm_name(self.name, self.year).start_date
return data_start

Expand All @@ -252,7 +269,7 @@ def end_date(self, end_date: datetime):
@lru_cache(maxsize=None)
def __data_end(self) -> datetime:
data_end = self.__entry["end_date"]
if pandas.isna(data_end):
if pandas.isna(data_end) and not self.synthetic:
data_end = VortexTrack.from_storm_name(self.name, self.year).end_date
return data_end

Expand All @@ -268,6 +285,10 @@ def status(self) -> StormStatus:
else:
return StormStatus.HISTORICAL

@property
def synthetic(self) -> bool:
return self.__synthetic

def track(
self,
start_date: datetime = None,
Expand Down Expand Up @@ -510,3 +531,49 @@ def __repr__(self) -> str:
f"end_date={repr(self.end_date)}"
f")"
)

def __copy__(self) -> "StormEvent":
return self.__class__(
self.name,
year=self.year,
start_date=self.start_date,
end_date=self.end_date,
)

def perturb(
self,
name: str = None,
year: int = None,
start_date: datetime = None,
end_date: datetime = None,
**kwargs,
) -> "StormEvent":
"""
:param name: storm name
:param year: storm year
:param start_date: starting time
:param end_date: ending time
:return: a new synthetic storm based on parameters from the current storm
"""

if name is None:
name = self.name
if year is None:
year = self.year
if start_date is None:
start_date = self.start_date
elif isinstance(start_date, timedelta):
start_date = self.start_date + start_date
if end_date is None:
end_date = self.end_date
elif isinstance(end_date, timedelta):
end_date = self.end_date + end_date

return self.__class__(
name=name,
year=year,
start_date=start_date,
end_date=end_date,
synthetic=True,
**kwargs,
)
32 changes: 32 additions & 0 deletions tests/test_stormevent.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,38 @@ def test_storm_event_lookup():
assert ida2021.end_date == datetime(2021, 9, 4, 18)


def test_synthetic_stormevent(florence2018):
synth_1 = StormEvent("synth_1", year=2018, synthetic=True)
synth_2 = StormEvent(
"synth_2",
start_date=datetime(2019, 10, 2),
end_date=datetime(2019, 10, 10),
year=2019,
synthetic=True,
)
synth_florence = florence2018.perturb(name="synth_florence")

assert synth_1.name == "synth_1"
assert synth_1.year == 2018
assert synth_1.nhc_code is None
assert synth_1.start_date is None
assert synth_1.end_date is None
assert synth_1.synthetic

assert synth_2.name == "synth_2"
assert synth_2.year == 2019
assert synth_2.nhc_code is None
assert synth_2.start_date == datetime(2019, 10, 2)
assert synth_2.end_date == datetime(2019, 10, 10)
assert synth_2.synthetic

assert synth_florence.name == "synth_florence"
assert synth_florence.year == 2018
assert synth_florence.start_date == florence2018.start_date
assert synth_florence.end_date == florence2018.end_date
assert synth_florence.synthetic


def test_storm_event_time_interval():
florence2018 = StormEvent("florence", 2018, start_date=timedelta(days=-2))
paine2016 = StormEvent.from_nhc_code("EP172016", end_date=timedelta(days=1))
Expand Down