Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
71 changes: 71 additions & 0 deletions climada/hazard/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,74 @@

Define Forecast variant of Hazard.
"""

import logging

import numpy as np

from climada.hazard.base import Hazard
from climada.util.forecast import Forecast

LOGGER = logging.getLogger(__name__)


class HazardForecast(Forecast, Hazard):
"""A hazard object with forecast information"""

def __init__(
self,
lead_time: np.ndarray | None = None,
member: np.ndarray | None = None,
**hazard_kwargs,
):
"""
Initialize a HazardForecast object.

Parameters
----------
lead_time : np.ndarray of np.timedelta64 or None, optional
Forecast lead times. Default is empty array.
member : np.ndarray or None, optional
Ensemble member identifiers as integers. Default is empty array.
**hazard_kwargs
keyword arguments to pass to :py:class:`~climada.hazard.base.Hazard` See
py:meth`~climada.hazard.base.Hazard.__init__` for details.
"""
super().__init__(lead_time=lead_time, member=member, **hazard_kwargs)

@classmethod
def from_hazard(cls, hazard: Hazard, lead_time: np.ndarray, member: np.ndarray):
"""
Create a HazardForecast object from a Hazard object.

Parameters
----------
hazard : climada.hazard.base.Hazard
Hazard object to convert into a HazardForecast.
lead_time : np.ndarray of np.timedelta64 or None, optional
Forecast lead times. Default is empty array.
member : np.ndarray or None, optional
Ensemble member identifiers as integers. Default is empty array.

Returns
-------
HazardForecast
A HazardForecast object with the same attributes as the input hazard,
but with lead_time and member attributes set from instance of HazardForecast.
"""
return cls(
lead_time=lead_time,
member=member,
haz_type=hazard.haz_type,
pool=hazard.pool,
units=hazard.units,
centroids=hazard.centroids,
event_id=hazard.event_id,
frequency=hazard.frequency,
frequency_unit=hazard.frequency_unit,
event_name=hazard.event_name,
date=hazard.date,
orig=hazard.orig,
intensity=hazard.intensity,
fraction=hazard.fraction,
)
42 changes: 22 additions & 20 deletions climada/hazard/test/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,29 @@
"""


def hazard_kwargs():
return {
"haz_type": "TC",
"pool": None,
"intensity": sparse.csr_matrix(
[[0.2, 0.3, 0.4], [0.1, 0.1, 0.01], [4.3, 2.1, 1.0], [5.3, 0.2, 0.0]]
),
"fraction": sparse.csr_matrix(
[[0.02, 0.03, 0.04], [0.01, 0.01, 0.01], [0.3, 0.1, 0.0], [0.3, 0.2, 0.0]]
),
"centroids": Centroids(lat=np.array([1, 3, 5]), lon=np.array([2, 4, 6])),
"event_id": np.array([1, 2, 3, 4]),
"event_name": ["ev1", "ev2", "ev3", "ev4"],
"date": np.array([1, 2, 3, 4]),
"orig": np.array([True, False, False, True]),
"frequency": np.array([0.1, 0.5, 0.5, 0.2]),
"frequency_unit": "1/week",
"units": "m/s",
}


def dummy_hazard():
fraction = sparse.csr_matrix(
[[0.02, 0.03, 0.04], [0.01, 0.01, 0.01], [0.3, 0.1, 0.0], [0.3, 0.2, 0.0]]
)
intensity = sparse.csr_matrix(
[[0.2, 0.3, 0.4], [0.1, 0.1, 0.01], [4.3, 2.1, 1.0], [5.3, 0.2, 0.0]]
)

return Hazard(
"TC",
intensity=intensity,
fraction=fraction,
centroids=Centroids(lat=np.array([1, 3, 5]), lon=np.array([2, 4, 6])),
event_id=np.array([1, 2, 3, 4]),
event_name=["ev1", "ev2", "ev3", "ev4"],
date=np.array([1, 2, 3, 4]),
orig=np.array([True, False, False, True]),
frequency=np.array([0.1, 0.5, 0.5, 0.2]),
frequency_unit="1/week",
units="m/s",
)
return Hazard(**hazard_kwargs())


class TestLoader(unittest.TestCase):
Expand Down
68 changes: 56 additions & 12 deletions climada/hazard/test/test_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,72 @@
Tests for Hazard Forecast.
"""

import numpy as np
import numpy.testing as npt
import pandas as pd
import pytest
from scipy.sparse import csr_matrix

from climada.hazard import Hazard
from climada.hazard.base import Hazard
from climada.hazard.forecast import HazardForecast
from climada.hazard.test.test_base import hazard_kwargs

# --- Examples for fixtures and test organization --- #


@pytest.fixture
def hazard():
return Hazard()
def haz_kwargs():
return hazard_kwargs()


def test_empty_hazard(hazard):
assert hazard.size == 0
assert hazard.haz_type == ""
@pytest.fixture
def hazard(haz_kwargs):
return Hazard(**haz_kwargs)


@pytest.fixture
def lead_time():
return pd.timedelta_range("1h", periods=6).to_numpy()


class TestSomething:
@pytest.fixture
def member():
return np.arange(6)

@pytest.fixture(autouse=True)
def haz_type(self, hazard):
hazard.haz_type = "foo"

def test_haz_type(self, hazard):
assert hazard.haz_type == "foo"
@pytest.fixture
def haz_fc(lead_time, member, haz_kwargs):
return HazardForecast(
lead_time=lead_time,
member=member,
**haz_kwargs,
)


def assert_hazard_kwargs(hazard: Hazard, **kwargs):
for key, value in kwargs.items():
attr = getattr(hazard, key)
if isinstance(value, (np.ndarray, list)):
npt.assert_array_equal(attr, value)
elif isinstance(value, csr_matrix):
npt.assert_array_equal(attr.todense(), value.todense())
else:
assert attr == value


def test_init_hazard_forecast(haz_fc, member, lead_time, haz_kwargs):
assert isinstance(haz_fc, HazardForecast)
npt.assert_array_equal(haz_fc.lead_time, lead_time)
assert haz_fc.lead_time.dtype == lead_time.dtype
npt.assert_array_equal(haz_fc.member, member)
assert_hazard_kwargs(haz_fc, **haz_kwargs)


def test_from_hazard(lead_time, member, hazard, haz_kwargs):
haz_fc_from_haz = HazardForecast.from_hazard(
hazard, lead_time=lead_time, member=member
)
assert isinstance(haz_fc_from_haz, HazardForecast)
npt.assert_array_equal(haz_fc_from_haz.lead_time, lead_time)
npt.assert_array_equal(haz_fc_from_haz.member, member)
assert_hazard_kwargs(haz_fc_from_haz, **haz_kwargs)
Loading