Skip to content

Commit

Permalink
Merge pull request #45 from Daafip/dev
Browse files Browse the repository at this point in the history
Adding makking forcing
  • Loading branch information
Daafip committed Mar 29, 2024
2 parents e070ede + a669e26 commit 3748173
Show file tree
Hide file tree
Showing 15 changed files with 82 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ __pycache__/
.Python
build/
develop-eggs/
# dist/
dist/
downloads/
eggs/
.eggs/
Expand Down
32 changes: 0 additions & 32 deletions .readthedocs.yaml

This file was deleted.

4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ Adding `.finalize()` method - clears up the directory. Especially useful for DA.
#### 1.5.1
- fix bug/implementation error with time indexing, docker image version 1.3.1
#### 1.5.2
- typo in bmi implementation: docker image 1.3.2
- typo in bmi implementation: docker image 1.3.2
#### 1.6.0
- now compatible with ewatercycle V2.1 `LumpedMakkinkForcing` which generates evaporation from era5/CMIP.
Binary file removed dist/ewatercycle_hbv-1.4.1-py2.py3-none-any.whl
Binary file not shown.
Binary file removed dist/ewatercycle_hbv-1.4.1.tar.gz
Binary file not shown.
Binary file removed dist/ewatercycle_hbv-1.4.2-py2.py3-none-any.whl
Binary file not shown.
Binary file removed dist/ewatercycle_hbv-1.4.2.tar.gz
Binary file not shown.
Binary file removed dist/ewatercycle_hbv-1.5.0-py2.py3-none-any.whl
Binary file not shown.
Binary file removed dist/ewatercycle_hbv-1.5.0.tar.gz
Binary file not shown.
Binary file removed dist/ewatercycle_hbv-1.5.1-py2.py3-none-any.whl
Binary file not shown.
Binary file removed dist/ewatercycle_hbv-1.5.1.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name = "ewatercycle-HBV"
description = "Implementation of HBV for eWaterCycle"
readme = "README.md"
license = "Apache-2.0"
version = "1.5.2"
version = "1.6.0"
authors = [
{ name = "David Haasnoot", email = "[email protected]" },
]
Expand Down
2 changes: 1 addition & 1 deletion src/ewatercycle_HBV/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.5.2"
__version__ = "1.6.0"
12 changes: 8 additions & 4 deletions src/ewatercycle_HBV/forcing.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def from_test_txt(self) -> xr.Dataset:
Dataset with forcing data.
"""
if self.directory is None or self.camels_file is None:
raise ValueError("Directory or camels_file is not set")
self.file_not_found_error()
fn = self.directory / self.camels_file
forcing = np.loadtxt(fn, delimiter=" ")
names = ["year", "month", "day", "pr","Q", "pev"]
Expand Down Expand Up @@ -144,7 +144,7 @@ def from_camels_txt(self) -> xr.Dataset:
Dataset with forcing data.
"""
if self.directory is None or self.camels_file is None:
raise ValueError("Directory or camels_file is not set")
self.file_not_found_error()
fn = self.directory / self.camels_file
data = {}
with open(fn, 'r') as fin:
Expand Down Expand Up @@ -206,8 +206,9 @@ def from_camels_txt(self) -> xr.Dataset:
return ds

def from_external_source(self):
"""Runs checks on externally provided forcing"""
if None in [self.directory, self.pr, self.pev]:
raise ValueError("Directory or camels_file is not set")
self.file_not_found_error()

# often same file
if self.pr == self.pev:
Expand All @@ -228,7 +229,7 @@ def from_external_source(self):
ds_pr = xr.open_dataset(self.directory / self.pr)
ds_pev = xr.open_dataset(self.directory / self.pev)
combined_data_vars = list(ds_pr.data_vars) + list(ds_pev.data_vars)
if not sum([param in combined_data_vars for param in REQUIRED_PARAMS]) == len(REQUIRED_PARAMS):
if sum([param in combined_data_vars for param in REQUIRED_PARAMS]) != len(REQUIRED_PARAMS):
raise UserWarning(f"Supplied NetCDF files must contain {REQUIRED_PARAMS} respectively")

ds_pr, ds_name_pr = self.crop_ds(ds_pr, "external")
Expand All @@ -253,6 +254,9 @@ def crop_ds(self, ds: xr.Dataset, name: str):

return ds, ds_name

def file_not_found_error(self):
raise ValueError("Directory, camels_file or pr & pev values is not set correctly")

def calc_pet(s_rad, t_min, t_max, doy, alpha, elev, lat) -> np.ndarray:
"""Calculates Potential Evaporation using Priestly–Taylor PET estimate, callibrated with longterm P-T trends from the camels data set (alpha).
Expand Down
98 changes: 68 additions & 30 deletions src/ewatercycle_HBV/model.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""eWaterCycle wrapper for the HBV model."""
import json
import os.path
import xarray as xr
import warnings
from collections.abc import ItemsView
from pathlib import Path
from typing import Any, Type

from ewatercycle.base.forcing import GenericLumpedForcing # or later Use custom forcing instead?
from ewatercycle.forcing import LumpedMakkinkForcing
from ewatercycle.forcing import GenericLumpedForcing

from ewatercycle_HBV.forcing import HBVForcing # Use custom forcing instead
from ewatercycle.base.model import ContainerizedModel, eWaterCycleModel
from ewatercycle.container import ContainerImage
Expand All @@ -32,10 +34,9 @@
class HBVMethods(eWaterCycleModel):
"""
The eWatercycle HBV model.
"""
forcing: HBVForcing # The model requires forcing.

forcing: LumpedMakkinkForcing|HBVForcing|GenericLumpedForcing # The model requires forcing.
parameter_set: None # The model has no parameter set.

_config: dict = {
Expand All @@ -49,22 +50,52 @@ def _make_cfg_file(self, **kwargs) -> Path:
"""Write model configuration file."""

# do some basic test to check on forcing
if self.forcing.test_data_bool:
self.forcing.from_test_txt()
elif self.forcing.camels_txt_defined():
self.forcing.from_camels_txt()
elif self.forcing.forcing_nc_defined():
self.forcing.from_external_source()
else:
raise UserWarning("Ensure either a txt file with camels data or an(/set of) xarrays is defined")

self._config["precipitation_file"] = str(
self.forcing.directory / self.forcing.pr
)

self._config["potential_evaporation_file"] = str(
self.forcing.directory / self.forcing.pev
)
if type(self.forcing).__name__ == 'HBVForcing':
if self.forcing.test_data_bool:
self.forcing.from_test_txt()
elif self.forcing.camels_txt_defined():
self.forcing.from_camels_txt()
elif self.forcing.forcing_nc_defined():
self.forcing.from_external_source()
else:
raise UserWarning("Ensure either a txt file with camels data or an(/set of) xarrays is defined")

self._config["precipitation_file"] = str(
self.forcing.directory / self.forcing.pr
)

self._config["potential_evaporation_file"] = str(
self.forcing.directory / self.forcing.pev
)

elif type(self.forcing).__name__ == 'GenericLumpedForcing':
raise UserWarning("Generic Lumped Forcing does not provide potential evaporation, which this model needs")

elif type(self.forcing).__name__ == 'LumpedMakkinkForcing':
ds = xr.open_dataset(self.forcing.directory / self.forcing.filenames['evspsblpot'])
attributes = ds['evspsblpot'].attrs
attributes['units'] = 'mm'
ds = ds.rename({'evspsblpot': 'pev'})
ds['pev'] = ds['pev'] * 86400
ds['pev'].attrs = attributes
temporary_pev_file = self.forcing.directory / self.forcing.filenames['evspsblpot'].replace('evspsblpot', 'pev_mm')
ds.to_netcdf(temporary_pev_file)

ds = xr.open_dataset(self.forcing.directory / self.forcing.filenames['pr'])
attributes = ds['pr'].attrs
attributes['units'] = 'mm'
ds['pr'] = ds['pr'] * 86400
ds['pr'].attrs = attributes
temporary_pr_file = self.forcing.directory / self.forcing.filenames['pr'].replace('pr', 'pr_mm')
ds.to_netcdf(temporary_pr_file)

self._config["precipitation_file"] = str(
temporary_pr_file
)
self._config["potential_evaporation_file"] = str(
temporary_pev_file
)

## possibly add later for snow?
# self._config["temperature_file"] = str(
# self.forcing.directory / self.forcing.tas
Expand Down Expand Up @@ -160,15 +191,22 @@ def finalize(self) -> None:
except FileNotFoundError:
warnings.warn(message=f'Config folder not found at {self._cfg_dir.rmdir()}',category=UserWarning)


# NetCDF files created are timestamped and running them a lot creates many files, remove these
if self.forcing.camels_txt_defined() or self.forcing.test_data_bool:
for file in ["potential_evaporation_file", "precipitation_file"]:
path = self.forcing.directory / self._config[file]
if path.is_file(): # often both with be the same, e.g. with camels data.
path.unlink()
else:
pass
if type(self.forcing).__name__ == 'HBVForcing':
# NetCDF files created are timestamped and running them a lot creates many files, remove these
if self.forcing.camels_txt_defined() or self.forcing.test_data_bool:
self.unlink()

elif type(self.forcing).__name__ == 'LumpedMakkinkForcing':
# we created a temporary file so let's unlink that
self.unlink()

def unlink(self):
for file in ["potential_evaporation_file", "precipitation_file"]:
path = self.forcing.directory / self._config[file]
if path.is_file(): # often both with be the same, e.g. with camels data.
path.unlink()
else:
pass

class HBV(ContainerizedModel, HBVMethods):
"""The HBV eWaterCycle model, with the Container Registry docker image."""
Expand Down

0 comments on commit 3748173

Please sign in to comment.