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

Change hydrological modelling to classes, add Hydrotel #18

Merged
merged 86 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
e06655e
first working version
RondeauG Jul 18, 2023
160d544
first working version
RondeauG Jul 21, 2023
edefaa2
removed extra file
RondeauG Jul 21, 2023
d393736
better docstrings
RondeauG Jul 21, 2023
8b3eace
restore xhydro
RondeauG Jul 21, 2023
0ef45d8
changed calendar logic
RondeauG Jul 21, 2023
d041fad
xclim-compatible standard name
RondeauG Jul 24, 2023
0ade3f0
renamed datachecks to fit closer to xclim
RondeauG Jul 27, 2023
cdc0fbb
cleanup
RondeauG Aug 15, 2023
ee1d9f0
xscen health_checks
RondeauG Aug 21, 2023
6f3867f
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Oct 11, 2023
3ded3f7
remove todos
RondeauG Oct 11, 2023
a14b116
finalised changes
RondeauG Oct 12, 2023
6311c81
small fix
RondeauG Oct 12, 2023
bd92018
tmp_commit
RondeauG Oct 12, 2023
a93491f
small fixes
Zeitsperre Oct 12, 2023
6d97d4c
Merge remote-tracking branch 'origin/hydrotel' into hydrotel
RondeauG Oct 12, 2023
82a143c
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Oct 12, 2023
09f29de
Merge branch 'main' into hydrotel
Zeitsperre Oct 12, 2023
1dd71d3
added tests + bugfix
RondeauG Oct 17, 2023
2df9e1d
Merge remote-tracking branch 'origin/hydrotel' into hydrotel
RondeauG Oct 17, 2023
148710e
full cov
RondeauG Oct 18, 2023
6b45d03
maybe fix test
RondeauG Oct 18, 2023
8b5a2f1
maybe fix again...
RondeauG Oct 18, 2023
651334d
maybe fix again...
RondeauG Oct 18, 2023
18f0f20
remove faulty encoding
RondeauG Oct 18, 2023
c1b974d
upd History
RondeauG Oct 18, 2023
8a33347
added notebook
RondeauG Oct 18, 2023
d20fb79
add weather config file
RondeauG Oct 23, 2023
abf0798
upd notebooks
RondeauG Oct 25, 2023
5071f46
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Oct 25, 2023
abdd8e0
use pytest fixtures, docstring and typing adjustments, move yml files…
Zeitsperre Oct 25, 2023
2d70a93
Merge branch 'main' into hydrotel
Zeitsperre Nov 2, 2023
887e5ff
Update docs/notebooks/hydrotel.ipynb
RondeauG Nov 7, 2023
1a49524
Update docs/notebooks/hydrotel.ipynb
RondeauG Nov 7, 2023
4e6322f
fake data in fake_hydrotel_project
RondeauG Nov 7, 2023
52415ee
clean notebook outputs
RondeauG Nov 7, 2023
01d2a76
upd notebooks
RondeauG Nov 7, 2023
1186b17
notebook path workaround
RondeauG Nov 7, 2023
8efa2e6
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Nov 7, 2023
48913f9
better error message
RondeauG Nov 7, 2023
996ac92
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Nov 7, 2023
8cf2742
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Feb 12, 2024
5def36e
dependencies
RondeauG Feb 12, 2024
63629ad
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 12, 2024
9ba13e9
corrections for new pre-commit rules
RondeauG Feb 12, 2024
cc75a56
Merge remote-tracking branch 'origin/hydrotel' into hydrotel
RondeauG Feb 12, 2024
961e2a4
add module description
RondeauG Feb 12, 2024
95d782f
line too long
RondeauG Feb 12, 2024
22c9d0b
hydrotel now actually works
RondeauG Feb 20, 2024
9cc46d8
Merge branch 'main' into hydrotel
Zeitsperre Feb 20, 2024
db305ae
cleanup of hydrotel
RondeauG Feb 22, 2024
f3d7ef9
small cleanup
RondeauG Feb 22, 2024
1928329
hydrological model
RondeauG Feb 22, 2024
c875aa1
Merge remote-tracking branch 'origin/hydrotel' into hydrotel
RondeauG Feb 22, 2024
ed3d9fe
abstract class and fixed hydrotel tests
RondeauG Feb 23, 2024
a5dc3e9
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Feb 23, 2024
3721eec
fixed more tests with the new hydrological model structure
RondeauG Feb 23, 2024
fb6ed9c
full test coverage
RondeauG Feb 26, 2024
363511c
fix test
RondeauG Feb 26, 2024
ba21d4e
updated docs
RondeauG Feb 28, 2024
5156777
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Feb 28, 2024
743f381
prevent circular imports
RondeauG Feb 28, 2024
f73e5f1
prevent circular imports
RondeauG Feb 28, 2024
8d8b9c4
Merge branch 'main' into hydrotel
Zeitsperre Feb 29, 2024
1a298b3
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Feb 29, 2024
e5e70ba
small bugfix + more control on get_inputs
RondeauG Mar 6, 2024
b669d9d
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Mar 6, 2024
0d2d7a2
Merge branch 'main' into hydrotel
RondeauG Mar 6, 2024
c4663d8
Merge branch 'main' into hydrotel
Zeitsperre Mar 8, 2024
45c4e42
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Mar 20, 2024
51bb6cd
new index structure
RondeauG Mar 20, 2024
d058039
Merge remote-tracking branch 'origin/hydrotel' into hydrotel
RondeauG Mar 20, 2024
7ea5c4f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 20, 2024
c4e1b21
Merge branch 'main' into hydrotel
Zeitsperre Mar 21, 2024
573cd8b
apply suggestions from code review
RondeauG Apr 11, 2024
78e6447
Merge remote-tracking branch 'origin/hydrotel' into hydrotel
RondeauG Apr 11, 2024
f3c45c7
fix docs
RondeauG Apr 11, 2024
2ae81a5
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Apr 11, 2024
c2a6452
fix tests
RondeauG Apr 11, 2024
6df28be
reorder notebooks
RondeauG Apr 11, 2024
1371bf1
less strict checkups
RondeauG Apr 12, 2024
13c5863
upd changes
RondeauG Apr 12, 2024
9ef8917
forgot issue
RondeauG Apr 12, 2024
ee49177
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Apr 12, 2024
daa655c
Merge remote-tracking branch 'origin/main' into hydrotel
RondeauG Apr 16, 2024
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
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ LOCALES := docs/locales
help:
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)

clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
clean: clean-build clean-docs clean-pyc clean-test ## remove all build, test, coverage and Python artifacts

clean-build: ## remove build artifacts
rm -fr build/
Expand All @@ -37,6 +37,8 @@ clean-build: ## remove build artifacts
find . -name '*.egg' -exec rm -f {} +

clean-docs: ## remove docs artifacts
rm -fr docs/notebooks/_data/
rm -fr docs/notebooks/.ipynb_checkpoints/
rm -f docs/apidoc/xhydro*.rst
rm -f docs/apidoc/modules.rst
rm -f docs/locales/fr/LC_MESSAGES/*.mo
Expand Down
13 changes: 13 additions & 0 deletions docs/notebooks/_finder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Utility to find the folder containing the notebooks."""

from __future__ import annotations

from pathlib import Path


def _find_current_folder():
"""Find the folder containing the notebooks.

Needed in order to run the notebooks from the docs/notebooks folder.
"""
return Path(__file__).absolute().parent
425 changes: 425 additions & 0 deletions docs/notebooks/hydrological_modelling.ipynb

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions docs/notebooks/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Usage
:maxdepth: 1
:numbered:

climate_change
gis
local_frequency_analysis
hydrological_modelling
optimal_interpolation
local_frequency_analysis
climate_change
2 changes: 2 additions & 0 deletions environment-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ dependencies:
- intake <2.0.0 # This should be set by xdatasets once on conda-forge
- leafmap
- numpy
- pandas
- planetary-computer
- pystac
- pystac-client
- pooch >=1.8.0
- pydantic >=2.0,<2.5.3 # FIXME: Remove pin once our dependencies (xclim, xscen) support pydantic 2.5.3
- pyyaml
- rasterio
- spotpy
- stackstac
Expand Down
2 changes: 2 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ dependencies:
- intake <2.0.0 # This should be set by xdatasets once on conda-forge
- leafmap
- numpy
- pandas
- planetary-computer
- pooch >=1.8.0
- pydantic >=2.0,<2.5.3 # FIXME: Remove pin once our dependencies (xclim, xscen) support pydantic 2.5.3
- pystac
- pystac-client
- pyyaml
- rasterio
- spotpy
- stackstac
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ dependencies = [
"intake-esm !=2024.2.6", # pin needed for intake-esm (dependency of xscen) to work with Python3.9
"leafmap",
"numpy",
"pandas",
"planetary-computer",
"pooch >=1.8.0",
"pydantic >=2.0,<2.5.3",
"pystac",
"pystac-client",
"pyyaml",
"rasterio",
"spotpy",
"stackstac",
Expand Down
6 changes: 3 additions & 3 deletions tests/test_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import numpy as np
import pytest

from xhydro.modelling import hydrological_model
from xhydro.modelling.calibration import perform_calibration
from xhydro.modelling.hydrological_modelling import _dummy_model
from xhydro.modelling.obj_funcs import get_objective_function, transform_flows


Expand Down Expand Up @@ -50,8 +50,8 @@ def test_spotpy_calibration():

# Test dummy model response
model_config["parameters"] = [5, 5, 5]
qsim = _dummy_model(model_config)
assert qsim["qsim"].values[3] == 3500.00
qsim = hydrological_model(model_config).run()
assert qsim["streamflow"].values[3] == 3500.00

# Also test to ensure SCEUA and take_minimize is required.
best_parameters_sceua, best_simulation, best_objfun = perform_calibration(
Expand Down
129 changes: 81 additions & 48 deletions tests/test_hydrological_modelling.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,84 @@
import numpy as np
import pytest

from xhydro.modelling.hydrological_modelling import (
get_hydrological_model_inputs,
run_hydrological_model,
)


def test_hydrological_modelling():
"""Test the hydrological models as they become online"""
# Test the dummy model
model_config = {
"precip": np.array([10, 11, 12, 13, 14, 15]),
"temperature": np.array([10, 3, -5, 1, 15, 0]),
"qobs": np.array([120, 130, 140, 150, 160, 170]),
"drainage_area": np.array([10]),
"model_name": "Dummy",
"parameters": np.array([5, 5, 5]),
}
qsim = run_hydrological_model(model_config)
assert qsim["qsim"].values[3] == 3500.00

# Test the exceptions for new models
model_config.update(model_name="ADD_OTHER_HERE")
qsim = run_hydrological_model(model_config)
assert qsim == 0


def test_import_unknown_model():
"""Test for unknown model"""
with pytest.raises(NotImplementedError) as pytest_wrapped_e:
model_config = {"model_name": "fake_model"}
_ = run_hydrological_model(model_config)
assert pytest_wrapped_e.type == NotImplementedError


def test_get_unknown_model_requirements():
"""Test for required inputs for models with unknown name"""
with pytest.raises(NotImplementedError) as pytest_wrapped_e:
model_name = "fake_model"
_ = get_hydrological_model_inputs(model_name)
assert pytest_wrapped_e.type == NotImplementedError


def test_get_model_requirements():
"""Test for required inputs for models"""
model_name = "Dummy"
required_config = get_hydrological_model_inputs(model_name)
print(required_config.keys())
assert len(required_config.keys()) == 4
from xhydro.modelling import get_hydrological_model_inputs, hydrological_model


class TestHydrologicalModelling:
def test_hydrological_modelling(self):
"""Test the hydrological models as they become online"""
# Test the dummy model
model_config = {
"precip": np.array([10, 11, 12, 13, 14, 15]),
"temperature": np.array([10, 3, -5, 1, 15, 0]),
"qobs": np.array([120, 130, 140, 150, 160, 170]),
"drainage_area": np.array([10]),
"model_name": "Dummy",
"parameters": np.array([5, 5, 5]),
}
qsim = hydrological_model(model_config).run()
np.testing.assert_array_equal(qsim["streamflow"].values[3], 3500.00)

def test_import_unknown_model(self):
"""Test for unknown model"""
with pytest.raises(NotImplementedError) as pytest_wrapped_e:
model_config = {"model_name": "fake_model"}
_ = hydrological_model(model_config).run()
assert pytest_wrapped_e.type == NotImplementedError

def test_missing_name(self):
with pytest.raises(ValueError, match="The model name must be provided"):
model_config = {"parameters": [1, 2, 3]}
hydrological_model(model_config).run()


class TestHydrologicalModelRequirements:
def test_get_unknown_model_requirements(self):
"""Test for required inputs for models with unknown name"""
with pytest.raises(NotImplementedError) as pytest_wrapped_e:
model_name = "fake_model"
_ = get_hydrological_model_inputs(model_name)
assert pytest_wrapped_e.type == NotImplementedError

@pytest.mark.parametrize("model_name", ["Dummy", "Hydrotel"])
def test_get_model_requirements(self, model_name):
"""Test for required inputs for models"""
expected_keys = {"Dummy": (6, 6), "Hydrotel": (8, 3)}

all_config, _ = get_hydrological_model_inputs(model_name)
assert len(all_config.keys()) == expected_keys[model_name][0]

all_config, _ = get_hydrological_model_inputs(model_name, required_only=True)
assert len(all_config.keys()) == expected_keys[model_name][1]


class TestDummyModel:
def test_inputs(self):
model_config = {
"model_name": "Dummy",
"precip": np.array([10, 11, 12, 13, 14, 15]),
"temperature": np.array([10, 3, -5, 1, 15, 0]),
"qobs": np.array([120, 130, 140, 150, 160, 170]),
"drainage_area": np.array([10]),
"parameters": np.array([5, 5, 5]),
}
dummy = hydrological_model(model_config)
ds_in = dummy.get_inputs()
np.testing.assert_array_equal(ds_in.precip, model_config["precip"])
np.testing.assert_array_equal(ds_in.temperature, model_config["temperature"])
assert len(ds_in.time) == len(model_config["precip"])

def test_streamflow(self):
model_config = {
"model_name": "Dummy",
"precip": np.array([10, 11, 12, 13, 14, 15]),
"temperature": np.array([10, 3, -5, 1, 15, 0]),
"qobs": np.array([120, 130, 140, 150, 160, 170]),
"drainage_area": np.array([10]),
"parameters": np.array([5, 5, 5]),
}
dummy = hydrological_model(model_config)
ds_out = dummy.get_streamflow()
np.testing.assert_array_equal(ds_out["streamflow"].values[3], 3500.00)
assert dummy.qsim.equals(ds_out)
assert dummy.get_streamflow().equals(ds_out)
Loading