Skip to content

Commit d2b6b0d

Browse files
authored
fix(par): specific discharge for parallel simulations (#2479)
* - add spdis check to virtual gwe and gwt model: only synchronize spdis when it is available * - python format * - add check and error for index map out of bounds * - add release note * - fixed typo in note * - forgot about map being present but empty * - remove commented code * - check: map can be empty - fix: map is zero-based
1 parent caa5803 commit d2b6b0d

File tree

9 files changed

+475
-22
lines changed

9 files changed

+475
-22
lines changed

autotest/test_gwt_adv03_gwtgwt.py

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
"""
2+
Test gwt-gwt coupling for advection without dispersion.
3+
"""
4+
5+
import flopy
6+
import pytest
7+
from framework import TestFramework
8+
9+
cases = ["adv03_gwtgwt"]
10+
scheme = "tvd"
11+
12+
# solver settings
13+
nouter, ninner = 100, 300
14+
hclose, rclose, relax = 1e-6, 1e-6, 1.0
15+
16+
g_delr = 1.0
17+
18+
19+
def get_gwf_model(sim, gwfname, gwfpath, modelshape, chdspd=None, welspd=None):
20+
nlay, nrow, ncol, xshift, yshift = modelshape
21+
delr = g_delr
22+
delc = 1.0
23+
top = 1.0
24+
botm = [0.0]
25+
strt = 1.0
26+
hk = 1.0
27+
laytyp = 0
28+
29+
gwf = flopy.mf6.ModflowGwf(
30+
sim,
31+
modelname=gwfname,
32+
save_flows=True,
33+
)
34+
35+
dis = flopy.mf6.ModflowGwfdis(
36+
gwf,
37+
nlay=nlay,
38+
nrow=nrow,
39+
ncol=ncol,
40+
delr=delr,
41+
delc=delc,
42+
top=top,
43+
botm=botm,
44+
xorigin=xshift,
45+
yorigin=yshift,
46+
)
47+
48+
# initial conditions
49+
ic = flopy.mf6.ModflowGwfic(gwf, strt=strt)
50+
51+
# node property flow
52+
npf = flopy.mf6.ModflowGwfnpf(
53+
gwf,
54+
icelltype=laytyp,
55+
k=hk,
56+
save_specific_discharge=False,
57+
)
58+
59+
# chd files
60+
if chdspd is not None:
61+
chd = flopy.mf6.modflow.mfgwfchd.ModflowGwfchd(
62+
gwf,
63+
stress_period_data=chdspd,
64+
save_flows=False,
65+
pname="CHD-1",
66+
)
67+
68+
# wel files
69+
if welspd is not None:
70+
wel = flopy.mf6.ModflowGwfwel(
71+
gwf,
72+
print_input=True,
73+
print_flows=True,
74+
stress_period_data=welspd,
75+
save_flows=False,
76+
auxiliary="CONCENTRATION",
77+
pname="WEL-1",
78+
)
79+
80+
# output control
81+
oc = flopy.mf6.ModflowGwfoc(
82+
gwf,
83+
budget_filerecord=f"{gwfname}.cbc",
84+
head_filerecord=f"{gwfname}.hds",
85+
headprintrecord=[("COLUMNS", 10, "WIDTH", 15, "DIGITS", 6, "GENERAL")],
86+
saverecord=[("HEAD", "LAST"), ("BUDGET", "LAST")],
87+
printrecord=[("HEAD", "LAST"), ("BUDGET", "LAST")],
88+
)
89+
gwf.set_model_relative_path(gwfpath)
90+
return gwf
91+
92+
93+
def get_gwt_model(sim, gwtname, gwtpath, modelshape, scheme, sourcerecarray=None):
94+
nlay, nrow, ncol, xshift, yshift = modelshape
95+
delr = g_delr
96+
delc = 1.0
97+
top = 1.0
98+
botm = [0.0]
99+
strt = 1.0
100+
hk = 1.0
101+
laytyp = 0
102+
103+
gwt = flopy.mf6.MFModel(
104+
sim,
105+
model_type="gwt6",
106+
modelname=gwtname,
107+
)
108+
gwt.name_file.save_flows = True
109+
110+
dis = flopy.mf6.ModflowGwtdis(
111+
gwt,
112+
nlay=nlay,
113+
nrow=nrow,
114+
ncol=ncol,
115+
delr=delr,
116+
delc=delc,
117+
top=top,
118+
botm=botm,
119+
xorigin=xshift,
120+
yorigin=yshift,
121+
)
122+
123+
# initial conditions
124+
ic = flopy.mf6.ModflowGwtic(gwt, strt=0.0)
125+
126+
# advection
127+
adv = flopy.mf6.ModflowGwtadv(gwt, scheme=scheme)
128+
129+
# mass storage and transfer
130+
mst = flopy.mf6.ModflowGwtmst(gwt, porosity=0.1)
131+
132+
# sources
133+
ssm = flopy.mf6.ModflowGwtssm(gwt, sources=sourcerecarray)
134+
135+
# output control
136+
oc = flopy.mf6.ModflowGwtoc(
137+
gwt,
138+
budget_filerecord=f"{gwtname}.cbc",
139+
concentration_filerecord=f"{gwtname}.ucn",
140+
concentrationprintrecord=[("COLUMNS", 10, "WIDTH", 15, "DIGITS", 6, "GENERAL")],
141+
saverecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")],
142+
printrecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")],
143+
)
144+
145+
gwt.set_model_relative_path(gwtpath)
146+
return gwt
147+
148+
149+
def build_models(idx, test):
150+
# temporal discretization
151+
nper = 1
152+
perlen = [5.0]
153+
nstp = [20]
154+
tsmult = [1.0]
155+
tdis_rc = []
156+
for i in range(nper):
157+
tdis_rc.append((perlen[i], nstp[i], tsmult[i]))
158+
159+
# build MODFLOW 6 files
160+
ws = test.workspace
161+
sim = flopy.mf6.MFSimulation(sim_name=ws, version="mf6", exe_name="mf6", sim_ws=ws)
162+
# create tdis package
163+
tdis = flopy.mf6.ModflowTdis(
164+
sim, time_units="DAYS", nper=nper, perioddata=tdis_rc, pname="sim.tdis"
165+
)
166+
167+
# grid information
168+
nlay, nrow, ncol = 1, 2, 50
169+
170+
# Create gwf1 model
171+
welspd = {0: [[(0, 0, 0), 1.0, 1.0], [(0, 1, 0), 1.0, 1.0]]}
172+
chdspd = None
173+
gwf1 = get_gwf_model(
174+
sim,
175+
"flow1",
176+
"flow1",
177+
(nlay, nrow, ncol, 0.0, 0.0),
178+
chdspd=chdspd,
179+
welspd=welspd,
180+
)
181+
182+
# Create gwf2 model
183+
welspd = None
184+
chdspd = {0: [[(0, 0, ncol - 1), 0.0000000], [(0, 1, ncol - 1), 0.0000000]]}
185+
gwf2 = get_gwf_model(
186+
sim,
187+
"flow2",
188+
"flow2",
189+
(nlay, nrow, ncol, ncol * g_delr, 0.0),
190+
chdspd=chdspd,
191+
welspd=welspd,
192+
)
193+
194+
# gwf-gwf with interface model enabled
195+
gwfgwf_data = [
196+
[(0, 0, ncol - 1), (0, 0, 0), 1, 0.5, 0.5, 1.0, 0.0, 1.0],
197+
[(0, 1, ncol - 1), (0, 1, 0), 1, 0.5, 0.5, 1.0, 0.0, 1.0],
198+
]
199+
gwfgwf = flopy.mf6.ModflowGwfgwf(
200+
sim,
201+
exgtype="GWF6-GWF6",
202+
nexg=len(gwfgwf_data),
203+
exgmnamea=gwf1.name,
204+
exgmnameb=gwf2.name,
205+
exchangedata=gwfgwf_data,
206+
auxiliary=["ANGLDEGX", "CDIST"],
207+
filename="flow1_flow2.gwfgwf",
208+
)
209+
210+
# create iterative model solution and register the gwf model with it
211+
imsgwf = flopy.mf6.ModflowIms(
212+
sim,
213+
print_option="SUMMARY",
214+
outer_dvclose=hclose,
215+
outer_maximum=nouter,
216+
under_relaxation="NONE",
217+
inner_maximum=ninner,
218+
inner_dvclose=hclose,
219+
rcloserecord=rclose,
220+
linear_acceleration="CG",
221+
scaling_method="NONE",
222+
reordering_method="NONE",
223+
relaxation_factor=relax,
224+
filename="flow.ims",
225+
)
226+
sim.register_ims_package(imsgwf, [gwf1.name, gwf2.name])
227+
228+
# Create gwt model
229+
sourcerecarray = [("WEL-1", "AUX", "CONCENTRATION")]
230+
gwt1 = get_gwt_model(
231+
sim,
232+
"transport1",
233+
"transport1",
234+
(nlay, nrow, ncol, 0.0, 0.0),
235+
scheme,
236+
sourcerecarray=sourcerecarray,
237+
)
238+
239+
# Create gwt model
240+
sourcerecarray = None
241+
gwt2 = get_gwt_model(
242+
sim,
243+
"transport2",
244+
"transport2",
245+
(nlay, nrow, ncol, ncol * g_delr, 0.0),
246+
scheme,
247+
sourcerecarray=sourcerecarray,
248+
)
249+
250+
# Create GWT GWT exchange
251+
gwt1gwt2 = flopy.mf6.ModflowGwtgwt(
252+
sim,
253+
exgtype="GWT6-GWT6",
254+
gwfmodelname1=gwf1.name,
255+
gwfmodelname2=gwf2.name,
256+
adv_scheme=scheme,
257+
nexg=len(gwfgwf_data),
258+
exgmnamea=gwt1.name,
259+
exgmnameb=gwt2.name,
260+
exchangedata=gwfgwf_data,
261+
auxiliary=["ANGLDEGX", "CDIST"],
262+
filename="transport1_transport2.gwtgwt",
263+
)
264+
265+
# GWF GWT exchange
266+
gwfgwt1 = flopy.mf6.ModflowGwfgwt(
267+
sim,
268+
exgtype="GWF6-GWT6",
269+
exgmnamea="flow1",
270+
exgmnameb="transport1",
271+
filename="flow1_transport1.gwfgwt",
272+
)
273+
gwfgwt2 = flopy.mf6.ModflowGwfgwt(
274+
sim,
275+
exgtype="GWF6-GWT6",
276+
exgmnamea="flow2",
277+
exgmnameb="transport2",
278+
filename="flow2_transport2.gwfgwt",
279+
)
280+
281+
# create iterative model solution and register the gwt model with it
282+
imsgwt = flopy.mf6.ModflowIms(
283+
sim,
284+
print_option="SUMMARY",
285+
outer_dvclose=hclose,
286+
outer_maximum=nouter,
287+
under_relaxation="NONE",
288+
inner_maximum=ninner,
289+
inner_dvclose=hclose,
290+
rcloserecord=rclose,
291+
linear_acceleration="BICGSTAB",
292+
scaling_method="NONE",
293+
reordering_method="NONE",
294+
relaxation_factor=relax,
295+
filename="transport.ims",
296+
)
297+
sim.register_ims_package(imsgwt, [gwt1.name, gwt2.name])
298+
299+
return sim, None
300+
301+
302+
def check_output(idx, test):
303+
# for now, just check that simulation finishes correctly
304+
return
305+
306+
307+
@pytest.mark.parametrize("idx, name", enumerate(cases))
308+
@pytest.mark.developmode
309+
def test_mf6model(idx, name, function_tmpdir, targets):
310+
test = TestFramework(
311+
name=name,
312+
workspace=function_tmpdir,
313+
targets=targets,
314+
build=lambda t: build_models(idx, t),
315+
check=lambda t: check_output(idx, t),
316+
)
317+
test.run()

autotest/test_par_gwt_adv03.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
This test reuses the simulation data and config in
3+
test_gwt_adv03_gwtgwt.py and runs it in parallel mode.
4+
"""
5+
6+
import pytest
7+
from framework import TestFramework
8+
9+
cases = ["par_adv03_gwtgwt"]
10+
11+
12+
@pytest.mark.parallel
13+
@pytest.mark.developmode
14+
@pytest.mark.parametrize("idx, name", enumerate(cases))
15+
def test_mf6model(idx, name, function_tmpdir, targets):
16+
from test_gwt_adv03_gwtgwt import build_models, check_output
17+
18+
test = TestFramework(
19+
name=name,
20+
workspace=function_tmpdir,
21+
targets=targets,
22+
build=lambda t: build_models(idx, t),
23+
check=lambda t: check_output(idx, t),
24+
compare=None,
25+
parallel=True,
26+
ncpus=2,
27+
)
28+
test.run()

doc/ReleaseNotes/develop.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,8 @@ section = "fixes"
8888
subsection = "solution"
8989
description = "Fixes a bug first included in version 6.4.2 that has changed the convergence check of the numerical solution. In cases where the linear solver failed to converge, or when it did converge but the STRICT option was active and convergence did not occur on the first iteration, the solution was erroneously considered for overall convergence."
9090

91+
[[items]]
92+
section = "fixes"
93+
subsection = "parallel"
94+
description = "Fixes a memory exception that has occurred when running parallel solute transport on Windows without the dispersion package. This has no effect on the simulation results other than causing a premature termination in some cases."
95+

0 commit comments

Comments
 (0)