Skip to content

Commit

Permalink
integrate tests with test framework
Browse files Browse the repository at this point in the history
  • Loading branch information
wpbonelli committed Dec 27, 2023
1 parent 4c9f516 commit 060ecaa
Show file tree
Hide file tree
Showing 14 changed files with 814 additions and 638 deletions.
57 changes: 36 additions & 21 deletions autotest/framework.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import shutil
import time
from itertools import repeat
from pathlib import Path
from subprocess import PIPE, STDOUT, Popen
from traceback import format_exc
Expand Down Expand Up @@ -313,13 +314,7 @@ def __init__(
workspace = Path(workspace).expanduser().absolute()
assert workspace.is_dir(), f"{workspace} is not a valid directory"
if verbose:
from pprint import pprint

print("Initializing test", name, "in workspace", workspace)
contents = list(workspace.glob("*"))
if any(contents):
print(f"Workspace is not empty:")
pprint(contents)

self.name = name
self.workspace = workspace
Expand All @@ -339,7 +334,7 @@ def __init__(
self.rclose = 0.001 if rclose is None else rclose
self.overwrite = overwrite
self.verbose = verbose
self.xfail = xfail
self.xfail = [xfail] if isinstance(xfail, bool) else xfail

def __repr__(self):
return self.name
Expand Down Expand Up @@ -632,7 +627,7 @@ def run_sim_or_model(
workspace: Union[str, os.PathLike],
target: Union[str, os.PathLike] = "mf6",
xfail: bool = False,
) -> bool:
) -> Tuple[bool, List[str]]:
"""
Run a simulation or model with FloPy.
Expand Down Expand Up @@ -660,14 +655,16 @@ def run_sim_or_model(
self.cmp_namefile = (
None
if "mf6" in target.name or "libmf6" in target.name
else os.path.basename(nf) if nf else None
else os.path.basename(nf)
if nf
else None
)

# run the model
try:
# via MODFLOW API
if "libmf6" in target.name and self.api_func:
success, _ = self.api_func(target, workspace)
success, buff = self.api_func(target, workspace)
# via MF6 executable
elif "mf6" in target.name:
# parallel test if configured
Expand All @@ -676,7 +673,7 @@ def run_sim_or_model(
f"Parallel test {self.name} on {self.ncpus} processes"
)
try:
success, _ = run_parallel(
success, buff = run_parallel(
workspace, target, self.ncpus
)
except Exception:
Expand All @@ -689,10 +686,11 @@ def run_sim_or_model(
else:
# otherwise serial run
try:
success, _ = flopy.run_model(
success, buff = flopy.run_model(
target,
self.workspace / "mfsim.nam",
model_ws=workspace,
report=True,
)
except Exception:
warn(
Expand All @@ -704,8 +702,8 @@ def run_sim_or_model(
else:
# non-MF6 model
try:
success, _ = flopy.run_model(
target, self.cmp_namefile, workspace
success, buff = flopy.run_model(
target, self.cmp_namefile, workspace, report=True
)
except Exception:
warn(f"{target} model failed:\n{format_exc()}")
Expand Down Expand Up @@ -734,7 +732,7 @@ def run_sim_or_model(
f"Unhandled error in comparison model {self.name}:\n{format_exc()}"
)

return success
return success, buff

def compare_output(self, compare):
"""
Expand Down Expand Up @@ -792,17 +790,33 @@ def run(self):
sims = sims if isinstance(sims, Iterable) else [sims]
sims = [sim for sim in sims if sim] # filter Nones
self.sims = sims
nsims = len(sims)
self.buffs = list(repeat(None, nsims))
assert len(self.xfail) in [
1,
nsims,
], f"Invalid xfail: expected a single boolean or one for each model"
if len(self.xfail) == 1 and nsims:
self.xfail = list(repeat(self.xfail[0], nsims))
write_input(*sims, overwrite=self.overwrite, verbose=self.verbose)
else:
self.sims = [MFSimulation.load(sim_ws=self.workspace)]
self.buffs = [None]
assert (
len(self.xfail) == 1
), f"Invalid xfail: expected a single boolean or one for each model"

# run models/simulations
for sim_or_model in self.sims:
for i, sim_or_model in enumerate(self.sims):
workspace = get_workspace(sim_or_model)
target = self._try_resolve(sim_or_model.exe_name, self.targets.mf6)
assert self.run_sim_or_model(
workspace, target, self.xfail
), f"{'Simulation' if 'mf6' in str(target) else 'Model'} failed: {workspace}"
xfail = self.xfail[i]
success, buff = self.run_sim_or_model(workspace, target, xfail)
self.buffs[i] = buff # store model output for assertions later
assert success, (
f"{'Simulation' if 'mf6' in str(target) else 'Model'} "
f"{'should have failed' if xfail else 'failed'}: {workspace}"
)

# get expected output files from main simulation
_, self.outp = get_mf6_files(
Expand Down Expand Up @@ -842,10 +856,11 @@ def run(self):
# todo: don't hardcode workspace or assume agreement with test case
# simulation workspace, set & access simulation workspaces directly
workspace = self.workspace / self.compare
assert self.run_sim_or_model(
success, _ = self.run_sim_or_model(
workspace,
self.targets.get(self.compare, self.targets.mf6),
), f"Comparison model failed: {workspace}"
)
assert success, f"Comparison model failed: {workspace}"

# compare model results, if enabled
if self.verbose:
Expand Down
183 changes: 90 additions & 93 deletions autotest/prt/prt_test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
from types import SimpleNamespace
from typing import Optional, Tuple
from typing import Tuple

import flopy
import matplotlib as mpl
Expand All @@ -12,107 +12,104 @@ def all_equal(series, val):
return a[0] == val and (a[0] == a).all()


def get_gwf_sim(
name, ws, mf6
) -> Tuple[flopy.mf6.MFSimulation, SimpleNamespace]:
"""
Simple GWF simulation for use/modification by PRT tests
"""

# test case context
ctx = SimpleNamespace(
nlay=1,
nrow=10,
ncol=10,
top=1.0,
botm=[0.0],
nper=1,
perlen=1.0,
nstp=1,
tsmult=1.0,
porosity=0.1,
# mp7 release points (cell-local coordinates)
releasepts_mp7=[
# node number, localx, localy, localz
(0, float(f"0.{i + 1}"), float(f"0.{i + 1}"), 0.5)
for i in range(9)
],
# PRT release points (cell indices and global coordinates both required)
releasepts_prt=[
# particle index, k, i, j, x, y, z
[i, 0, 0, 0, float(f"0.{i + 1}"), float(f"9.{i + 1}"), 0.5]
for i in range(9)
],
)
class BasicDisCase:
nlay = 1
nrow = 10
ncol = 10
top = 1.0
botm = [0.0]
nper = 1
perlen = 1.0
nstp = 1
tsmult = 1.0
porosity = 0.1
releasepts_mp7 = [
# node number, localx, localy, localz
(0, float(f"0.{i + 1}"), float(f"0.{i + 1}"), 0.5)
for i in range(9)
]
releasepts_prt = [
# particle index, k, i, j, x, y, z
[i, 0, 0, 0, float(f"0.{i + 1}"), float(f"9.{i + 1}"), 0.5]
for i in range(9)
]

# create simulation
sim = flopy.mf6.MFSimulation(
sim_name=name,
exe_name=mf6,
version="mf6",
sim_ws=ws,
)
@staticmethod
def get_gwf_sim(name, ws, mf6) -> flopy.mf6.MFSimulation:
"""
Simple GWF simulation for use/modification by PRT tests
"""

# create simulation
sim = flopy.mf6.MFSimulation(
sim_name=name,
exe_name=mf6,
version="mf6",
sim_ws=ws,
)

# create tdis package
flopy.mf6.modflow.mftdis.ModflowTdis(
sim,
pname="tdis",
time_units="DAYS",
nper=ctx.nper,
perioddata=[(ctx.perlen, ctx.nstp, ctx.tsmult)],
)
# create tdis package
flopy.mf6.modflow.mftdis.ModflowTdis(
sim,
pname="tdis",
time_units="DAYS",
nper=BasicDisCase.nper,
perioddata=[
(BasicDisCase.perlen, BasicDisCase.nstp, BasicDisCase.tsmult)
],
)

# create gwf model
gwfname = f"{name}_gwf"
gwf = flopy.mf6.ModflowGwf(sim, modelname=gwfname, save_flows=True)

# create gwf discretization
flopy.mf6.modflow.mfgwfdis.ModflowGwfdis(
gwf,
pname="dis",
nlay=ctx.nlay,
nrow=ctx.nrow,
ncol=ctx.ncol,
)
# create gwf model
gwfname = f"{name}_gwf"
gwf = flopy.mf6.ModflowGwf(sim, modelname=gwfname, save_flows=True)

# create gwf discretization
flopy.mf6.modflow.mfgwfdis.ModflowGwfdis(
gwf,
pname="dis",
nlay=BasicDisCase.nlay,
nrow=BasicDisCase.nrow,
ncol=BasicDisCase.ncol,
)

# create gwf initial conditions package
flopy.mf6.modflow.mfgwfic.ModflowGwfic(gwf, pname="ic")
# create gwf initial conditions package
flopy.mf6.modflow.mfgwfic.ModflowGwfic(gwf, pname="ic")

# create gwf node property flow package
flopy.mf6.modflow.mfgwfnpf.ModflowGwfnpf(
gwf,
pname="npf",
save_saturation=True,
save_specific_discharge=True,
)
# create gwf node property flow package
flopy.mf6.modflow.mfgwfnpf.ModflowGwfnpf(
gwf,
pname="npf",
save_saturation=True,
save_specific_discharge=True,
)

# create gwf chd package
spd = {
0: [[(0, 0, 0), 1.0, 1.0], [(0, 9, 9), 0.0, 0.0]],
1: [[(0, 0, 0), 0.0, 0.0], [(0, 9, 9), 1.0, 2.0]],
}
chd = flopy.mf6.ModflowGwfchd(
gwf,
pname="CHD-1",
stress_period_data=spd,
auxiliary=["concentration"],
)
# create gwf chd package
spd = {
0: [[(0, 0, 0), 1.0, 1.0], [(0, 9, 9), 0.0, 0.0]],
1: [[(0, 0, 0), 0.0, 0.0], [(0, 9, 9), 1.0, 2.0]],
}
chd = flopy.mf6.ModflowGwfchd(
gwf,
pname="CHD-1",
stress_period_data=spd,
auxiliary=["concentration"],
)

# create gwf output control package
# output file names
gwf_budget_file = f"{gwfname}.bud"
gwf_head_file = f"{gwfname}.hds"
oc = flopy.mf6.ModflowGwfoc(
gwf,
budget_filerecord=gwf_budget_file,
head_filerecord=gwf_head_file,
saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")],
)
# create gwf output control package
# output file names
gwf_budget_file = f"{gwfname}.bud"
gwf_head_file = f"{gwfname}.hds"
oc = flopy.mf6.ModflowGwfoc(
gwf,
budget_filerecord=gwf_budget_file,
head_filerecord=gwf_head_file,
saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")],
)

# create iterative model solution for gwf model
ims = flopy.mf6.ModflowIms(sim)
# create iterative model solution for gwf model
ims = flopy.mf6.ModflowIms(sim)

return sim, ctx
return sim


def check_track_data(
Expand Down
Loading

0 comments on commit 060ecaa

Please sign in to comment.