Skip to content

Commit 00eee7a

Browse files
authored
fix(npf): variablecv dewatered fix (#2500)
* update test_gwf_ats02.py * exclude test028_sfr_rewet until next release * update release notes
1 parent 635dea7 commit 00eee7a

File tree

6 files changed

+223
-10
lines changed

6 files changed

+223
-10
lines changed

autotest/test_external_models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
EXCLUDE = [
2020
"alt_model",
2121
"test205_gwtbuy-henrytidal",
22+
# todo reinstate after 6.7.0 release
23+
"test028_sfr_rewet",
2224
# todo reinstate after 6.5.0 release
2325
"test001d_Tnewton",
2426
# remove tests with nwt usg conductance weighting

autotest/test_gwf_ats02.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
def build_models(idx, test):
2626
perlen = [10, 10]
2727
nper = len(perlen)
28-
nstp = [5, 5]
28+
nstp = [1, 1]
2929
tsmult = nper * [1.0]
3030
delr = 1.0
3131
delc = 1.0
@@ -75,7 +75,7 @@ def build_models(idx, test):
7575
)
7676

7777
# create iterative model solution and register the gwf model with it
78-
nouter, ninner = 20, 5
78+
nouter, ninner = 50, 5
7979
hclose, rclose, relax = 1e-6, 1e-6, 0.97
8080
imsgwf = flopy.mf6.ModflowIms(
8181
sim,
@@ -216,7 +216,7 @@ def check_output(idx, test):
216216
except:
217217
assert False, f'could not load data from "{fpth}"'
218218
# ensure layer 1 is dry with the DRY value
219-
assert np.max(tc["OBS1"][:201]) == -1.0e30, "layer 1 should be dry for this period"
219+
assert np.max(tc["OBS1"][:9]) == -1.0e30, "layer 1 should be dry for this period"
220220

221221

222222
@pytest.mark.parametrize("idx, name", enumerate(cases))

autotest/test_gwf_npf_vcv.py

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
"""
2+
Test the dependent_variable_scale option for gwt for a one-dimensional model grid
3+
of square cells.
4+
"""
5+
6+
import flopy
7+
import numpy as np
8+
import pytest
9+
from framework import TestFramework
10+
11+
dewatered = [False, True, False, True]
12+
perched = [False, False, True, True]
13+
chd1_lst = (-0.5, 0.0)
14+
cases = [f"vcv{n:02d}_02" for n in range(len(dewatered))]
15+
16+
17+
def build_model(idx, ws, vhfb=False):
18+
nlay, nrow, ncol = 2, 1, 2
19+
nper = 2
20+
perlen = [1.0] * nper
21+
nstp = [1] * nper
22+
tsmult = [1.0] * nper
23+
delr = 1.0
24+
delc = 1.0
25+
top = 1.0
26+
botm = [0.0, -1.0]
27+
28+
chd0 = 0.5
29+
30+
strt = chd0
31+
32+
icelltype = 1
33+
hk = 100.0 # 50000.0 # 100.0
34+
area = delc * delr
35+
vk = 0.001
36+
if vhfb:
37+
if dewatered[idx]:
38+
v0 = 1.0 / ((area * vk) / (0.5 * (chd0 - botm[0])))
39+
# v1 should be 0.0
40+
v1 = 0.0
41+
else:
42+
v0 = 1.0 / ((area * vk) / (0.5 * (chd0 - botm[0])))
43+
v1 = 1.0 / ((area * vk) / (0.5 * (botm[0] - botm[1])))
44+
vcont = 1.0 / (v0 + v1)
45+
else:
46+
pass
47+
48+
tdis_rc = []
49+
for i in range(nper):
50+
tdis_rc.append((perlen[i], nstp[i], tsmult[i]))
51+
52+
name = cases[idx]
53+
54+
sim = flopy.mf6.MFSimulation(
55+
sim_name=name, version="mf6", exe_name="mf6", sim_ws=ws
56+
)
57+
58+
tdis = flopy.mf6.ModflowTdis(sim, time_units="DAYS", nper=nper, perioddata=tdis_rc)
59+
60+
ims = flopy.mf6.ModflowIms(
61+
sim,
62+
print_option="SUMMARY",
63+
complexity="simple",
64+
outer_dvclose=1e-8,
65+
outer_maximum=200,
66+
inner_dvclose=1e-9,
67+
inner_maximum=100,
68+
)
69+
70+
gwf = flopy.mf6.ModflowGwf(
71+
sim,
72+
modelname=name,
73+
print_input=True,
74+
)
75+
76+
dis = flopy.mf6.ModflowGwfdis(
77+
gwf,
78+
length_units="feet",
79+
nlay=nlay,
80+
nrow=nrow,
81+
ncol=ncol,
82+
delr=delr,
83+
delc=delc,
84+
top=top,
85+
botm=botm,
86+
)
87+
88+
ic = flopy.mf6.ModflowGwfic(gwf, strt=strt)
89+
90+
if dewatered[idx]:
91+
cvoptions = "variablecv dewatered"
92+
else:
93+
cvoptions = "variablecv"
94+
95+
npf = flopy.mf6.ModflowGwfnpf(
96+
gwf,
97+
print_flows=True,
98+
save_flows=True,
99+
cvoptions=cvoptions,
100+
perched=perched[idx],
101+
icelltype=icelltype,
102+
k=hk,
103+
k33=vk,
104+
)
105+
106+
chd_spd = {}
107+
for n in range(nper):
108+
spd = [((0, 0, 0), chd0)]
109+
spd += [((1, 0, 0), chd1_lst[n])]
110+
chd_spd[n] = spd
111+
chd = flopy.mf6.ModflowGwfchd(
112+
gwf,
113+
maxbound=len(spd),
114+
stress_period_data=chd_spd,
115+
)
116+
117+
obs_data = {
118+
"flowja.obs.csv": [
119+
("h0", "head", (0, 0, 1)),
120+
("h1", "head", (1, 0, 1)),
121+
("vflow", "FLOW-JA-FACE", (0, 0, 1), (1, 0, 1)),
122+
]
123+
}
124+
obs = flopy.mf6.ModflowUtlobs(gwf, continuous=obs_data)
125+
126+
return sim
127+
128+
129+
def build_models(idx, test):
130+
ws = test.workspace
131+
sim = build_model(idx, ws, vhfb=True)
132+
133+
return sim, None
134+
135+
136+
def check_results(idx, test):
137+
ws = test.workspace
138+
sim = flopy.mf6.MFSimulation.load(sim_ws=ws, verbosity_level=0)
139+
gwf = sim.get_model()
140+
141+
nper = sim.tdis.nper.array
142+
delr = gwf.dis.delr.array[1]
143+
delc = gwf.dis.delc.array[0]
144+
area = delr * delc
145+
146+
cellids = [(0, 0, 1), (1, 0, 1)]
147+
botm0 = gwf.dis.botm.array[cellids[0]]
148+
botm1 = gwf.dis.botm.array[cellids[1]]
149+
vk = gwf.npf.k33.array[cellids[0]]
150+
151+
obs = gwf.obs.output.obs().get_data()
152+
h0 = obs["H0"]
153+
h1 = obs["H1"]
154+
155+
answer = obs["VFLOW"]
156+
157+
is_dewatered = dewatered[idx]
158+
is_perched = perched[idx]
159+
160+
vflow = []
161+
for n in range(nper):
162+
if is_dewatered and h1[n] < botm0:
163+
v0 = 1.0 / ((area * vk) / (0.5 * (h0[n] - botm0)))
164+
v1 = 0.0
165+
else:
166+
v0 = 1.0 / ((area * vk) / (0.5 * (h0[n] - botm0)))
167+
v1 = 1.0 / ((area * vk) / (0.5 * (botm0 - botm1)))
168+
vcont = 1.0 / (v0 + v1)
169+
vflow.append(vcont * (h1[n] - h0[n]))
170+
171+
if is_perched and h1[n] < botm0:
172+
qcorr = vcont * (h1[n] - botm0)
173+
vflow[n] -= qcorr
174+
175+
vflow = np.array(vflow)
176+
177+
print(f"H0: {h0}")
178+
print(f"H1: {h1}")
179+
print(f"Vertical flow: {answer}")
180+
print(f"Answer: {vflow}")
181+
182+
assert np.allclose(vflow, answer), "simulated results not equal to the answer"
183+
184+
185+
@pytest.mark.parametrize("idx, name", enumerate(cases))
186+
def test_mf6model(idx, name, function_tmpdir, targets):
187+
test = TestFramework(
188+
name=name,
189+
workspace=function_tmpdir,
190+
targets=targets,
191+
build=lambda t: build_models(idx, t),
192+
check=lambda t: check_results(idx, t),
193+
)
194+
test.run()

doc/ReleaseNotes/develop.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ description = "Added HIGHEST\\_SATURATED option to the GWT SRC package. When act
3636
[[items]]
3737
section = "features"
3838
subsection = "basic"
39-
description = "Added a coordinate reference system option CRS to discretization packages. For example, an EPSG integer code, authority string, or Open Geospatial Consortium Well-Known Text (WKT) specification. This option does not affect simulation results, but is written to the binary grid file so that postprocessors can locate the grid in space. A new binary grid file version number (2) is introduced to indicate that CRS information may be present."
39+
description = "Added a coordinate reference system option CRS to discretization packages. For example, an EPSG integer code, authority string, or Open Geospatial Consortium Well-Known Text (WKT) specification. This option does not affect simulation results, but is written to the binary grid file so that postprocessors can locate the grid in space. A new binary grid file version number (2) is introduced to indicate that CRS information may be present."
40+
41+
[[items]]
42+
section = "fixes"
43+
subsection = "internal"
44+
description = "Fixed the DEWATERED VARIABLECV option. Previously, the saturation and vertical hydraulic conductivity of the underlying cell were included in the vertical conductance calculation when the water level in the underlying cell was below the top of the cell. This is not what is described in \\citet[][equation 4-44]{modflow6gwf}. The code has been modified to only include the saturation and vertical hydraulic conductivity of the overlying cell when calculating the vertical conductance when the water-level in the underlying cell is below the top of the cell."

src/Model/GroundWaterFlow/gwf-npf.f90

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,8 +1249,10 @@ subroutine log_options(this, found)
12491249
if (found%ivarcv) &
12501250
write (this%iout, '(4x,a)') 'Vertical conductance varies with water table.'
12511251
if (found%idewatcv) &
1252-
write (this%iout, '(4x,a)') 'Vertical conductance accounts for dewatered &
1253-
&portion of an underlying cell.'
1252+
write (this%iout, '(4x,a)') 'Vertical conductance is calculated using &
1253+
&only the saturated thickness and properties &
1254+
&of the overlying cell if the head in the &
1255+
&underlying cell is below its top.'
12541256
if (found%ixt3d) write (this%iout, '(4x,a)') 'XT3D formulation is selected.'
12551257
if (found%ixt3drhs) &
12561258
write (this%iout, '(4x,a)') 'XT3D RHS formulation is selected.'

src/Model/ModelUtilities/GwfConductanceUtils.f90

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,21 @@ function vcond(ibdn, ibdm, ictn, ictm, inewton, ivarcv, idewatcv, &
204204
satntmp = satn
205205
satmtmp = satm
206206
if (idewatcv == 0) then
207-
if (botn > botm) then
208-
satmtmp = DONE
207+
if (botn > botm) then
208+
satmtmp = DONE
209+
else
210+
satntmp = DONE
211+
end if
209212
else
210-
satntmp = DONE
211-
end if
213+
if (botn > botm) then
214+
if (satmtmp < DONE) then
215+
satmtmp = DZERO
216+
end if
217+
else
218+
if (satntmp < DONE) then
219+
satntmp = DZERO
220+
end if
221+
end if
212222
end if
213223
bovk1 = satntmp * (topn - botn) * DHALF / vkn
214224
bovk2 = satmtmp * (topm - botm) * DHALF / vkm

0 commit comments

Comments
 (0)