Skip to content

Commit 55add1c

Browse files
authored
feat(GWE): Introduce Groundwater Energy Transport (MODFLOW-ORG#1493)
* feat(GWE): Introduce Groundwater Energy Transport * fix typo in meson file * fix a formating issue that was popping up in an MST autotest * some legacy line leftover from a botched rebase, possibly * Need update initial autotest to conform to new autotest standards adopted with MODFLOW-ORG#1464 * Start looking for post-rebase breakages * Get gwfgwe and gwegwe exchanges up-to-date based on MODFLOW-ORG#1505 * Code that had been moved to set_active_status in FMI was still present in fmi_fc(). Removing. * Forgot to remove unused variables after making changes in 4d76729 * Adding another autotest specific to GWE * Adding another autotest after getting it MODFLOW-ORG#1464 compliant * Compliantizing another new autotest with PR MODFLOW-ORG#1464 * Fixes in response to MODFLOW-ORG#1493 (comment) * Fix in response to MODFLOW-ORG#1493 (comment) * Rerunning black in response to MODFLOW-ORG#1493 (comment) * Fix in response to MODFLOW-ORG#1493 (comment) * Made changes to dfn files in response to MODFLOW-ORG#1493 (comment) and MODFLOW-ORG#1493 (comment) and reran mf6ivar.py, which updated the tex files in this commit * Fix in response to MODFLOW-ORG#1493 (comment) * remove unnecessary line of script * Add missing lines to ConnectionBuilder.f90 related to GWE * Add a GWE vs GWT comparison autotest * GWE-GWE exchanges now working. Other clean-up for code uniformity * Rebrand constant temperature package acronym to CTP * Missed an import renaming update * Forgot to remove a now obsolete file due to renaming. * Rebrand energy storage and transfer package acronym to EST * Forgot to remove a now obsolete file due to renaming (again) * Rebrand gwe dispersion package acronym to CND (conduction) since that is the dominant process in heat transport * Remove gwe-related code from generalized transport code (tsp1.f90 & tsp1ssm1.f90) * add single-cell test for energy source loading (ESL) package * Adding energy source loading (ESL) package * remove unused variable * fprettify * Add another ESL autotest * Adding another autotest that compares gwe to three different analytical solutions from Carslaw & Jaeger (1947) * Bringing over Stallman autotest from previous GWE PR (MODFLOW-ORG#1237) * Adding streamflow energy transport (SFE) package * Add autotest for SFE * Adding lake energy transport (LKE) package. Includes new autotest * forgot meson update * Adding multi-aquifer well energy transport (MWE) package. Includes new autotest * Adding unsaturated-zone energy transport (UZE) package. Includes 2 new autotests * Attempting to reapply a failing autotest. Unable to discern why it is failing as downloaded contents from its failure are working locally. * removing troublesome autotest. Downloaded contents from failed run on Actions doesn't yield the same output that the logs are reporting. This particular autotest has an analytical solution (and a plot) that might be better shown on the modflow6-examples repo anyway * Removing a file that shouldn't have been added (snuck in among other staged files) * Update release notes
1 parent 67f1891 commit 55add1c

File tree

232 files changed

+34486
-1239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

232 files changed

+34486
-1239
lines changed

autotest/test_gwe_cnd.py

Lines changed: 495 additions & 0 deletions
Large diffs are not rendered by default.

autotest/test_gwe_drycell_cnd0.py

Lines changed: 423 additions & 0 deletions
Large diffs are not rendered by default.

autotest/test_gwe_drycell_cnd1.py

Lines changed: 484 additions & 0 deletions
Large diffs are not rendered by default.

autotest/test_gwe_drycell_cnd2.py

Lines changed: 669 additions & 0 deletions
Large diffs are not rendered by default.

autotest/test_gwe_esl01.py

Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
"""
2+
A "bathtub" test problem for GWE - bathtub meaning a single cell model
3+
(and actually, the bathtub is dry in this example)
4+
5+
Test the energy source loading package by warming a single cell with a known
6+
amount of energy input.
7+
8+
Model configuration
9+
10+
~: Represents energy source loading
11+
12+
+---------+
13+
| |
14+
| ~ |
15+
| |
16+
+---------+
17+
18+
"""
19+
20+
# Imports
21+
22+
import os
23+
import numpy as np
24+
import pytest
25+
import flopy
26+
27+
from framework import TestFramework
28+
29+
30+
# Base simulation and model name and workspace
31+
32+
scheme = "UPSTREAM"
33+
# scheme = "TVD"
34+
35+
cases = [
36+
"warmup", # 1-cell "bathtub" model with easily calculate-able answers
37+
]
38+
39+
# Model units
40+
length_units = "meters"
41+
time_units = "days"
42+
43+
# Parameterization
44+
45+
nrow = ncol = nlay = 1
46+
top = 1.0
47+
botm = [0.0]
48+
delr = 1.0 # Column width ($m$)
49+
delc = 1.0 # Row width ($m$)
50+
k11 = 1.0 # Horizontal hydraulic conductivity ($m/d$)
51+
ss = 1e-6 # Specific storage
52+
sy = 0.10 # Specific Yield
53+
prsity = sy # Porosity
54+
nper = 4 # Number of periods
55+
perlen = [1, 1, 1, 1] # Simulation time ($days$)
56+
nstp = [1, 1, 1, 1] # 10 day transient time steps
57+
steady = {0: False}
58+
transient = {0: True}
59+
strthd = 0.0
60+
61+
# Set some static model parameter values
62+
63+
k33 = k11 # Vertical hydraulic conductivity ($m/d$)
64+
idomain = 1 # All cells included in the simulation
65+
iconvert = 1 # All cells are convertible
66+
67+
icelltype = 1 # Cell conversion type (>1: unconfined)
68+
69+
# Set some static transport related model parameter values
70+
strt_temp1 = 0.0
71+
dispersivity = 0.0 # dispersion (remember, 1D model)
72+
73+
# GWE related parameters
74+
rhow = 1000.0
75+
cpw = 4183.0
76+
lhv = 2454.0
77+
cps = 760.0
78+
rhos = 1500.0
79+
80+
# Set solver parameter values (and related)
81+
nouter, ninner = 100, 300
82+
hclose, rclose, relax = 1e-10, 1e-10, 1.0
83+
ttsmult = 1.0
84+
85+
# Set up temporal data used by TDIS file
86+
tdis_rc = []
87+
for i in np.arange(nper):
88+
tdis_rc.append((perlen[i], nstp[i], ttsmult))
89+
90+
Joules_added_for_1degC_rise = (
91+
delr * delc * (top - botm[0]) * (1 - prsity) * cps * rhos
92+
)
93+
94+
# ### Create MODFLOW 6 GWE
95+
#
96+
# No GWF, only Heat conduction simulated
97+
98+
99+
def build_models(idx, test):
100+
# Base MF6 GWF model type
101+
ws = test.workspace
102+
name = cases[idx]
103+
104+
print("Building MF6 model...()".format(name))
105+
106+
# generate names for each model
107+
gwfname = "gwf-" + name
108+
gwename = "gwe-" + name
109+
110+
sim = flopy.mf6.MFSimulation(
111+
sim_name=name, sim_ws=ws, exe_name="mf6", version="mf6"
112+
)
113+
114+
# Instantiating MODFLOW 6 time discretization
115+
flopy.mf6.ModflowTdis(
116+
sim, nper=nper, perioddata=tdis_rc, time_units=time_units
117+
)
118+
119+
# Instantiating MODFLOW 6 groundwater flow model
120+
gwf = flopy.mf6.ModflowGwf(
121+
sim,
122+
modelname=gwfname,
123+
save_flows=True,
124+
model_nam_file="{}.nam".format(gwfname),
125+
)
126+
127+
# Instantiating MODFLOW 6 solver for flow model
128+
imsgwf = flopy.mf6.ModflowIms(
129+
sim,
130+
print_option="SUMMARY",
131+
outer_dvclose=hclose,
132+
outer_maximum=nouter,
133+
under_relaxation="NONE",
134+
inner_maximum=ninner,
135+
inner_dvclose=hclose,
136+
rcloserecord=rclose,
137+
linear_acceleration="CG",
138+
scaling_method="NONE",
139+
reordering_method="NONE",
140+
relaxation_factor=relax,
141+
filename="{}.ims".format(gwfname),
142+
)
143+
sim.register_ims_package(imsgwf, [gwfname])
144+
145+
# Instantiating MODFLOW 6 discretization package
146+
flopy.mf6.ModflowGwfdis(
147+
gwf,
148+
length_units=length_units,
149+
nlay=nlay,
150+
nrow=nrow,
151+
ncol=ncol,
152+
delr=delr,
153+
delc=delc,
154+
top=top,
155+
botm=botm,
156+
idomain=1,
157+
pname="DIS-1",
158+
filename="{}.dis".format(gwfname),
159+
)
160+
161+
# Instantiating MODFLOW 6 storage package
162+
flopy.mf6.ModflowGwfsto(
163+
gwf,
164+
ss=ss,
165+
sy=sy,
166+
iconvert=iconvert,
167+
steady_state=steady,
168+
transient=transient,
169+
pname="STO",
170+
filename="{}.sto".format(gwfname),
171+
)
172+
173+
# Instantiating MODFLOW 6 node-property flow package
174+
flopy.mf6.ModflowGwfnpf(
175+
gwf,
176+
save_flows=True,
177+
icelltype=icelltype,
178+
k=k11,
179+
k33=k33,
180+
save_specific_discharge=True,
181+
pname="NPF-1",
182+
filename="{}.npf".format(gwfname),
183+
)
184+
185+
# Instantiating MODFLOW 6 initial conditions package for flow model
186+
flopy.mf6.ModflowGwfic(
187+
gwf,
188+
strt=strthd,
189+
pname="IC-HD",
190+
filename="{}.ic".format(gwfname),
191+
)
192+
193+
# Instantiating MODFLOW 6 output control package for flow model
194+
flopy.mf6.ModflowGwfoc(
195+
gwf,
196+
pname="OC-1",
197+
head_filerecord="{}.hds".format(gwfname),
198+
budget_filerecord="{}.cbc".format(gwfname),
199+
headprintrecord=[("COLUMNS", 10, "WIDTH", 15, "DIGITS", 6, "GENERAL")],
200+
saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")],
201+
printrecord=[("HEAD", "ALL"), ("BUDGET", "ALL")],
202+
)
203+
204+
# ----------------------------------
205+
# Instantiating MODFLOW 6 GWE model
206+
# ----------------------------------
207+
gwe = flopy.mf6.ModflowGwe(
208+
sim, modelname=gwename, model_nam_file="{}.nam".format(gwename)
209+
)
210+
gwe.name_file.save_flows = True
211+
212+
imsgwe = flopy.mf6.ModflowIms(
213+
sim,
214+
print_option="SUMMARY",
215+
outer_dvclose=hclose,
216+
outer_maximum=nouter,
217+
under_relaxation="NONE",
218+
inner_maximum=ninner,
219+
inner_dvclose=hclose,
220+
rcloserecord=rclose,
221+
linear_acceleration="BICGSTAB",
222+
scaling_method="NONE",
223+
reordering_method="NONE",
224+
relaxation_factor=relax,
225+
filename="{}.ims".format(gwename),
226+
)
227+
sim.register_ims_package(imsgwe, [gwe.name])
228+
229+
# Instantiating MODFLOW 6 transport discretization package
230+
flopy.mf6.ModflowGwedis(
231+
gwe,
232+
nogrb=True,
233+
nlay=nlay,
234+
nrow=nrow,
235+
ncol=ncol,
236+
delr=delr,
237+
delc=delc,
238+
top=top,
239+
botm=botm,
240+
idomain=1,
241+
pname="DIS-GWE",
242+
filename="{}.dis".format(gwename),
243+
)
244+
245+
# Instantiating MODFLOW 6 transport initial concentrations
246+
flopy.mf6.ModflowGweic(
247+
gwe, strt=strt_temp1, pname="IC-2", filename="{}.ic".format(gwename)
248+
)
249+
250+
# Instantiating MODFLOW 6 transport advection package
251+
flopy.mf6.ModflowGweadv(
252+
gwe, scheme=scheme, pname="ADV-2", filename="{}.adv".format(gwename)
253+
)
254+
255+
# Instantiating MODFLOW 6 transport dispersion package
256+
flopy.mf6.ModflowGwecnd(
257+
gwe,
258+
xt3d_off=True,
259+
alh=dispersivity,
260+
ath1=dispersivity,
261+
ktw=0.5918 * 86400,
262+
kts=0.2700 * 86400,
263+
pname="CND-2",
264+
filename="{}.cnd".format(gwename),
265+
)
266+
267+
# Instantiating MODFLOW 6 transport mass storage package (formerly "reaction" package in MT3DMS)
268+
flopy.mf6.ModflowGweest(
269+
gwe,
270+
save_flows=True,
271+
porosity=prsity,
272+
cps=cps,
273+
rhos=rhos,
274+
packagedata=[cpw, rhow, lhv],
275+
pname="EST-2",
276+
filename="{}.est".format(gwename),
277+
)
278+
279+
# Instantiate MODFLOW 6 heat transport output control package
280+
flopy.mf6.ModflowGweoc(
281+
gwe,
282+
pname="OC-1",
283+
budget_filerecord="{}.cbc".format(gwename),
284+
temperature_filerecord="{}.ucn".format(gwename),
285+
temperatureprintrecord=[
286+
("COLUMNS", 10, "WIDTH", 15, "DIGITS", 6, "GENERAL")
287+
],
288+
saverecord=[("TEMPERATURE", "ALL"), ("BUDGET", "ALL")],
289+
printrecord=[("TEMPERATURE", "ALL"), ("BUDGET", "ALL")],
290+
)
291+
292+
# Instantiate energy source loading (ESL) package
293+
# Energy is added such that the temperature change in the cell will be
294+
# +1.0, +2.0, -1.0, and 0.0 degrees Celcius from stress period to stress
295+
# period
296+
esl_spd = {
297+
0: [
298+
[(0, 0, 0), Joules_added_for_1degC_rise],
299+
],
300+
1: [[(0, 0, 0), 2 * Joules_added_for_1degC_rise]],
301+
2: [[(0, 0, 0), -1 * Joules_added_for_1degC_rise]],
302+
3: [],
303+
}
304+
flopy.mf6.ModflowGweesl(
305+
gwe,
306+
stress_period_data=esl_spd,
307+
pname="ESL-1",
308+
filename="{}.esl".format(gwename),
309+
)
310+
311+
# Instantiating MODFLOW 6 flow-transport exchange mechanism
312+
flopy.mf6.ModflowGwfgwe(
313+
sim,
314+
exgtype="GWF6-GWE6",
315+
exgmnamea=gwfname,
316+
exgmnameb=gwename,
317+
pname="GWFGWE1",
318+
filename="{}.gwfgwe1".format(gwename),
319+
)
320+
321+
return sim, None
322+
323+
324+
def check_output(idx, test):
325+
print("evaluating results...")
326+
327+
# read transport results from GWE model
328+
name = cases[idx]
329+
gwename = "gwe-" + name
330+
331+
fpth = os.path.join(test.workspace, f"{gwename}.ucn")
332+
333+
try:
334+
# load temperatures
335+
tobj = flopy.utils.HeadFile(
336+
fpth, precision="double", text="TEMPERATURE"
337+
)
338+
temps = tobj.get_alldata()
339+
except:
340+
assert False, f'could not load temperature data from "{fpth}"'
341+
342+
# Energy source loading was carefully chosen to result in a +1,
343+
# +2 (for an absolute value of 3 deg C), -1, and 0.0 degree C
344+
# change between stress periods.
345+
known_ans = [1.0, 3.0, 2.0, 2.0]
346+
msg0 = (
347+
"Grid cell temperatures do not reflect the expected difference "
348+
"in stress period "
349+
)
350+
for index in np.arange(4):
351+
assert np.isclose(temps[index, 0, 0, 0], known_ans[index]), msg0 + str(
352+
index
353+
)
354+
355+
356+
# - No need to change any code below
357+
@pytest.mark.parametrize(
358+
"idx, name",
359+
list(enumerate(cases)),
360+
)
361+
def test_mf6model(idx, name, function_tmpdir, targets):
362+
test = TestFramework(
363+
name=name,
364+
workspace=function_tmpdir,
365+
targets=targets,
366+
build=lambda t: build_models(idx, t),
367+
check=lambda t: check_output(idx, t),
368+
)
369+
test.run()

0 commit comments

Comments
 (0)