Skip to content

Commit a3f4e5f

Browse files
Issue #960 regrid method refactor (#1054)
Fixes #960 and #1000 # Description Adds regrid schemes which replaces the dictionaries under ``_regrid_method`` with pydantic dataclasses. Pydantic comes with input validation base on type annotation, so requires no extra code. [See documentation here](https://docs.pydantic.dev/latest/concepts/dataclasses/) This requires pydantic as extra dependency, so is added as dependency. I kept ``_regrid_method`` as a class variable, as it gave problems as an attribute with ``imod.mf6.Package.from_file`` classmethod, where attributes are not forwarded. I created a new namespace: ``imod.mf6.regrid`` where we can store all Regridding related stuff. Probably good to move other public API there as well later on, namely the ``imod.mf6.utilities.regridder_types.RegridderType`` and ``imod.mf6.utilities.regrid.RegridderWeightsCache``. As these are more than just utilities: They are essential objects for users to do their regridding at the moment. # Checklist - [x] Links to correct issue - [x] Update changelog, if changes affect users - [x] PR title starts with ``Issue #nr``, e.g. ``Issue #737`` - [x] Unit tests were added - [x] **If feature added**: Added/extended example
1 parent c97c565 commit a3f4e5f

35 files changed

+897
-319
lines changed

docs/api/changelog.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,48 @@ All notable changes to this project will be documented in this file.
66
The format is based on `Keep a Changelog`_, and this project adheres to
77
`Semantic Versioning`_.
88

9+
[Unreleased]
10+
------------
11+
12+
Added
13+
~~~~~
14+
- Added objects with regrid settings. These can be used to provide custom
15+
settings: :class:`imod.mf6.regrid.ConstantHeadRegridMethod`,
16+
:class:`imod.mf6.regrid.DiscretizationRegridMethod`,
17+
:class:`imod.mf6.regrid.DispersionRegridMethod`,
18+
:class:`imod.mf6.regrid.DrainageRegridMethod`,
19+
:class:`imod.mf6.regrid.EmptyRegridMethod`,
20+
:class:`imod.mf6.regrid.EvapotranspirationRegridMethod`,
21+
:class:`imod.mf6.regrid.GeneralHeadBoundaryRegridMethod`,
22+
:class:`imod.mf6.regrid.InitialConditionsRegridMethod`,
23+
:class:`imod.mf6.regrid.MobileStorageTransferRegridMethod`,
24+
:class:`imod.mf6.regrid.NodePropertyFlowRegridMethod`,
25+
:class:`imod.mf6.regrid.RechargeRegridMethod`,
26+
:class:`imod.mf6.regrid.RiverRegridMethod`,
27+
:class:`imod.mf6.regrid.SpecificStorageRegridMethod`,
28+
:class:`imod.mf6.regrid.StorageCoefficientRegridMethod`.
29+
30+
Changed
31+
~~~~~~~
32+
- Instead of providing a dictionary with settings to ``Package.regrid_like``,
33+
provide one of the following ``RegridMethod`` objects:
34+
:class:`imod.mf6.regrid.ConstantHeadRegridMethod`,
35+
:class:`imod.mf6.regrid.DiscretizationRegridMethod`,
36+
:class:`imod.mf6.regrid.DispersionRegridMethod`,
37+
:class:`imod.mf6.regrid.DrainageRegridMethod`,
38+
:class:`imod.mf6.regrid.EmptyRegridMethod`,
39+
:class:`imod.mf6.regrid.EvapotranspirationRegridMethod`,
40+
:class:`imod.mf6.regrid.GeneralHeadBoundaryRegridMethod`,
41+
:class:`imod.mf6.regrid.InitialConditionsRegridMethod`,
42+
:class:`imod.mf6.regrid.MobileStorageTransferRegridMethod`,
43+
:class:`imod.mf6.regrid.NodePropertyFlowRegridMethod`,
44+
:class:`imod.mf6.regrid.RechargeRegridMethod`,
45+
:class:`imod.mf6.regrid.RiverRegridMethod`,
46+
:class:`imod.mf6.regrid.SpecificStorageRegridMethod`,
47+
:class:`imod.mf6.regrid.StorageCoefficientRegridMethod`.
48+
49+
50+
951
[0.17.1] - 2024-05-16
1052
---------------------
1153

docs/api/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This page provides an auto-generated summary of imod's API.
66
.. toctree::
77
:maxdepth: 2
88

9+
changelog
10+
911
io
1012
prepare
1113
select
@@ -19,5 +21,3 @@ This page provides an auto-generated summary of imod's API.
1921
flow
2022
msw
2123
metamod
22-
23-
changelog

docs/api/mf6.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,30 @@ Transport Packages
101101
MassSourceLoading
102102
SourceSinkMixing
103103
SourceSinkMixing.from_flow_model
104+
105+
106+
.. currentmodule:: imod.mf6.regrid
107+
108+
Regrid
109+
======
110+
111+
Regrid Method Settings
112+
----------------------
113+
114+
.. autosummary::
115+
:toctree: generated/mf6/regrid
116+
117+
ConstantHeadRegridMethod
118+
DiscretizationRegridMethod
119+
DispersionRegridMethod
120+
DrainageRegridMethod
121+
EmptyRegridMethod
122+
EvapotranspirationRegridMethod
123+
GeneralHeadBoundaryRegridMethod
124+
InitialConditionsRegridMethod
125+
MobileStorageTransferRegridMethod
126+
NodePropertyFlowRegridMethod
127+
RechargeRegridMethod
128+
RiverRegridMethod
129+
SpecificStorageRegridMethod
130+
StorageCoefficientRegridMethod

examples/mf6/different_ways_to_regrid_models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import xarray as xr
2626
from example_models import create_twri_simulation
2727

28+
from imod.mf6.regrid import NodePropertyFlowRegridMethod
2829
from imod.mf6.utilities.regrid import RegridderType, RegridderWeightsCache
2930

3031
# %%
@@ -98,7 +99,7 @@
9899
# a performance increase if that package uses the same regridding method,
99100
# because initializing a regridder is costly.
100101

101-
regridder_types = {"k": (RegridderType.CENTROIDLOCATOR, None)}
102+
regridder_types = NodePropertyFlowRegridMethod(k=(RegridderType.CENTROIDLOCATOR,))
102103
regrid_context = RegridderWeightsCache()
103104
npf_regridded = model["npf"].regrid_like(
104105
target_grid=target_grid,

examples/user-guide/08-regridding.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,10 @@
199199
# Regrid the recharge package with a custom regridder. In this case we opt
200200
# for the centroid locator regridder. This regridder is similar to using a
201201
# "nearest neighbour" lookup.
202+
from imod.mf6.regrid import RechargeRegridMethod
202203
from imod.mf6.utilities.regrid import RegridderType
203204

204-
regridder_types = {"rate": (RegridderType.CENTROIDLOCATOR, None)}
205+
regridder_types = RechargeRegridMethod(rate=(RegridderType.CENTROIDLOCATOR,))
205206

206207
regridded_recharge = original_rch_package.regrid_like(
207208
target_grid,
@@ -412,6 +413,8 @@ def plot_histograms_side_by_side(array_original, array_regridded, title):
412413
#
413414
# This code snippet prints all default methods:
414415
#
416+
from dataclasses import asdict
417+
415418
import pandas as pd
416419

417420
from imod.tests.fixtures.package_instance_creation import ALL_PACKAGE_INSTANCES
@@ -428,7 +431,7 @@ def plot_histograms_side_by_side(array_original, array_regridded, title):
428431
for pkg in ALL_PACKAGE_INSTANCES:
429432
if hasattr(pkg, "_regrid_method"):
430433
package_name = type(pkg).__name__
431-
regrid_methods = pkg._regrid_method
434+
regrid_methods = asdict(pkg.get_regrid_methods())
432435
for array_name in regrid_methods.keys():
433436
method_name = regrid_methods[array_name][0].name
434437
function_name = ""

imod/mf6/adv.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,14 @@
1010
"""
1111

1212
from copy import deepcopy
13-
from typing import Optional, Tuple
1413

1514
from imod.mf6.interfaces.iregridpackage import IRegridPackage
1615
from imod.mf6.package import Package
17-
from imod.mf6.utilities.regrid import RegridderType
1816

1917

2018
class Advection(Package, IRegridPackage):
2119
_pkg_id = "adv"
2220
_template = Package._initialize_template(_pkg_id)
23-
_regrid_method: dict[str, tuple[RegridderType, str]] = {}
2421

2522
def __init__(self, scheme: str):
2623
dict_dataset = {"scheme": scheme}
@@ -37,9 +34,6 @@ def mask(self, _) -> Package:
3734
"""
3835
return deepcopy(self)
3936

40-
def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
41-
return self._regrid_method
42-
4337

4438
class AdvectionUpstream(Advection):
4539
"""

imod/mf6/chd.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from typing import Optional, Tuple
2-
31
import numpy as np
42

53
from imod.logging import init_log_decorator
64
from imod.mf6.boundary_condition import BoundaryCondition
75
from imod.mf6.interfaces.iregridpackage import IRegridPackage
8-
from imod.mf6.utilities.regrid import RegridderType
6+
from imod.mf6.regrid.regrid_schemes import ConstantHeadRegridMethod
97
from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA
108
from imod.schemata import (
119
AllInsideNoDataSchema,
@@ -108,14 +106,7 @@ class ConstantHead(BoundaryCondition, IRegridPackage):
108106
_keyword_map = {}
109107
_auxiliary_data = {"concentration": "species"}
110108
_template = BoundaryCondition._initialize_template(_pkg_id)
111-
112-
_regrid_method = {
113-
"head": (
114-
RegridderType.OVERLAP,
115-
"mean",
116-
), # TODO: should be set to barycentric once supported
117-
"concentration": (RegridderType.OVERLAP, "mean"),
118-
}
109+
_regrid_method = ConstantHeadRegridMethod()
119110

120111
@init_log_decorator()
121112
def __init__(
@@ -141,6 +132,7 @@ def __init__(
141132
"repeat_stress": repeat_stress,
142133
}
143134
super().__init__(dict_dataset)
135+
144136
self._validate_init_schemata(validate)
145137

146138
def _validate(self, schemata, **kwargs):
@@ -149,6 +141,3 @@ def _validate(self, schemata, **kwargs):
149141
errors = super()._validate(schemata, **kwargs)
150142

151143
return errors
152-
153-
def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
154-
return self._regrid_method

imod/mf6/cnc.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
from typing import Optional, Tuple
2-
31
import numpy as np
42

53
from imod.logging import init_log_decorator
64
from imod.mf6.boundary_condition import BoundaryCondition
75
from imod.mf6.interfaces.iregridpackage import IRegridPackage
8-
from imod.mf6.utilities.regridding_types import RegridderType
96
from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA
107
from imod.schemata import (
118
AllInsideNoDataSchema,
@@ -53,8 +50,6 @@ class ConstantConcentration(BoundaryCondition, IRegridPackage):
5350
_period_data = ("concentration",)
5451
_template = BoundaryCondition._initialize_template(_pkg_id)
5552

56-
_regrid_method: dict[str, tuple[RegridderType, str]] = {}
57-
5853
_init_schemata = {
5954
"concentration": [
6055
DTypeSchema(np.floating),
@@ -91,6 +86,3 @@ def __init__(
9186
}
9287
super().__init__(dict_dataset)
9388
self._validate_init_schemata(validate)
94-
95-
def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
96-
return self._regrid_method

imod/mf6/dis.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pathlib
2-
from typing import List, Optional, Tuple
2+
from typing import List
33

44
import numpy as np
55

@@ -8,7 +8,7 @@
88
from imod.mf6.interfaces.imaskingsettings import IMaskingSettings
99
from imod.mf6.interfaces.iregridpackage import IRegridPackage
1010
from imod.mf6.package import Package
11-
from imod.mf6.utilities.regrid import RegridderType
11+
from imod.mf6.regrid.regrid_schemes import DiscretizationRegridMethod
1212
from imod.mf6.validation import DisBottomSchema
1313
from imod.schemata import (
1414
ActiveCellsConnectedSchema,
@@ -85,12 +85,7 @@ class StructuredDiscretization(Package, IRegridPackage, IMaskingSettings):
8585
_grid_data = {"top": np.float64, "bottom": np.float64, "idomain": np.int32}
8686
_keyword_map = {"bottom": "botm"}
8787
_template = Package._initialize_template(_pkg_id)
88-
89-
_regrid_method = {
90-
"top": (RegridderType.OVERLAP, "mean"),
91-
"bottom": (RegridderType.OVERLAP, "mean"),
92-
"idomain": (RegridderType.OVERLAP, "mode"),
93-
}
88+
_regrid_method = DiscretizationRegridMethod()
9489

9590
@property
9691
def skip_variables(self) -> List[str]:
@@ -151,6 +146,3 @@ def _validate(self, schemata, **kwargs):
151146
errors = super()._validate(schemata, **kwargs)
152147

153148
return errors
154-
155-
def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
156-
return self._regrid_method

imod/mf6/disv.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Optional, Tuple
1+
from typing import List
22

33
import numpy as np
44
import pandas as pd
@@ -7,7 +7,7 @@
77
from imod.mf6.interfaces.imaskingsettings import IMaskingSettings
88
from imod.mf6.interfaces.iregridpackage import IRegridPackage
99
from imod.mf6.package import Package
10-
from imod.mf6.utilities.regrid import RegridderType
10+
from imod.mf6.regrid.regrid_schemes import DiscretizationRegridMethod
1111
from imod.mf6.validation import DisBottomSchema
1212
from imod.mf6.write_context import WriteContext
1313
from imod.schemata import (
@@ -67,12 +67,7 @@ class VerticesDiscretization(Package, IRegridPackage, IMaskingSettings):
6767
_grid_data = {"top": np.float64, "bottom": np.float64, "idomain": np.int32}
6868
_keyword_map = {"bottom": "botm"}
6969
_template = Package._initialize_template(_pkg_id)
70-
71-
_regrid_method = {
72-
"top": (RegridderType.OVERLAP, "mean"),
73-
"bottom": (RegridderType.OVERLAP, "mean"),
74-
"idomain": (RegridderType.OVERLAP, "mode"),
75-
}
70+
_regrid_method = DiscretizationRegridMethod()
7671

7772
@property
7873
def skip_variables(self) -> List[str]:
@@ -159,6 +154,3 @@ def _validate(self, schemata, **kwargs):
159154
errors = super()._validate(schemata, **kwargs)
160155

161156
return errors
162-
163-
def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
164-
return self._regrid_method

0 commit comments

Comments
 (0)