Skip to content

Commit

Permalink
Feature/test file lock (#652)
Browse files Browse the repository at this point in the history
* hazard.from_raster_xarray: close opened dataset at last

* hazard.test_base_xarray: close datasets
use setUpClass and tearDownClass for testfiles

* CHANGELOG: added `from_raster_xarray`

* hazard.Hazard.from_raster_xarray: idiomatic use of `with open`

* Have two Hazard classmethods for reading xarray Dataset and file

Split the existing classmethod into two methods 'from_xarray_raster' and
'from_xarray_raster_file'. One first loads data from a dataset, the
second opens a file and then calls the first.

* Split classmethods.
* Update unit tests.
* Update docstrings.

* Improve tests for Hazard.from_xarray_raster(_file)

* Use temporary directory for storing data instead of local directory.
* Consistently open data files in context managers.

* Fix linter issue "no-else-raise"

* Update CHANGELOG.md

---------

Co-authored-by: Lukas Riedel <[email protected]>
  • Loading branch information
emanuel-schmid and peanutfun authored Feb 17, 2023
1 parent 8317442 commit 11a707b
Show file tree
Hide file tree
Showing 3 changed files with 341 additions and 238 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ updated:

### Added

- `climada.engine.impact.Impact` objects have new methods `from_hdf5` and `write_hdf5` for reading their data from, and writing it to, H5 files [#606](https://github.com/CLIMADA-project/climada_python/pull/606).
- `climada.hazard.Hazard.from_xarray_raster(_file)` class methods for reading `Hazard` objects from an `xarray.Dataset`, or from a file that can be read by `xarray`.
[#507](https://github.com/CLIMADA-project/climada_python/pull/507),
[#589](https://github.com/CLIMADA-project/climada_python/pull/589),
[#652](https://github.com/CLIMADA-project/climada_python/pull/652).
- `climada.engine.impact.Impact` objects have new methods `from_hdf5` and `write_hdf5` for reading their data from, and writing it to, H5 files [#606](https://github.com/CLIMADA-project/climada_python/pull/606)
- `climada.engine.impact.Impact` objects has a new class method `concat` for concatenation of impacts based on the same exposures [#529](https://github.com/CLIMADA-project/climada_python/pull/529).
- `climada.engine.impact_calc`: this module was separated from `climada.engine.impact` and contains the code that dealing with impact _calculation_ while the latter focuses on impact _data_ [#560](https://github.com/CLIMADA-project/climada_python/pull/560).
- The classes `Hazard`, `Impact` and `ImpactFreqCurve` have a novel attribute `frequency_unit`. Before it was implicitly set to annual, now it can be specified and accordingly displayed in plots.
Expand Down
86 changes: 68 additions & 18 deletions climada/hazard/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,52 @@ def set_vector(self, *args, **kwargs):
self.__dict__ = Hazard.from_vector(*args, **kwargs).__dict__

@classmethod
def from_raster_xarray(
def from_xarray_raster_file(
cls, filepath: Union[pathlib.Path, str], *args, **kwargs
):
"""Read raster-like data from a file that can be loaded with xarray
This wraps :py:meth:`~Hazard.from_xarray_raster` by first opening the target file
as xarray dataset and then passing it to that classmethod. Use this wrapper as a
simple alternative to opening the file yourself. The signature is exactly the
same, except for the first argument, which is replaced by a file path here.
Additional (keyword) arguments are passed to
:py:meth:`~Hazard.from_xarray_raster`.
Parameters
----------
filepath : Path or str
Path of the file to read with xarray. May be any file type supported by
xarray. See https://docs.xarray.dev/en/stable/user-guide/io.html
Returns
-------
hazard : climada.Hazard
A hazard object created from the input data
Examples
--------
>>> hazard = Hazard.from_xarray_raster_file("path/to/file.nc", "", "")
Notes
-----
If you have specific requirements for opening a data file, prefer opening it
yourself and using :py:meth:`~Hazard.from_xarray_raster`, following this pattern:
>>> open_kwargs = dict(engine="h5netcdf", chunks=dict(x=-1, y="auto"))
>>> with xarray.open_dataset("path/to/file.nc", **open_kwargs) as dset:
... hazard = Hazard.from_xarray_raster(dset, "", "")
"""
with xr.open_dataset(filepath, chunks="auto") as dset:
return cls.from_xarray_raster(dset, *args, **kwargs)

@classmethod
def from_xarray_raster(
cls,
data: Union[xr.Dataset, str, pathlib.Path],
data: xr.Dataset,
hazard_type: str,
intensity_unit: str,
*,
Expand All @@ -422,7 +465,7 @@ def from_raster_xarray(
crs: str = DEF_CRS,
rechunk: bool = False,
):
"""Read raster-like data from an xarray Dataset or a raster data file
"""Read raster-like data from an xarray Dataset
This method reads data that can be interpreted using three coordinates for event,
latitude, and longitude. The data and the coordinates themselves may be organized
Expand All @@ -443,12 +486,13 @@ def from_raster_xarray(
meaning that the object can be used in all CLIMADA operations without throwing
an error due to missing data or faulty data types.
Use :py:meth:`~Hazard.from_xarray_raster_file` to open a file on disk
and load the resulting dataset with this method in one step.
Parameters
----------
data : xarray.Dataset or str
The data to read from. May be an opened dataset or a path to a raster data
file, in which case the file is opened first. Works with any file format
supported by ``xarray``.
data : xarray.Dataset
The dataset to read from.
hazard_type : str
The type identifier of the hazard. Will be stored directly in the hazard
object.
Expand Down Expand Up @@ -499,6 +543,11 @@ def from_raster_xarray(
hazard : climada.Hazard
A hazard object created from the input data
See Also
--------
:py:meth:`~Hazard.from_xarray_raster_file`
Use this method if you want CLIMADA to open and read a file on disk for you.
Notes
-----
* Single-valued coordinates given by ``coordinate_vars``, that are not proper
Expand Down Expand Up @@ -534,7 +583,7 @@ def from_raster_xarray(
... longitude=[0, 1, 2],
... ),
... )
>>> hazard = Hazard.from_raster_xarray(dset, "", "")
>>> hazard = Hazard.from_xarray_raster(dset, "", "")
For non-default coordinate names, use the ``coordinate_vars`` argument.
Expand All @@ -551,7 +600,7 @@ def from_raster_xarray(
... longitude=[0, 1, 2],
... ),
... )
>>> hazard = Hazard.from_raster_xarray(
>>> hazard = Hazard.from_xarray_raster(
... dset, "", "", coordinate_vars=dict(event="day", latitude="lat")
... )
Expand All @@ -568,7 +617,7 @@ def from_raster_xarray(
... latitude=(["y", "x"], [[0.0, 0.0, 0.0], [0.1, 0.1, 0.1]]),
... ),
... )
>>> hazard = Hazard.from_raster_xarray(dset, "", "")
>>> hazard = Hazard.from_xarray_raster(dset, "", "")
Optional data is read from the dataset if the default keys are found. Users can
specify custom variables in the data, or that the default keys should be ignored,
Expand All @@ -593,7 +642,7 @@ def from_raster_xarray(
... longitude=[0, 1, 2],
... ),
... )
>>> hazard = Hazard.from_raster_xarray(
>>> hazard = Hazard.from_xarray_raster(
... dset,
... "",
... "",
Expand Down Expand Up @@ -627,7 +676,7 @@ def from_raster_xarray(
... longitude=[0, 1, 2],
... ),
... )
>>> hazard = Hazard.from_raster_xarray(dset, "", "") # Same as first example
>>> hazard = Hazard.from_xarray_raster(dset, "", "") # Same as first example
If one coordinate is missing altogehter, you must add it or expand the dimensions
before loading the dataset:
Expand All @@ -645,14 +694,15 @@ def from_raster_xarray(
... ),
... )
>>> dset = dset.expand_dims(time=[numpy.datetime64("2000-01-01")])
>>> hazard = Hazard.from_raster_xarray(dset, "", "")
>>> hazard = Hazard.from_xarray_raster(dset, "", "")
"""
# If the data is a string, open the respective file
# Check data type for better error message
if not isinstance(data, xr.Dataset):
LOGGER.info("Loading Hazard from file: %s", data)
data: xr.Dataset = xr.open_dataset(data, chunks="auto")
else:
LOGGER.info("Loading Hazard from xarray Dataset")
if isinstance(data, (pathlib.Path, str)):
raise TypeError("Passing a path to this classmethod is not supported. "
"Use Hazard.from_xarray_raster_file instead.")

raise TypeError("This method only supports xarray.Dataset as input data")

# Initialize Hazard object
hazard_kwargs = dict(haz_type=hazard_type, units=intensity_unit)
Expand Down
Loading

0 comments on commit 11a707b

Please sign in to comment.