Skip to content

Commit ed4a922

Browse files
Merge branch 'master' into wq_add_anihfb
2 parents cd10d2e + 03613a0 commit ed4a922

File tree

4 files changed

+89
-8
lines changed

4 files changed

+89
-8
lines changed

docs/api/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Fixed
1515
results in invalid MODFLOW6 input for 2D grid data, such as DIS top.
1616
- :meth:`imod.flow.ImodflowModel.write` no longer writes incorrect projectfiles
1717
for non-grid values with a time and layer dimension.
18+
- :func:`imod.evaluate.interpolate_value_boundaries`: Fix edge case when
19+
successive values in z direction are exactly equal to the boundary value.
1820

1921
Changed
2022
~~~~~~~

imod/evaluate/boundaries.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ def _interpolate_value_boundaries_z1d(values, z, dz, threshold, out):
1717
if (n % 2 == 0 and values[k, i, j] >= threshold) or (
1818
n % 2 == 1 and values[k, i, j] <= threshold
1919
): # exceeding (n even) or falling below threshold (n odd)
20+
if values[k, i, j] == values[k - 1, i, j]:
21+
# edge case: subsequent values equal threshold result in ZeroDivisionError
22+
continue
2023
if not np.isnan(values[k - 1, i, j]):
2124
# interpolate z coord of threshold
2225
out[n, i, j] = z[k - 1] + (threshold - values[k - 1, i, j]) * (
@@ -42,6 +45,9 @@ def _interpolate_value_boundaries_z3d(values, z, dz, threshold, out):
4245
if (n % 2 == 0 and values[k, i, j] >= threshold) or (
4346
n % 2 == 1 and values[k, i, j] <= threshold
4447
): # exceeding (n even) or falling below threshold (n odd)
48+
if values[k, i, j] == values[k - 1, i, j]:
49+
# edge case: subsequent values equal threshold result in ZeroDivisionError
50+
continue
4551
if not np.isnan(values[k - 1, i, j]):
4652
# interpolate z coord of threshold
4753
out[n, i, j] = z[k - 1, i, j] + (
@@ -69,6 +75,10 @@ def _interpolate_value_boundaries_z3ddz1d(values, z, dz, threshold, out):
6975
if (n % 2 == 0 and values[k, i, j] >= threshold) or (
7076
n % 2 == 1 and values[k, i, j] <= threshold
7177
): # exceeding (n even) or falling below threshold (n odd)
78+
# if (values[k, i, j] == threshold) and (values[k - 1, i, j] == threshold):
79+
if values[k, i, j] == values[k - 1, i, j]:
80+
# edge case: subsequent values equal threshold result in ZeroDivisionError
81+
continue
7282
if not np.isnan(values[k - 1, i, j]):
7383
# interpolate z coord of threshold
7484
out[n, i, j] = z[k - 1, i, j] + (
@@ -111,18 +121,25 @@ def interpolate_value_boundaries(values, z, threshold):
111121

112122
values = values.load()
113123
out = xr.full_like(values, np.nan)
124+
# if z.dz is a scalar, conform to z
125+
# TODO: broadcast dz to z, so _interpolate_value_boundaries_z3ddz1d is superfluous?
126+
if len(z.dz.dims) == 0:
127+
dz = np.ones_like(z) * z.dz.values
128+
else:
129+
dz = z.dz.values
130+
114131
if len(z.dims) == 1:
115132
_interpolate_value_boundaries_z1d(
116-
values.values, z.values, z.dz.values, threshold, out.values
133+
values.values, z.values, dz, threshold, out.values
117134
)
118135
else:
119-
if len(z.dz.dims) == 1:
136+
if len(z.dz.dims) <= 1:
120137
_interpolate_value_boundaries_z3ddz1d(
121-
values.values, z.values, z.dz.values, threshold, out.values
138+
values.values, z.values, dz, threshold, out.values
122139
)
123140
else:
124141
_interpolate_value_boundaries_z3d(
125-
values.values, z.values, z.dz.values, threshold, out.values
142+
values.values, z.values, dz, threshold, out.values
126143
)
127144
out = out.rename({"layer": "boundary"})
128145
out = out.dropna(dim="boundary", how="all")

imod/tests/test_evaluate/test_boundaries.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,61 @@ def test_interpolate_value_boundaries_voxels():
126126

127127
assert exc_ref.round(5).equals(exc.round(5))
128128
assert fallb_ref.round(5).equals(fallb.round(5))
129+
130+
131+
def test_interpolate_value_boundaries_scalardz():
132+
data = np.array([[[0.5], [1.2]], [[1.0], [0.5]], [[1.2], [3.0]]])
133+
z = np.array([-1.0, -3.0, -5.0])
134+
dz = np.array(2.0)
135+
coords = {"layer": [1, 2, 3], "y": [0.5, 1.5], "x": [0.5]}
136+
zcoords = {"layer": [1, 2, 3]}
137+
dims = ("layer", "y", "x")
138+
zdims = "layer"
139+
da = xr.DataArray(data, coords, dims)
140+
z = xr.DataArray(z, zcoords, zdims)
141+
z = z.assign_coords({"dz": dz})
142+
143+
exc_ref = np.array([[[-3.0], [0.0]], [[np.nan], [-3.4]]])
144+
fallb_ref = np.array([[[np.nan], [-1.57142857]]])
145+
coords2 = {"boundary": [0, 1], "y": [0.5, 1.5], "x": [0.5]}
146+
dims2 = ("boundary", "y", "x")
147+
exc_ref = xr.DataArray(exc_ref, coords2, dims2)
148+
coords2["boundary"] = [0]
149+
fallb_ref = xr.DataArray(fallb_ref, coords2, dims2)
150+
151+
exc, fallb = imod.evaluate.interpolate_value_boundaries(da, z, 1.0)
152+
153+
assert exc_ref.round(5).equals(exc.round(5))
154+
assert fallb_ref.round(5).equals(fallb.round(5))
155+
156+
157+
def test_interpolate_value_boundaries_samevalues():
158+
# sequence of same data - not equal and equal to boundary
159+
data = np.array([[[1.0], [1.0]], [[1.0], [1.0]], [[1.0], [3.0]]])
160+
z = np.array([[[0.5], [0.5]], [[-1.0], [-1.0]], [[-5.0], [-5.0]]])
161+
dz = np.array([[[1.0], [1.0]], [[2.0], [2.0]], [[6.0], [6.0]]])
162+
coords = {"layer": [1, 2, 3], "y": [0.5, 1.5], "x": [0.5]}
163+
dims = ("layer", "y", "x")
164+
da = xr.DataArray(data, coords, dims)
165+
z = xr.DataArray(z, coords, dims)
166+
z = z.assign_coords({"dz": (("layer", "y", "x"), dz)})
167+
168+
exc_ref = np.array([[[1.0], [1.0]]])
169+
fallb_ref = np.empty((0, 2, 1))
170+
coords2 = {"boundary": [0], "y": [0.5, 1.5], "x": [0.5]}
171+
dims2 = ("boundary", "y", "x")
172+
exc_ref = xr.DataArray(exc_ref, coords2, dims2)
173+
coords2["boundary"] = []
174+
fallb_ref = xr.DataArray(fallb_ref, coords2, dims2)
175+
176+
# values not equal to boundary
177+
exc, fallb = imod.evaluate.interpolate_value_boundaries(da, z, 0.9)
178+
179+
assert exc_ref.round(5).equals(exc.round(5))
180+
assert fallb_ref.round(5).equals(fallb.round(5))
181+
182+
# values equal to boundary
183+
exc, fallb = imod.evaluate.interpolate_value_boundaries(da, z, 1.0)
184+
185+
assert exc_ref.round(5).equals(exc.round(5))
186+
assert fallb_ref.round(5).equals(fallb.round(5))

setup.cfg

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,14 @@ pytest11 =
8181

8282
[flake8]
8383
ignore =
84-
E203 # whitespace before ':' - doesn't work well with black
85-
E402 # module level import not at top of file
86-
E501 # line too long - let black worry about that
87-
W503 # line break before binary operator
84+
# whitespace before ':' - doesn't work well with black
85+
E203,
86+
# module level import not at top of file
87+
E402,
88+
# line too long - let black worry about that
89+
E501,
90+
# line break before binary operator
91+
W503,
8892
per-file-ignores =
8993
./docs/conf.py:F401
9094
__init__.py:F401

0 commit comments

Comments
 (0)