From 29dfdcfe03d1d73d48f244787b8241d3a396d416 Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Fri, 6 Jan 2023 09:28:14 -0500 Subject: [PATCH] refactor(tests): use devtools fixtures (#1130) * use fixtures from devtools for loading external models and tracking target exes * convert adhoc --model, --package and --original-regression opts to pytest fixtures/CLI opts * use devtools and flopy in build_exes.py and get_exes.py, remove pymake usage * remove pymake usage from common_regression.py and external model tests * use GitHub API token for CI step to download executables * update developer docs --- .github/workflows/ci.yml | 8 + .github/workflows/docs.yml | 2 + .github/workflows/large.yml | 2 + DEVELOPER.md | 88 ++- autotest/build_exes.py | 163 +--- autotest/common_regression.py | 1005 ++++++++++++++++++++---- autotest/conftest.py | 49 ++ autotest/get_exes.py | 174 ++-- autotest/simulation.py | 44 +- autotest/test_gwf_henry_nr.py | 45 +- autotest/test_mf6_tmp_simulations.py | 21 +- autotest/test_z01_testmodels_mf6.py | 298 ++----- autotest/test_z02_testmodels_mf5to6.py | 246 +----- autotest/test_z03_examples.py | 247 +----- autotest/test_z03_largetestmodels.py | 207 +---- 15 files changed, 1250 insertions(+), 1349 deletions(-) create mode 100644 autotest/conftest.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c181447651d..4f8244733ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,6 +149,8 @@ jobs: - name: Get executables working-directory: modflow6/autotest + env: + GITHUB_TOKEN: ${{ github.token }} run: pytest -v --durations 0 get_exes.py - name: Test programs @@ -221,6 +223,8 @@ jobs: - name: Get executables working-directory: modflow6/autotest + env: + GITHUB_TOKEN: ${{ github.token }} run: pytest -v --durations 0 get_exes.py - name: Test modflow6 @@ -327,12 +331,16 @@ jobs: - name: Get executables if: runner.os != 'Windows' working-directory: modflow6/autotest + env: + GITHUB_TOKEN: ${{ github.token }} run: pytest -v --durations 0 get_exes.py - name: Get executables (Windows) if: runner.os == 'Windows' working-directory: modflow6/autotest shell: pwsh + env: + GITHUB_TOKEN: ${{ github.token }} run: pytest -v --durations 0 get_exes.py - name: Test programs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ff97a6ad8f6..b4d4bae2b5a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -103,6 +103,8 @@ jobs: - name: Run benchmarks working-directory: modflow6/distribution + env: + GITHUB_TOKEN: ${{ github.token }} run: python benchmark.py - name: Run sphinx diff --git a/.github/workflows/large.yml b/.github/workflows/large.yml index 3abe23604ff..b18cff74882 100644 --- a/.github/workflows/large.yml +++ b/.github/workflows/large.yml @@ -93,6 +93,8 @@ jobs: - name: Get executables working-directory: modflow6/autotest + env: + GITHUB_TOKEN: ${{ github.token }} run: | pytest -v --durations 0 get_exes.py diff --git a/DEVELOPER.md b/DEVELOPER.md index ddafa361611..f9aa18a23b3 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -152,22 +152,17 @@ We also provide make files which can be used to build MODFLOW 6 with [GNU Make]( For the build instructions we refer to the [GNU Make Manual](https://www.gnu.org/software/make/manual/). -## Running Tests Locally +## Running Tests -For complete testing as done on the CI, clone your fork of the modflow6-testmodels repository (via either ssh or https, ssh shown here): +Tests should pass locally before a PR is opened on Github. All the tests are executed by the CI system and a pull request can only be merged with passing tests. -```shell -git clone git@github.com:/modflow6-testmodels.git -``` -* The modflow6-testmodels repository must be cloned in the same directory that contains the modflow6 repository. - -To run tests first change directory to the `autotest` folder: +Tests must be run from the `autotest` folder: ```shell cd modflow6/autotest ``` -Update your flopy installation by executing +FloPy plugins must first be updated: ```shell python update_flopy.py @@ -186,24 +181,79 @@ Unless you built and installed MODFLOW 6 binaries with meson, you will also have pytest -v build_exes.py ``` -Then the tests can be run with commands similar to these: +Then the tests can be run with `pytest` as usual, for instance: ```shell -# Build MODFLOW 6 tests +# Run all tests with verbose output pytest -v +``` -# Build MODFLOW 6 example tests -pytest -v test_z01_testmodels_mf6.py +Tests can be run in parallel with the `-n` option, which accepts an integer argument for the number of processes to use. If the value `auto` is provided, `pytest-xdist` will use as many processes as your machine has available CPUs: -# Build MODFLOW 5 to 6 converter example tests -pytest -v test_z02_testmodels_mf5to6.py +```shell +pytest -v -n auto ``` -The tests can be run in parallel by adding the flag "-n" which accepts an argument for the specific number of processes to use or "auto" to let pytest decide: +### External model repos + +While many tests create models programmatically, the full suite tests MODFLOW 6 against example models stored in the following external repositories: + +- [`MODFLOW-USGS/modflow6-testmodels`](https://github.com/MODFLOW-USGS/modflow6-testmodels) +- [`MODFLOW-USGS/modflow6-largetestmodels`](https://github.com/MODFLOW-USGS/modflow6-largetestmodels) +- [`MODFLOW-USGS/modflow6-examples`](https://github.com/MODFLOW-USGS/modflow6-examples) + +#### Installing external repos + +By default, the tests expect these repositories side-by-side with (i.e. in the same parent directory as) the `modflow6` repository. If the repos are somewhere else, you can set the `REPOS_PATH` environment variable to point to their parent directory. + +**Note:** a convenient way to persist environment variables needed for tests is to store them in a `.env` file in the `autotest` folder. Each variable should be defined on a separate line, with format `KEY=VALUE`. The `pytest-dotenv` plugin will then automatically load any variables found in this file into the test process' environment. + +##### Test models + +The test model repos can simply be cloned — ideally, into the parent directory of the `modflow6` repository, so that repositories live side-by-side: ```shell -pytest -v -n auto +git clone MODFLOW-USGS/modflow6-testmodels +git clone MODFLOW-USGS/modflow6-largetestmodels ``` -You should execute the test suites before submitting a PR to Github. -All the tests are executed on our Continuous Integration infrastructure and a pull request can only be merged once all tests pass. +##### Example models + +First clone the example models repo: + +```shell +git clone MODFLOW-USGS/modflow6-examples +``` + +The example models require some setup after cloning. Some extra Python dependencies are required to build the examples: + +```shell +cd modflow6-examples/etc +pip install -r requirements.pip.txt +``` + +Then, still from the `etc` folder, run: + +```shell +python ci_build_files.py +``` + +This will build the examples for subsequent use by the tests. + +#### Running external model tests + +External model tests are located in their own files: + +```shell +# Run MODFLOW 6 test models +pytest -v -n auto test_z01_testmodels_mf6.py + +# Run MODFLOW 5 to 6 conversion test models +pytest -v -n auto test_z02_testmodels_mf5to6.py + +# Run example models +pytest -v -n auto test_z03_examples.py + +# Run large test models +pytest -v -n auto test_z03_largetestmodels.py +``` diff --git a/autotest/build_exes.py b/autotest/build_exes.py index fd9b22a51e7..3d71b8da338 100644 --- a/autotest/build_exes.py +++ b/autotest/build_exes.py @@ -1,156 +1,31 @@ -# Build targets +import argparse +from pathlib import Path -# to use ifort on windows, run this -# python build_exes.py -fc ifort +import pytest +from modflow_devtools.build import meson_build -# can compile only mf6 directly using this command: -# python -c "import build_exes; build_exes.test_build_modflow6()" +from conftest import project_root_path -import os -import pathlib as pl -import subprocess as sp -import sys -from contextlib import contextmanager -from framework import running_on_CI +repository = "MODFLOW-USGS/modflow6" +top_bin_path = project_root_path / "bin" -if running_on_CI(): - print("running on CI environment") - os.environ["PYMAKE_DOUBLE"] = "1" -# set OS dependent extensions -eext = "" -soext = ".so" -if sys.platform.lower() == "win32": - eext = ".exe" - soext = ".dll" -elif sys.platform.lower() == "darwin": - soext = ".dylib" +@pytest.fixture +def bin_path(): + return top_bin_path -mfexe_pth = "temp/mfexes" -# use the line below to set fortran compiler using environmental variables -# os.environ["FC"] = "ifort" - -# some flags to check for errors in the code -# add -Werror for compilation to terminate if errors are found -strict_flags = ( - "-fall-intrinsics " - "-Wtabs -Wline-truncation -Wunused-label " - "-Wunused-variable -pedantic -std=f2008 " - "-Wcharacter-truncation" -) - - -@contextmanager -def set_directory(path: str): - """Sets the cwd within the context - - Args: - path (Path): The path to the cwd - - Yields: - None - """ - - origin = os.path.abspath(os.getcwd()) - path = os.path.abspath(path) - try: - os.chdir(path) - print(f"change from {origin} -> {path}") - yield - finally: - os.chdir(origin) - print(f"change from {path} -> {origin}") - - -def relpath_fallback(pth): - try: - # throws ValueError on Windows if pth is on a different drive - return os.path.relpath(pth) - except ValueError: - return os.path.abspath(pth) - - -def create_dir(pth): - # create pth directory - print(f"creating... {os.path.abspath(pth)}") - os.makedirs(pth, exist_ok=True) - - msg = f"could not create... {os.path.abspath(pth)}" - assert os.path.exists(pth), msg - - -def set_compiler_environment_variable(): - fc = None - - # parse command line arguments - for idx, arg in enumerate(sys.argv): - if arg.lower() == "-fc": - fc = sys.argv[idx + 1] - elif arg.lower().startswith("-fc="): - fc = arg.split("=")[1] - - # determine if fc needs to be set to the FC environmental variable - env_var = os.getenv("FC", default="gfortran") - if fc is None and fc != env_var: - fc = env_var - - # validate Fortran compiler - fc_options = ( - "gfortran", - "ifort", +def test_meson_build(bin_path): + meson_build( + project_path=project_root_path, + build_path=project_root_path / "builddir", + bin_path=bin_path ) - if fc not in fc_options: - raise ValueError( - f"Fortran compiler {fc} not supported. Fortran compile must be " - + f"[{', '.join(str(value) for value in fc_options)}]." - ) - - # set FC environment variable - os.environ["FC"] = fc - - -def meson_build( - dir_path: str = "..", - libdir: str = "bin", -): - set_compiler_environment_variable() - is_windows = sys.platform.lower() == "win32" - with set_directory(dir_path): - cmd = ( - "meson setup builddir " - + f"--bindir={os.path.abspath(libdir)} " - + f"--libdir={os.path.abspath(libdir)} " - + "--prefix=" - ) - if is_windows: - cmd += "%CD%" - else: - cmd += "$(pwd)" - if pl.Path("builddir").is_dir(): - cmd += " --wipe" - print(f"setup meson\nrunning...\n {cmd}") - sp.run(cmd, shell=True, check=True) - - cmd = "meson install -C builddir" - print(f"build and install with meson\nrunning...\n {cmd}") - sp.run(cmd, shell=True, check=True) - - -def test_create_dirs(): - pths = [os.path.join("..", "bin"), os.path.join("temp")] - - for pth in pths: - create_dir(pth) - - return - - -def test_meson_build(): - meson_build() if __name__ == "__main__": - test_create_dirs() - test_meson_build() + parser = argparse.ArgumentParser("Rebuild local development version of MODFLOW 6") + parser.add_argument("-p", "--path", help="path to bin directory", default=top_bin_path) + args = parser.parse_args() + test_meson_build(Path(args.path).resolve()) diff --git a/autotest/common_regression.py b/autotest/common_regression.py index 65951f5b6d5..a167d9235ee 100644 --- a/autotest/common_regression.py +++ b/autotest/common_regression.py @@ -1,168 +1,879 @@ import os +import shutil import sys -try: - import pymake -except: - msg = "Error. Pymake package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install https://github.com/modflowpy/pymake/zipball/master" - raise Exception(msg) +ignore_ext = ( + ".hds", + ".hed", + ".bud", + ".cbb", + ".cbc", + ".ddn", + ".ucn", + ".glo", + ".lst", + ".list", + ".gwv", + ".mv", + ".out", +) -try: - import flopy -except: - msg = "Error. FloPy package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install flopy" - raise Exception(msg) +def model_setup(namefile, dst, remove_existing=True, extrafiles=None): + """Setup MODFLOW-based model files for autotests. -def get_home_dir(): - # determine if CI run - is_CI = "CI" in os.environ + Parameters + ---------- + namefile : str + MODFLOW-based model name file. + dst : str + destination path for comparison model or file(s) + remove_existing : bool + boolean indicating if an existing comparision model or file(s) should + be replaced (default is True) + extrafiles : str or list of str + list of extra files to include in the comparision - home = os.path.expanduser("~") + Returns + ------- - if is_CI: - if sys.platform.lower() == "win32": - home = os.path.normpath(os.path.join(os.getcwd(), "..", "..")) + """ + # Construct src pth from namefile or lgr file + src = os.path.dirname(namefile) + + # Create the destination folder, if required + create_dir = False + if os.path.exists(dst): + if remove_existing: + print("Removing folder " + dst) + shutil.rmtree(dst) + create_dir = True else: - cwd_pth = os.getcwd() - - # convert current working directory to a list - cwd_list = cwd_pth.split(sep=os.path.sep) - - # add leading path separator back into list - for idx, pth in enumerate(cwd_list): - if len(pth) < 1: - cwd_list[idx] = os.path.sep - if pth.endswith(":") and sys.platform.lower() == "win32": - cwd_list[idx] += os.path.sep - - ipos = 0 - for idx, s in enumerate(cwd_list): - if s.lower().startswith("modflow6"): - ipos = idx - break + create_dir = True + if create_dir: + os.mkdir(dst) - home = os.path.join(*cwd_list[:ipos]) + # determine if a namefile is a lgr control file - get individual + # name files out of the lgr control file + namefiles = [namefile] + ext = os.path.splitext(namefile)[1] + if ".lgr" in ext.lower(): + lines = [line.rstrip("\n") for line in open(namefile)] + for line in lines: + if len(line) < 1: + continue + if line[0] == "#": + continue + t = line.split() + if ".nam" in t[0].lower(): + fpth = os.path.join(src, t[0]) + namefiles.append(fpth) - print(f"HOME: {home}") + # Make list of files to copy + files2copy = [] + for fpth in namefiles: + files2copy.append(os.path.basename(fpth)) + ext = os.path.splitext(fpth)[1] + # copy additional files contained in the name file and + # associated package files + if ext.lower() == ".nam": + fname = os.path.abspath(fpth) + files2copy = files2copy + get_input_files(fname) - return home + if extrafiles is not None: + if isinstance(extrafiles, str): + extrafiles = [extrafiles] + for fl in extrafiles: + files2copy.append(os.path.basename(fl)) + # Copy the files + for f in files2copy: + srcf = os.path.join(src, f) + dstf = os.path.join(dst, f) -def set_mf6_regression(): - mf6_regression = True - for arg in sys.argv: - if arg.lower() in ("--original_regression", "-oreg"): - mf6_regression = False - break - return mf6_regression - - -def is_directory_available(example_basedir): - available = False - if example_basedir is not None: - available = os.path.isdir(example_basedir) - if not available: - print(f'"{example_basedir}" does not exist') - print(f"no need to run {os.path.basename(__file__)}") - return available - - -def get_example_basedir(home, find_dir, subdir=None): - example_basedir = None - for root, dirs, files in os.walk(home): - for d in dirs: - if d == find_dir or d == find_dir + ".git": - example_basedir = os.path.join(root, d) - if subdir is not None: - example_basedir = os.path.join(example_basedir, subdir) + # Check to see if dstf is going into a subfolder, and create that + # subfolder if it doesn't exist + sf = os.path.dirname(dstf) + if not os.path.isdir(sf): + os.makedirs(sf) + + # Now copy the file + if os.path.exists(srcf): + print("Copy file '" + srcf + "' -> '" + dstf + "'") + shutil.copy(srcf, dstf) + else: + print(srcf + " does not exist") + + return + + +def setup_comparison(namefile, dst, remove_existing=True): + """Setup a comparison model or comparision file(s) for a MODFLOW-based + model. + + Parameters + ---------- + namefile : str + MODFLOW-based model name file. + dst : str + destination path for comparison model or file(s) + remove_existing : bool + boolean indicating if an existing comparision model or file(s) should + be replaced (default is True) + + + Returns + ------- + + """ + # Construct src pth from namefile + src = os.path.dirname(namefile) + action = None + for root, dirs, files in os.walk(src): + dl = [d.lower() for d in dirs] + if any(".cmp" in s for s in dl): + idx = None + for jdx, d in enumerate(dl): + if ".cmp" in d: + idx = jdx + break + if idx is not None: + if "mf2005.cmp" in dl[idx] or "mf2005" in dl[idx]: + action = dirs[idx] + elif "mfnwt.cmp" in dl[idx] or "mfnwt" in dl[idx]: + action = dirs[idx] + elif "mfusg.cmp" in dl[idx] or "mfusg" in dl[idx]: + action = dirs[idx] + elif "mf6.cmp" in dl[idx] or "mf6" in dl[idx]: + action = dirs[idx] + elif "libmf6.cmp" in dl[idx] or "libmf6" in dl[idx]: + action = dirs[idx] + else: + action = dirs[idx] break - if example_basedir is not None: - example_basedir = os.path.abspath(example_basedir) - print(f"Example base directory: {example_basedir}") - break - return example_basedir - - -def get_example_dirs(example_basedir, exclude, prefix="test", find_sim=True): - example_dirs = [ - d - for d in os.listdir(example_basedir) - if prefix in d and d not in exclude - ] - - # make sure mfsim.nam is present in each directory - if find_sim: - remove_dirs = [] - # add_dirs = [] - for temp_dir in example_dirs: - epth = os.path.join(example_basedir, temp_dir) - fpth = os.path.join(epth, "mfsim.nam") - if not os.path.isfile(fpth): - remove_dirs.append(temp_dir) - # for sub_dir in ("mf6gwf", "mf6gwt"): - # tpth = os.path.join(epth, sub_dir) - # fpth = os.path.join(tpth, "mfsim.nam") - # if os.path.isfile(fpth): - # add_dirs.append(os.path.join(temp_dir, sub_dir)) - - for remove_dir in remove_dirs: - example_dirs.remove(remove_dir) - - # example_dirs += add_dirs - - # sort in numerical order for case sensitive os - example_dirs = sorted( - example_dirs, key=lambda v: (v.upper(), v[0].islower()) + if action is not None: + dst = os.path.join(dst, f"{action}") + if not os.path.isdir(dst): + try: + os.mkdir(dst) + except: + print("Could not make " + dst) + # clean directory + else: + print(f"cleaning...{dst}") + for root, dirs, files in os.walk(dst): + for f in files: + tpth = os.path.join(root, f) + print(f" removing...{tpth}") + os.remove(tpth) + for d in dirs: + tdir = os.path.join(root, d) + print(f" removing...{tdir}") + shutil.rmtree(tdir) + # copy files + cmppth = os.path.join(src, action) + files = os.listdir(cmppth) + files2copy = [] + if action.lower() == ".cmp": + for file in files: + if ".cmp" in os.path.splitext(file)[1].lower(): + files2copy.append(os.path.join(cmppth, file)) + for srcf in files2copy: + f = os.path.basename(srcf) + dstf = os.path.join(dst, f) + # Now copy the file + if os.path.exists(srcf): + print("Copy file '" + srcf + "' -> '" + dstf + "'") + shutil.copy(srcf, dstf) + else: + print(srcf + " does not exist") + else: + for file in files: + if ".nam" in os.path.splitext(file)[1].lower(): + files2copy.append( + os.path.join(cmppth, os.path.basename(file)) + ) + nf = os.path.join(src, action, os.path.basename(file)) + model_setup(nf, dst, remove_existing=remove_existing) + break + + return action + + +def get_input_files(namefile): + """Return a list of all the input files in this model. + + Parameters + ---------- + namefile : str + path to a MODFLOW-based model name file + + Returns + ------- + filelist : list + list of MODFLOW-based model input files + + """ + srcdir = os.path.dirname(namefile) + filelist = [] + fname = os.path.join(srcdir, namefile) + with open(fname, "r") as f: + lines = f.readlines() + + for line in lines: + ll = line.strip().split() + if len(ll) < 2: + continue + if line.strip()[0] in ["#", "!"]: + continue + ext = os.path.splitext(ll[2])[1] + if ext.lower() not in ignore_ext: + if len(ll) > 3: + if "replace" in ll[3].lower(): + continue + filelist.append(ll[2]) + + # Now go through every file and look for other files to copy, + # such as 'OPEN/CLOSE'. If found, then add that file to the + # list of files to copy. + otherfiles = [] + for fname in filelist: + fname = os.path.join(srcdir, fname) + try: + f = open(fname, "r") + for line in f: + + # Skip invalid lines + ll = line.strip().split() + if len(ll) < 2: + continue + if line.strip()[0] in ["#", "!"]: + continue + + if "OPEN/CLOSE" in line.upper(): + for i, s in enumerate(ll): + if "OPEN/CLOSE" in s.upper(): + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + otherfiles.append(stmp) + break + except: + print(fname + " does not exist") + + filelist = filelist + otherfiles + + return filelist + + +def get_namefiles(pth, exclude=None): + """Search through a path (pth) for all .nam files. + + Parameters + ---------- + pth : str + path to model files + exclude : str or lst + File or list of files to exclude from the search (default is None) + + Returns + ------- + namefiles : lst + List of namefiles with paths + + """ + namefiles = [] + for root, _, files in os.walk(pth): + namefiles += [ + os.path.join(root, file) for file in files if file.endswith(".nam") + ] + if exclude is not None: + if isinstance(exclude, str): + exclude = [exclude] + exclude = [e.lower() for e in exclude] + pop_list = [] + for namefile in namefiles: + for e in exclude: + if e in namefile.lower(): + pop_list.append(namefile) + for e in pop_list: + namefiles.remove(e) + + return namefiles + + +def get_sim_name(namefiles, rootpth=None): + """Get simulation name. + + Parameters + ---------- + namefiles : str or list of strings + path(s) to MODFLOW-based model name files + rootpth : str + optional root directory path (default is None) + + Returns + ------- + simname : list + list of namefiles without the file extension + + """ + if isinstance(namefiles, str): + namefiles = [namefiles] + sim_name = [] + for namefile in namefiles: + t = namefile.split(os.sep) + if rootpth is None: + idx = -1 + else: + idx = t.index(os.path.split(rootpth)[1]) + + # build dst with everything after the rootpth and before + # the namefile file name. + dst = "" + if idx < len(t): + for d in t[idx + 1 : -1]: + dst += f"{d}_" + + # add namefile basename without extension + dst += t[-1].replace(".nam", "") + sim_name.append(dst) + + return sim_name + + +def setup_mf6( + src, dst, mfnamefile="mfsim.nam", extrafiles=None, remove_existing=True +): + """Copy all of the MODFLOW 6 input files from the src directory to the dst + directory. + + Parameters + ---------- + src : src + directory path with original MODFLOW 6 input files + dst : str + directory path that original MODFLOW 6 input files will be copied to + mfnamefile : str + optional MODFLOW 6 simulation name file (default is mfsim.nam) + extrafiles : bool + boolean indicating if extra files should be included (default is None) + remove_existing : bool + boolean indicating if existing file in dst should be removed (default + is True) + + Returns + ------- + mf6inp : list + list of MODFLOW 6 input files + mf6outp : list + list of MODFLOW 6 output files + + """ + + # Create the destination folder + create_dir = False + if os.path.exists(dst): + if remove_existing: + print("Removing folder " + dst) + shutil.rmtree(dst) + create_dir = True + else: + create_dir = True + if create_dir: + os.makedirs(dst) + + # Make list of files to copy + fname = os.path.join(src, mfnamefile) + fname = os.path.abspath(fname) + mf6inp, mf6outp = get_mf6_files(fname) + files2copy = [mfnamefile] + mf6inp + + # determine if there are any .ex files + exinp = [] + for f in mf6outp: + ext = os.path.splitext(f)[1] + if ext.lower() == ".hds": + pth = os.path.join(src, f + ".ex") + if os.path.isfile(pth): + exinp.append(f + ".ex") + if len(exinp) > 0: + files2copy += exinp + if extrafiles is not None: + files2copy += extrafiles + + # Copy the files + for f in files2copy: + srcf = os.path.join(src, f) + dstf = os.path.join(dst, f) + + # Check to see if dstf is going into a subfolder, and create that + # subfolder if it doesn't exist + sf = os.path.dirname(dstf) + if not os.path.isdir(sf): + try: + os.mkdir(sf) + except: + print("Could not make " + sf) + + # Now copy the file + if os.path.exists(srcf): + print("Copy file '" + srcf + "' -> '" + dstf + "'") + shutil.copy(srcf, dstf) + else: + print(srcf + " does not exist") + + return mf6inp, mf6outp + + +def get_mf6_comparison(src): + """Determine comparison type for MODFLOW 6 simulation. + + Parameters + ---------- + src : str + directory path to search for comparison types + + Returns + ------- + action : str + comparison type + + """ + action = None + # Possible comparison - the order matters + optcomp = ( + "compare", + ".cmp", + "mf2005", + "mf2005.cmp", + "mfnwt", + "mfnwt.cmp", + "mfusg", + "mfusg.cmp", + "mflgr", + "mflgr.cmp", + "libmf6", + "libmf6.cmp", + "mf6", + "mf6.cmp", ) + # Construct src pth from namefile + action = None + for _, dirs, _ in os.walk(src): + dl = [d.lower() for d in dirs] + for oc in optcomp: + if any(oc in s for s in dl): + action = oc + break + return action + + +def setup_mf6_comparison(src, dst, remove_existing=True): + """Setup comparision for MODFLOW 6 simulation. + + Parameters + ---------- + src : src + directory path with original MODFLOW 6 input files + dst : str + directory path that original MODFLOW 6 input files will be copied to + remove_existing : bool + boolean indicating if existing file in dst should be removed (default + is True) + + Returns + ------- + action : str + comparison type - return example_dirs - - -def get_select_dirs(select_dirs, dirs): - found_dirs = [] - for d in select_dirs: - if d.endswith("*"): - for test_dir in dirs: - if test_dir.startswith(d.replace("*", "")): - found_dirs.append(test_dir) - elif d.endswith("+"): - dd = d.replace("+", "") - for test_dir in dirs: - sorted_list = sorted([dd, test_dir], reverse=True) - if sorted_list[0] == test_dir: - found_dirs.append(test_dir) - elif d.endswith("-"): - dd = d.replace("-", "") - for test_dir in dirs: - sorted_list = sorted([dd, test_dir]) - if sorted_list[0] == test_dir or dd in sorted_list[0]: - found_dirs.append(test_dir) + """ + # get the type of comparison to use (compare, mf2005, etc.) + action = get_mf6_comparison(src) + + if action is not None: + dst = os.path.join(dst, f"{action}") + if not os.path.isdir(dst): + try: + os.mkdir(dst) + except: + print("Could not make " + dst) + # clean directory + else: + print(f"cleaning...{dst}") + for root, dirs, files in os.walk(dst): + for f in files: + tpth = os.path.join(root, f) + print(f" removing...{tpth}") + os.remove(tpth) + for d in dirs: + tdir = os.path.join(root, d) + print(f" removing...{tdir}") + shutil.rmtree(tdir) + # copy files + cmppth = os.path.join(src, action) + files = os.listdir(cmppth) + files2copy = [] + if action.lower() == "compare" or action.lower() == ".cmp": + for file in files: + if ".cmp" in os.path.splitext(file)[1].lower(): + files2copy.append(os.path.join(cmppth, file)) + for srcf in files2copy: + f = os.path.basename(srcf) + dstf = os.path.join(dst, f) + # Now copy the file + if os.path.exists(srcf): + print("Copy file '" + srcf + "' -> '" + dstf + "'") + shutil.copy(srcf, dstf) + else: + print(srcf + " does not exist") else: - if d in dirs: - found_dirs.append(d) + if "mf6" in action.lower(): + for file in files: + if "mfsim.nam" in file.lower(): + srcf = os.path.join(cmppth, os.path.basename(file)) + files2copy.append(srcf) + srcdir = os.path.join(src, action) + setup_mf6(srcdir, dst, remove_existing=remove_existing) + break + else: + for file in files: + if ".nam" in os.path.splitext(file)[1].lower(): + srcf = os.path.join(cmppth, os.path.basename(file)) + files2copy.append(srcf) + nf = os.path.join(src, action, os.path.basename(file)) + model_setup(nf, dst, remove_existing=remove_existing) + break - return found_dirs + return action -def get_select_packages(select_packages, exdir, dirs): - found_dirs = [] - for d in dirs: - pth = os.path.join(exdir, d) - namefiles = pymake.get_namefiles(pth) - ftypes = [] - for namefile in namefiles: - ftype = pymake.get_mf6_ftypes(namefile, select_packages) - if ftype not in ftypes: - ftypes += ftype - if len(ftypes) > 0: - ftypes = [item.upper() for item in ftypes] - for pak in select_packages: - if pak in ftypes: - found_dirs.append(d) - break - return found_dirs +def get_mf6_nper(tdisfile): + """Return the number of stress periods in the MODFLOW 6 model. + + Parameters + ---------- + tdisfile : str + path to the TDIS file + + Returns + ------- + nper : int + number of stress periods in the simulation + + """ + with open(tdisfile, "r") as f: + lines = f.readlines() + line = [line for line in lines if "NPER" in line.upper()][0] + nper = line.strip().split()[1] + return nper + + +def get_mf6_mshape(disfile): + """Return the shape of the MODFLOW 6 model. + + Parameters + ---------- + disfile : str + path to a MODFLOW 6 discretization file + + Returns + ------- + mshape : tuple + tuple with the shape of the MODFLOW 6 model. + + """ + with open(disfile, "r") as f: + lines = f.readlines() + + d = {} + for line in lines: + + # Skip over blank and commented lines + ll = line.strip().split() + if len(ll) < 2: + continue + if line.strip()[0] in ["#", "!"]: + continue + + for key in ["NODES", "NCPL", "NLAY", "NROW", "NCOL"]: + if ll[0].upper() in key: + d[key] = int(ll[1]) + + if "NODES" in d: + mshape = (d["NODES"],) + elif "NCPL" in d: + mshape = (d["NLAY"], d["NCPL"]) + elif "NLAY" in d: + mshape = (d["NLAY"], d["NROW"], d["NCOL"]) + else: + print(d) + raise Exception("Could not determine model shape") + return mshape + + +def get_mf6_files(mfnamefile): + """Return a list of all the MODFLOW 6 input and output files in this model. + + Parameters + ---------- + mfnamefile : str + path to the MODFLOW 6 simulation name file + + Returns + ------- + filelist : list + list of MODFLOW 6 input files in a simulation + outplist : list + list of MODFLOW 6 output files in a simulation + + """ + + srcdir = os.path.dirname(mfnamefile) + filelist = [] + outplist = [] + + filekeys = ["TDIS6", "GWF6", "GWT", "GWF6-GWF6", "GWF-GWT", "IMS6"] + namefilekeys = ["GWF6", "GWT"] + namefiles = [] + + with open(mfnamefile) as f: + + # Read line and skip comments + lines = f.readlines() + + for line in lines: + + # Skip over blank and commented lines + ll = line.strip().split() + if len(ll) < 2: + continue + if line.strip()[0] in ["#", "!"]: + continue + + for key in filekeys: + if key in ll[0].upper(): + fname = ll[1] + filelist.append(fname) + + for key in namefilekeys: + if key in ll[0].upper(): + fname = ll[1] + namefiles.append(fname) + + # Go through name files and get files + for namefile in namefiles: + fname = os.path.join(srcdir, namefile) + with open(fname, "r") as f: + lines = f.readlines() + insideblock = False + + for line in lines: + ll = line.upper().strip().split() + if len(ll) < 2: + continue + if ll[0] in "BEGIN" and ll[1] in "PACKAGES": + insideblock = True + continue + if ll[0] in "END" and ll[1] in "PACKAGES": + insideblock = False + + if insideblock: + ll = line.strip().split() + if len(ll) < 2: + continue + if line.strip()[0] in ["#", "!"]: + continue + filelist.append(ll[1]) + + # Recursively go through every file and look for other files to copy, + # such as 'OPEN/CLOSE' and 'TIMESERIESFILE'. If found, then + # add that file to the list of files to copy. + flist = filelist + # olist = outplist + while True: + olist = [] + flist, olist = _get_mf6_external_files(srcdir, olist, flist) + # add to filelist + if len(flist) > 0: + filelist = filelist + flist + # add to outplist + if len(olist) > 0: + outplist = outplist + olist + # terminate loop if no additional files + # if len(flist) < 1 and len(olist) < 1: + if len(flist) < 1: + break + + return filelist, outplist + + +def _get_mf6_external_files(srcdir, outplist, files): + """Get list of external files in a MODFLOW 6 simulation. + + Parameters + ---------- + srcdir : str + path to a directory containing a MODFLOW 6 simulation + outplist : list + list of output files in a MODFLOW 6 simulation + files : list + list of MODFLOW 6 name files + + Returns + ------- + + """ + extfiles = [] + + for fname in files: + fname = os.path.join(srcdir, fname) + try: + f = open(fname, "r") + for line in f: + + # Skip invalid lines + ll = line.strip().split() + if len(ll) < 2: + continue + if line.strip()[0] in ["#", "!"]: + continue + + if "OPEN/CLOSE" in line.upper(): + for i, s in enumerate(ll): + if s.upper() == "OPEN/CLOSE": + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + extfiles.append(stmp) + break + + if "TS6" in line.upper(): + for i, s in enumerate(ll): + if s.upper() == "FILEIN": + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + extfiles.append(stmp) + break + + if "TAS6" in line.upper(): + for i, s in enumerate(ll): + if s.upper() == "FILEIN": + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + extfiles.append(stmp) + break + + if "OBS6" in line.upper(): + for i, s in enumerate(ll): + if s.upper() == "FILEIN": + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + extfiles.append(stmp) + break + + if "EXTERNAL" in line.upper(): + for i, s in enumerate(ll): + if s.upper() == "EXTERNAL": + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + extfiles.append(stmp) + break + + if "FILE" in line.upper(): + for i, s in enumerate(ll): + if s.upper() == "FILEIN": + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + extfiles.append(stmp) + break + + if "FILE" in line.upper(): + for i, s in enumerate(ll): + if s.upper() == "FILEOUT": + stmp = ll[i + 1] + stmp = stmp.replace('"', "") + stmp = stmp.replace("'", "") + outplist.append(stmp) + break + + except: + print("could not get a list of external mf6 files") + + return extfiles, outplist + + +def get_mf6_ftypes(namefile, ftypekeys): + """Return a list of FTYPES that are in the name file and in ftypekeys. + + Parameters + ---------- + namefile : str + path to a MODFLOW 6 name file + ftypekeys : list + list of desired FTYPEs + + Returns + ------- + ftypes : list + list of FTYPES that match ftypekeys in namefile + + """ + with open(namefile, "r") as f: + lines = f.readlines() + + ftypes = [] + for line in lines: + + # Skip over blank and commented lines + ll = line.strip().split() + if len(ll) < 2: + continue + if line.strip()[0] in ["#", "!"]: + continue + + for key in ftypekeys: + if ll[0].upper() in key: + ftypes.append(ll[0]) + + return ftypes + + +def get_mf6_blockdata(f, blockstr): + """Return list with all non comments between start and end of block + specified by blockstr. + + Parameters + ---------- + f : file object + open file object + blockstr : str + name of block to search + + Returns + ------- + data : list + list of data in specified block + + """ + data = [] + + # find beginning of block + for line in f: + if line[0] != "#": + t = line.split() + if t[0].lower() == "begin" and t[1].lower() == blockstr.lower(): + break + for line in f: + if line[0] != "#": + t = line.split() + if t[0].lower() == "end" and t[1].lower() == blockstr.lower(): + break + else: + data.append(line.rstrip()) + return data diff --git a/autotest/conftest.py b/autotest/conftest.py new file mode 100644 index 00000000000..5a1c9ffc112 --- /dev/null +++ b/autotest/conftest.py @@ -0,0 +1,49 @@ +from pathlib import Path + +import pytest +from modflow_devtools.executables import Executables, build_default_exe_dict + +pytest_plugins = ["modflow_devtools.fixtures"] +project_root_path = Path(__file__).parent.parent + + +def should_compare(test: str, comparisons: dict, executables: Executables) -> bool: + if test in comparisons.keys(): + version = Executables.get_version(path=executables.mf6) + print(f"MODFLOW 6 development version='{version}'") + version = Executables.get_version(path=executables.mf6_regression) + print(f"MODFLOW 6 regression version='{version}'") + if version in comparisons[test]: + print( + f"Test {test} does not run with versions {comparisons[test]}" + ) + print( + f"Skipping regression test of sim {test} because the version is {version}" + ) + return False + return True + + +@pytest.fixture(scope="session") +def bin_path() -> Path: + return project_root_path / "bin" + + +@pytest.fixture(scope="session") +def targets(bin_path) -> Executables: + return Executables(**build_default_exe_dict(bin_path)) + + +@pytest.fixture +def original_regression(request) -> bool: + oreg = request.config.getoption("--original-regression") + return oreg + + +def pytest_addoption(parser): + parser.addoption( + "--original-regression", + action="store_true", + default=False, + help="TODO" + ) diff --git a/autotest/get_exes.py b/autotest/get_exes.py index 7530f31ca3f..4b2d31c870e 100644 --- a/autotest/get_exes.py +++ b/autotest/get_exes.py @@ -1,119 +1,93 @@ -# Get executables - -import os -import shutil +import argparse +from pathlib import Path +from tempfile import TemporaryDirectory +from warnings import warn +import flopy +import pytest from flaky import flaky -import pymake - -from build_exes import meson_build -from framework import running_on_CI - -if running_on_CI(): - print("running on CI environment") - os.environ["PYMAKE_DOUBLE"] = "1" - -# path to rebuilt executables for previous versions of MODFLOW -rebuilt_bindir = os.path.join("..", "bin", "rebuilt") - -if not os.path.exists(rebuilt_bindir): - os.makedirs(rebuilt_bindir) - -# paths to downloaded for previous versions of MODFLOW -downloaded_bindir = os.path.join("..", "bin", "downloaded") - -if not os.path.exists(downloaded_bindir): - os.makedirs(downloaded_bindir) - - -mfexe_pth = "temp/mfexes" +from modflow_devtools.build import meson_build +from modflow_devtools.download import download_and_unzip, get_release +from modflow_devtools.misc import get_ostag -# use the line below to set fortran compiler using environmental variables -# os.environ["FC"] = "gfortran" +from conftest import project_root_path -# some flags to check for errors in the code -# add -Werror for compilation to terminate if errors are found -strict_flags = ( - "-Wtabs -Wline-truncation -Wunused-label " - "-Wunused-variable -pedantic -std=f2008 " - "-Wcharacter-truncation" -) +repository = "MODFLOW-USGS/modflow6" +top_bin_path = project_root_path / "bin" -def get_compiler_envvar(fc): - env_var = os.environ.get("FC") - if env_var is not None: - if env_var != fc: - fc = env_var - return fc +def get_asset_name(asset: dict) -> str: + ostag = get_ostag() + name = asset["name"] + if "win" in ostag: + return name + else: + prefix = name.rpartition('_')[0] + prefix += f"_{ostag}" + return f"{prefix}.zip" -def create_dir(pth): - # create pth directory - print(f"creating... {os.path.abspath(pth)}") - os.makedirs(pth, exist_ok=True) +@pytest.fixture +def rebuilt_bin_path() -> Path: + return top_bin_path / "rebuilt" - msg = f"could not create... {os.path.abspath(pth)}" - assert os.path.exists(pth), msg - -def rebuild_mf6_release(): - target = "mf6" - download_pth = os.path.join("temp") - target_dict = pymake.usgs_program_data.get_target(target) - - pymake.download_and_unzip( - target_dict["url"], - pth=download_pth, - verbose=True, - ) - - # update IDEVELOP MODE in the release - srcpth = os.path.join( - download_pth, target_dict["dirname"], target_dict["srcdir"] - ) - fpth = os.path.join(srcpth, "Utilities", "version.f90") - with open(fpth) as f: - lines = f.read().splitlines() - assert len(lines) > 0, f"could not update {srcpth}" - - f = open(fpth, "w") - for line in lines: - tag = "IDEVELOPMODE = 0" - if tag in line: - line = line.replace(tag, "IDEVELOPMODE = 1") - f.write(f"{line}\n") - f.close() - - # build release source files with Meson - root_path = os.path.join(download_pth, target_dict["dirname"]) - meson_build(dir_path=root_path, libdir=os.path.abspath(rebuilt_bindir)) - - -def test_create_dirs(): - pths = [os.path.join("..", "bin"), os.path.join("temp")] - - for pth in pths: - create_dir(pth) +@pytest.fixture +def downloaded_bin_path() -> Path: + return top_bin_path / "downloaded" @flaky(max_runs=3) -def test_getmfexes(verify=True): - pymake.getmfexes(mfexe_pth, verify=verify) - for target in os.listdir(mfexe_pth): - srcpth = os.path.join(mfexe_pth, target) - if os.path.isfile(srcpth): - dstpth = os.path.join(downloaded_bindir, target) - print(f"copying {srcpth} -> {dstpth}") - shutil.copy(srcpth, dstpth) +def test_rebuild_release(rebuilt_bin_path: Path): + print(f"Rebuilding and installing last release to: {rebuilt_bin_path}") + release = get_release(repository) + assets = release["assets"] + ostag = get_ostag() + asset = next(iter([a for a in assets if a["name"] == get_asset_name(a)]), None) + if not asset: + warn(f"Couldn't find asset for OS {get_ostag()}, available assets:\n{assets}") + + with TemporaryDirectory() as td: + download_path = Path(td) + download_and_unzip( + asset["browser_download_url"], + path=download_path, + verbose=True, + ) + + # update IDEVELOPMODE + source_files_path = download_path / asset["name"].replace(".zip", "") / "src" + version_file_path = source_files_path / "Utilities" / "version.f90" + with open(version_file_path) as f: + lines = f.read().splitlines() + assert len(lines) > 0, f"File is empty: {source_files_path}" + with open(version_file_path, "w") as f: + for line in lines: + tag = "IDEVELOPMODE = 0" + if tag in line: + line = line.replace(tag, "IDEVELOPMODE = 1") + f.write(f"{line}\n") + + # rebuild with Meson + meson_build( + project_path=source_files_path.parent, + build_path=download_path / "builddir", + bin_path=rebuilt_bin_path + ) @flaky(max_runs=3) -def test_rebuild_mf6_release(): - rebuild_mf6_release() +def test_get_executables(downloaded_bin_path: Path): + print(f"Installing MODFLOW-related executables to: {downloaded_bin_path}") + downloaded_bin_path.mkdir(exist_ok=True, parents=True) + flopy.utils.get_modflow(str(downloaded_bin_path)) if __name__ == "__main__": - test_create_dirs() - test_getmfexes(verify=False) - test_rebuild_mf6_release() + parser = argparse.ArgumentParser("Get executables needed for MODFLOW 6 testing") + parser.add_argument("-p", "--path", help="path to top-level bin directory") + args = parser.parse_args() + bin_path = Path(args.path).resolve() if args.path else top_bin_path + + test_get_executables(bin_path / "downloaded") + test_rebuild_release(bin_path / "rebuilt") diff --git a/autotest/simulation.py b/autotest/simulation.py index 18fb064f30c..a354d5873c3 100644 --- a/autotest/simulation.py +++ b/autotest/simulation.py @@ -4,25 +4,12 @@ import time import numpy as np +import flopy +from flopy.utils.compare import compare_heads -try: - import pymake -except: - msg = "Error. Pymake package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install https://github.com/modflowpy/pymake/zipball/master" - raise Exception(msg) - -try: - import flopy -except: - msg = "Error. FloPy package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install flopy" - raise Exception(msg) - -import targets +from common_regression import get_namefiles, get_mf6_files, setup_mf6, setup_mf6_comparison, get_mf6_comparison from framework import running_on_CI, set_teardown_test +import targets sfmt = "{:25s} - {}" extdict = { @@ -49,6 +36,7 @@ def __init__( api_func=None, mf6_regression=False, make_comparison=True, + simpath=None, ): teardown_test = set_teardown_test() for idx, arg in enumerate(sys.argv): @@ -85,7 +73,7 @@ def __init__( print(msg) self.name = name self.exfunc = exfunc - self.simpath = None + self.simpath = simpath self.inpt = None self.outp = None self.coutp = None @@ -157,7 +145,7 @@ def set_model(self, pth, testModel=True): # get MODFLOW 6 output file names fpth = os.path.join(pth, "mfsim.nam") - mf6inp, mf6outp = pymake.get_mf6_files(fpth) + mf6inp, mf6outp = get_mf6_files(fpth) self.outp = mf6outp # determine comparison model @@ -168,7 +156,7 @@ def set_model(self, pth, testModel=True): # self.action = pymake.get_mf6_comparison(pth) if self.action is not None: if "mf6" in self.action or "mf6-regression" in self.action: - cinp, self.coutp = pymake.get_mf6_files(fpth) + cinp, self.coutp = get_mf6_files(fpth) def setup(self, src, dst): msg = sfmt.format("Setup test", self.name) @@ -181,7 +169,7 @@ def setup(self, src, dst): + f"{os.path.abspath(os.getcwd())}" ) try: - self.inpt, self.outp = pymake.setup_mf6(src=src, dst=dst) + self.inpt, self.outp = setup_mf6(src=src, dst=dst) print("waiting...") time.sleep(0.5) success = True @@ -225,11 +213,11 @@ def setup_comparison(self, src, dst, testModel=True): shutil.rmtree(pth) shutil.copytree(dst, pth) elif testModel: - action = pymake.setup_mf6_comparison( + action = setup_mf6_comparison( src, dst, remove_existing=self.teardown_test ) else: - action = pymake.get_mf6_comparison(dst) + action = get_mf6_comparison(dst) self.action = action @@ -316,7 +304,7 @@ def run(self): ): nam = None else: - npth = pymake.get_namefiles(cpth)[0] + npth = get_namefiles(cpth)[0] nam = os.path.basename(npth) self.nam_cmp = nam try: @@ -379,7 +367,7 @@ def compare(self): files_cmp.append(file) elif "mf6" in self.action: fpth = os.path.join(cpth, "mfsim.nam") - cinp, self.coutp = pymake.get_mf6_files(fpth) + cinp, self.coutp = get_mf6_files(fpth) head_extensions = ( "hds", @@ -473,7 +461,7 @@ def compare(self): print(txt) # make comparison - success_tst = pymake.compare_heads( + success_tst = compare_heads( None, pth, precision="double", @@ -635,7 +623,7 @@ def _compare_heads(self, msgall, extensions="hds"): outfile = os.path.join( self.simpath, outfile + f".{extension}.cmp.out" ) - success_tst = pymake.compare_heads( + success_tst = compare_heads( None, None, precision="double", @@ -671,7 +659,7 @@ def _compare_concentrations(self, msgall, extensions="ucn"): outfile = os.path.join( self.simpath, outfile + f".{extension}.cmp.out" ) - success_tst = pymake.compare_heads( + success_tst = compare_heads( None, None, precision="double", diff --git a/autotest/test_gwf_henry_nr.py b/autotest/test_gwf_henry_nr.py index ec9ffaacbe2..73c800532f9 100644 --- a/autotest/test_gwf_henry_nr.py +++ b/autotest/test_gwf_henry_nr.py @@ -18,9 +18,9 @@ msg += " pip install flopy" raise Exception(msg) +from conftest import should_compare from framework import testing_framework from simulation import Simulation -from targets import get_mf6_version ex = ["gwf_henrynr01"] exdirs = [] @@ -245,24 +245,12 @@ def build_model(idx, dir): return sim, None -def set_make_comparison(): - version = get_mf6_version() - print(f"MODFLOW version='{version}'") - version = get_mf6_version(version="mf6-regression") - print(f"MODFLOW regression version='{version}'") - if version in ("6.2.1",): - make_comparison = False - else: - make_comparison = True - return make_comparison - - # - No need to change any code below @pytest.mark.parametrize( "idx, dir", list(enumerate(exdirs)), ) -def test_mf6model(idx, dir): +def test_mf6model(idx, dir, targets): # initialize testing framework test = testing_framework() @@ -276,33 +264,6 @@ def test_mf6model(idx, dir): idxsim=idx, mf6_regression=True, cmp_verbose=False, - make_comparison=set_make_comparison(), + make_comparison=should_compare("gwf_henry_nr", comparisons={"gwf_henry_nr": ("6.2.1",)}, executables=targets), ) ) - - -def main(): - # initialize testing framework - test = testing_framework() - - # run the test model - for idx, on_dir in enumerate(exdirs): - test.build_mf6_models(build_model, idx, dir) - sim = Simulation( - on_dir, - idxsim=idx, - mf6_regression=True, - cmp_verbose=True, - make_comparison=set_make_comparison(), - ) - test.run_mf6(sim) - - return - - -if __name__ == "__main__": - # print message - print(f"standalone run of {os.path.basename(__file__)}") - - # run main routine - main() diff --git a/autotest/test_mf6_tmp_simulations.py b/autotest/test_mf6_tmp_simulations.py index c97cea10dc8..c6ee3c22b1a 100644 --- a/autotest/test_mf6_tmp_simulations.py +++ b/autotest/test_mf6_tmp_simulations.py @@ -3,22 +3,7 @@ import pytest -try: - import pymake -except: - msg = "Error. Pymake package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install https://github.com/modflowpy/pymake/zipball/master" - raise Exception(msg) - -try: - import flopy -except: - msg = "Error. FloPy package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install flopy" - raise Exception(msg) - +from common_regression import get_mf6_ftypes, get_namefiles from simulation import Simulation exdir = os.path.join("..", "tmp_simulations") @@ -87,10 +72,10 @@ def get_mf6_models(): found_dirs = [] for d in dirs: pth = os.path.join(exdir, d) - namefiles = pymake.get_namefiles(pth) + namefiles = get_namefiles(pth) ftypes = [] for namefile in namefiles: - ftype = pymake.autotest.get_mf6_ftypes( + ftype = get_mf6_ftypes( namefile, select_packages ) if ftype not in ftypes: diff --git a/autotest/test_z01_testmodels_mf6.py b/autotest/test_z01_testmodels_mf6.py index de6a5339e94..57c9c810c79 100644 --- a/autotest/test_z01_testmodels_mf6.py +++ b/autotest/test_z01_testmodels_mf6.py @@ -1,265 +1,55 @@ -import os -import subprocess -import sys - import pytest -try: - import pymake -except: - msg = "Error. Pymake package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install https://github.com/modflowpy/pymake/zipball/master" - raise Exception(msg) - -try: - import flopy -except: - msg = "Error. FloPy package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install flopy" - raise Exception(msg) -from common_regression import ( - get_example_basedir, - get_example_dirs, - get_home_dir, - get_select_dirs, - get_select_packages, - is_directory_available, - set_mf6_regression, -) +from conftest import should_compare from simulation import Simulation -from targets import get_mf6_version - -# find path to examples directory -home = get_home_dir() - -find_dir = "modflow6-testmodels" -example_basedir = get_example_basedir(home, find_dir, subdir="mf6") - -if example_basedir is not None: - assert os.path.isdir(example_basedir) - - -def get_branch(): - try: - # determine current buildstat branch - b = subprocess.Popen( - ("git", "status"), stdout=subprocess.PIPE, stderr=subprocess.STDOUT - ).communicate()[0] - if isinstance(b, bytes): - b = b.decode("utf-8") - - # determine current buildstat branch - for line in b.splitlines(): - if "On branch" in line: - branch = line.replace("On branch ", "").rstrip() - except: - branch = None - - return branch - - -def get_mf6_models(): - """ - Get a list of test models - """ - - # determine if test directory exists - dir_avail = is_directory_available(example_basedir) - if not dir_avail: - return [] - - # determine if running on travis - is_CI = "CI" in os.environ - - # get current branch - if is_CI: - branch = os.path.basename(os.environ["GITHUB_REF"]) - else: - branch = get_branch() - print(f"On branch {branch}") - - # tuple of example files to exclude - # exclude = (None,) - exclude = ("test205_gwtbuy-henrytidal",) - - # update exclude - if is_CI: - exclude_CI = (None,) - exclude = exclude + exclude_CI - exclude = list(exclude) - - # write a summary of the files to exclude - print("list of tests to exclude:") - for idx, ex in enumerate(exclude): - print(f" {idx + 1}: {ex}") - - # build list of directories with valid example files - if example_basedir is not None: - example_dirs = get_example_dirs( - example_basedir, exclude, prefix="test" - ) - else: - example_dirs = [] - - # exclude dev examples on master or release branches - if "master" in branch.lower() or "release" in branch.lower() or branch.lower().startswith("v6"): - drmv = [] - for d in example_dirs: - if "_dev" in d.lower(): - drmv.append(d) - for d in drmv: - example_dirs.remove(d) - - # determine if only a selection of models should be run - select_example_dirs = None - select_packages = None - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--sim": - if len(sys.argv) > idx + 1: - select_example_dirs = sys.argv[idx + 1 :] - break - elif arg.lower() == "--pak": - if len(sys.argv) > idx + 1: - select_packages = sys.argv[idx + 1 :] - select_packages = [item.upper() for item in select_packages] - break - elif arg.lower() == "--match": - if len(sys.argv) > idx + 1: - like = sys.argv[idx + 1] - example_dirs = [item for item in example_dirs if like in item] - break - - # determine if the selection of model is in the test models to evaluate - if select_example_dirs is not None: - example_dirs = get_select_dirs(select_example_dirs, example_dirs) - if len(example_dirs) < 1: - msg = "Selected models not available in test" - print(msg) - # determine if the specified package(s) is in the test models to evaluate - if select_packages is not None: - example_dirs = get_select_packages( - select_packages, example_basedir, example_dirs - ) - if len(example_dirs) < 1: - msg = "Selected packages not available [" - for pak in select_packages: - msg += f" {pak}" - msg += "]" - print(msg) - - return example_dirs +excluded = ["alt_model"] +comparisons = { + "test001e_noUZF_3lay": ("6.2.1",), + "test005_advgw_tidal": ("6.2.1",), + "test017_Crinkle": ("6.2.1",), + "test028_sfr": ("6.2.1",), + "test028_sfr_rewet": ("6.2.1",), + "test028_sfr_rewet_nr": ("6.2.1",), + "test028_sfr_rewet_simple": ("6.2.1",), + "test028_sfr_simple": ("6.2.1",), + "test034_nwtp2": ("6.2.1",), + "test034_nwtp2_1d": ("6.2.1",), + "test045_lake1tr_nr": ("6.2.1",), + "test045_lake2tr": ("6.2.1",), + "test045_lake2tr_nr": ("6.2.1",), + "test051_uzfp2": ("6.2.1",), + "test051_uzfp3_lakmvr_v2": ("6.2.1",), + "test051_uzfp3_wellakmvr_v2": ("6.2.1",), + "test045_lake4ss": ("6.2.2",), + "test056_mt3dms_usgs_gwtex_dev": ("6.4.1",), + "test056_mt3dms_usgs_gwtex_IR_dev": ("6.4.1",), +} + + +def test_model(function_tmpdir, test_model_mf6, targets, original_regression): + exdir = test_model_mf6.parent + name = exdir.name + + if name in excluded: + pytest.skip(f"Excluding mf6 model: {name}") + + sim = Simulation( + name=name, + mf6_regression=not original_regression, + cmp_verbose=False, + make_comparison=should_compare(exdir, comparisons, targets), + simpath=str(exdir) + ) -def run_mf6(sim): - """ - Run the MODFLOW 6 simulation and compare to results generated using - 1) the current MODFLOW 6 release, 2) an existing head file, or 3) or - appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. + src = sim.simpath + dst = str(function_tmpdir) - """ - print("Current working directory: ".format(os.getcwd())) - src = os.path.join(example_basedir, sim.name) - dst = os.path.join("temp", sim.name) + # Run the MODFLOW 6 simulation and compare to results generated using + # 1) the current MODFLOW 6 release, 2) an existing head file, or 3) or + # appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. sim.setup(src, dst) sim.run() sim.compare() - sim.teardown() - - -def set_make_comparison(test): - compare_tests = { - "test001e_noUZF_3lay": ("6.2.1",), - "test005_advgw_tidal": ("6.2.1",), - "test017_Crinkle": ("6.2.1",), - "test028_sfr": ("6.2.1",), - "test028_sfr_rewet": ("6.2.1",), - "test028_sfr_rewet_nr": ("6.2.1",), - "test028_sfr_rewet_simple": ("6.2.1",), - "test028_sfr_simple": ("6.2.1",), - "test034_nwtp2": ("6.2.1",), - "test034_nwtp2_1d": ("6.2.1",), - "test045_lake1tr_nr": ("6.2.1",), - "test045_lake2tr": ("6.2.1",), - "test045_lake2tr_nr": ("6.2.1",), - "test051_uzfp2": ("6.2.1",), - "test051_uzfp3_lakmvr_v2": ("6.2.1",), - "test051_uzfp3_wellakmvr_v2": ("6.2.1",), - "test045_lake4ss": ("6.2.2",), - "test056_mt3dms_usgs_gwtex_dev": ("6.4.1",), - "test056_mt3dms_usgs_gwtex_IR_dev": ("6.4.1",), - } - make_comparison = True - if test in compare_tests.keys(): - version = get_mf6_version() - print(f"MODFLOW version='{version}'") - version = get_mf6_version(version="mf6-regression") - print(f"MODFLOW regression version='{version}'") - if version in compare_tests[test]: - make_comparison = False - print(f"Make comparison has been set to False.") - return make_comparison - - -mf6_models = get_mf6_models() - - -@pytest.mark.parametrize( - "exdir", - mf6_models, -) -def test_mf6model(exdir): - # run the test model - run_mf6( - Simulation( - exdir, - mf6_regression=set_mf6_regression(), - cmp_verbose=False, - make_comparison=set_make_comparison(exdir), - ) - ) - - -def main(): - # write message - tnam = os.path.splitext(os.path.basename(__file__))[0] - msg = f"Running {tnam} test" - print(msg) - - # determine if test directory exists - dir_available = is_directory_available(example_basedir) - if not dir_available: - return - - # get a list of test models to run - example_dirs = get_mf6_models() - - # run the test model - for on_dir in example_dirs: - sim = Simulation( - on_dir, - mf6_regression=set_mf6_regression(), - cmp_verbose=True, - make_comparison=set_make_comparison(on_dir), - ) - run_mf6(sim) - - return - - -if __name__ == "__main__": - - print(f"standalone run of {os.path.basename(__file__)}") - - delFiles = True - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--keep": - if len(sys.argv) > idx + 1: - delFiles = False - break - - # run main routine - main() diff --git a/autotest/test_z02_testmodels_mf5to6.py b/autotest/test_z02_testmodels_mf5to6.py index cd11f613889..420607d9022 100644 --- a/autotest/test_z02_testmodels_mf5to6.py +++ b/autotest/test_z02_testmodels_mf5to6.py @@ -1,133 +1,40 @@ import os -import pathlib -import shutil -import sys -import time +import flopy import pytest -try: - import pymake -except: - msg = "Error. Pymake package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install https://github.com/modflowpy/pymake/zipball/master" - raise Exception(msg) - -try: - import flopy -except: - msg = "Error. FloPy package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install flopy" - raise Exception(msg) - -from common_regression import ( - get_example_basedir, - get_example_dirs, - get_home_dir, - get_select_dirs, - get_select_packages, - is_directory_available, - set_mf6_regression, -) +from conftest import should_compare +from common_regression import model_setup, get_namefiles from simulation import Simulation -from targets import get_mf6_version -from targets import target_dict as target_dict - -# find path to examples directory -home = get_home_dir() - - -def get_mf5to6_models(): - """ - Get a list of test models - """ - - # determine if test directory exists - dir_available = is_directory_available(example_basedir) - if not dir_available: - return [] - - # list of example files to exclude - exclude = (None,) - - # write a summary of the files to exclude - print("list of tests to exclude:") - for idx, ex in enumerate(exclude): - print(f" {idx + 1}: {ex}") - - # build list of directories with valid example files - if example_basedir is not None: - example_dirs = get_example_dirs( - example_basedir, - exclude, - prefix="test", - find_sim=False, - ) - else: - example_dirs = [] - - # determine if only a selection of models should be run - select_dirs = None - select_packages = None - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--sim": - if len(sys.argv) > idx + 1: - select_dirs = sys.argv[idx + 1 :] - break - elif arg.lower() == "--pak": - if len(sys.argv) > idx + 1: - select_packages = sys.argv[idx + 1 :] - select_packages = [item.upper() for item in select_packages] - break - - # determine if the selection of model is in the test models to evaluate - if select_dirs is not None: - example_dirs = get_select_dirs(select_dirs, example_dirs) - if len(example_dirs) < 1: - msg = "Selected models not available in test" - print(msg) - - # determine if the specified package(s) is in the test models to evaluate - if select_packages is not None: - example_dirs = get_select_packages( - select_packages, example_basedir, example_dirs - ) - if len(example_dirs) < 1: - msg = "Selected packages not available [" - for idx, pak in enumerate(select_packages): - msg += f"{pak}" - if idx + 1 < len(select_packages): - msg += ", " - msg += "]" - print(msg) - - return example_dirs - - -find_dir = "modflow6-testmodels" -example_basedir = get_example_basedir(home, find_dir, subdir="mf5to6") - -if example_basedir is not None: - assert os.path.isdir(example_basedir) - -# get a list of test models to run -mf5to6_models = get_mf5to6_models() sfmt = "{:25s} - {}" +excluded = ["alt_model"] +comparisons = { + "testPr2": ("6.2.1",), + "testUzfLakSfr": ("6.2.1",), + "testUzfLakSfr_laketable": ("6.2.1",), + "testWetDry": ("6.2.1",), +} + + +def test_model(function_tmpdir, test_model_mf5to6, targets, original_regression): + exdir = test_model_mf5to6.parent + name = exdir.name + + if name in excluded: + pytest.skip(f"Excluding mf5to6 model: {name}") + + sim = Simulation( + exdir.name, + mf6_regression=not original_regression, + cmp_verbose=False, + make_comparison=should_compare(name, comparisons, targets), + simpath=str(exdir) + ) - -def run_mf5to6(sim): - """ - Run the MODFLOW 6 simulation and compare to existing head file or - appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. - - """ - src = os.path.join(example_basedir, sim.name) - dst = os.path.join("temp", f"z02_mf5to6_{sim.name}") - os.makedirs(dst, exist_ok=True) + src = sim.simpath + dst = str(function_tmpdir) # set lgrpth to None lgrpth = None @@ -146,19 +53,19 @@ def run_mf5to6(sim): # copy lgr files to working directory if lgrpth is not None: npth = lgrpth - pymake.setup(lgrpth, dst) + model_setup(lgrpth, dst) # copy MODFLOW-2005, MODFLOW-NWT, or MODFLOW-USG files to working directory else: - npths = pymake.get_namefiles(src) + npths = get_namefiles(src) if len(npths) < 1: msg = f"No name files in {src}" print(msg) assert False npth = npths[0] - pymake.setup(npth, dst) + model_setup(npth, dst) # run the mf5to6 converter - exe = os.path.abspath(target_dict["mf5to6"]) + exe = os.path.abspath(targets["mf5to6"]) print(sfmt.format("using executable", exe)) nmsg = "Program terminated normally" try: @@ -184,94 +91,13 @@ def run_mf5to6(sim): assert success, msg - # standard setup + # model setup src = dst - dst = os.path.join("temp", f"z02_mf6_{sim.name}") + dst = function_tmpdir / "models" sim.setup(src, dst) - # clean up temp/working directory (src) - if os.path.exists(src): - msg = f"Removing {src} directory" - print(msg) - shutil.rmtree(src) - time.sleep(0.5) - - # standard comparison run + # Run the MODFLOW 6 simulation and compare to existing head file or + # appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. sim.run() sim.compare() sim.teardown() - - -def set_make_comparison(test): - compare_tests = { - "testPr2": ("6.2.1",), - "testUzfLakSfr": ("6.2.1",), - "testUzfLakSfr_laketable": ("6.2.1",), - "testWetDry": ("6.2.1",), - } - make_comparison = True - if test in compare_tests.keys(): - version = get_mf6_version() - print(f"MODFLOW version='{version}'") - version = get_mf6_version(version="mf6-regression") - print(f"MODFLOW regression version='{version}'") - if version in compare_tests[test]: - make_comparison = False - return make_comparison - - -@pytest.mark.parametrize( - "exdir", - mf5to6_models, -) -def test_model(exdir): - run_mf5to6( - Simulation( - exdir, - mf6_regression=set_mf6_regression(), - cmp_verbose=False, - make_comparison=set_make_comparison(exdir), - ) - ) - - return - - -def main(): - # write message - tnam = os.path.splitext(os.path.basename(__file__))[0] - msg = f"Running {tnam} test" - print(msg) - - # get name of current file - module_name = sys.modules[__name__].__file__ - - # # get a list of test models to run - # example_dirs = get_mf5to6_models() - - # run the test model - for on_dir in mf5to6_models: - sim = Simulation( - on_dir, - mf6_regression=set_mf6_regression(), - cmp_verbose=False, - make_comparison=set_make_comparison(on_dir), - ) - run_mf5to6(sim) - - return - - -if __name__ == "__main__": - - print(f"standalone run of {os.path.basename(__file__)}") - - delFiles = True - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--keep": - if len(sys.argv) > idx + 1: - delFiles = False - break - - # run main routine - main() diff --git a/autotest/test_z03_examples.py b/autotest/test_z03_examples.py index 6983e411a9b..36eff7ecb6f 100644 --- a/autotest/test_z03_examples.py +++ b/autotest/test_z03_examples.py @@ -1,214 +1,53 @@ -import os -import sys - import pytest -try: - import pymake -except: - msg = "Error. Pymake package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install https://github.com/modflowpy/pymake/zipball/master" - raise Exception(msg) - -try: - import flopy -except: - msg = "Error. FloPy package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install flopy" - raise Exception(msg) - -from common_regression import ( - get_example_basedir, - get_example_dirs, - get_home_dir, - get_select_dirs, - get_select_packages, - is_directory_available, -) +from conftest import should_compare from simulation import Simulation -from targets import get_mf6_version - -# find path to modflow6-examples directory -home = get_home_dir() - -# get example_basedir -find_dir = "modflow6-examples" -example_basedir = get_example_basedir(home, find_dir, subdir="examples") - - -def get_mf6_models(): - """ - Get a list of test models - """ - - # determine if examples directory exists - dir_available = is_directory_available(example_basedir) - if not dir_available: - return [] - - # determine if running on travis - is_CI = "CI" in os.environ - - # tuple of example files to exclude - exclude = ("ex-gwf-csub-p02c",) - - # update exclude - if is_CI: - exclude_CI = (None,) - exclude = exclude + exclude_CI - exclude = list(exclude) - - # write a summary of the files to exclude - print("list of tests to exclude:") - for idx, ex in enumerate(exclude): - print(f" {idx + 1}: {ex}") - - # build list of directories with valid example files - if example_basedir is not None: - example_dirs = get_example_dirs(example_basedir, exclude, prefix="ex-") - else: - example_dirs = [] - - # determine if only a selection of models should be run - select_dirs = None - select_packages = None - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--sim": - if len(sys.argv) > idx + 1: - select_dirs = sys.argv[idx + 1 :] - break - elif arg.lower() == "--pak": - if len(sys.argv) > idx + 1: - select_packages = sys.argv[idx + 1 :] - select_packages = [item.upper() for item in select_packages] - break - - # determine if the selection of model is in the test models to evaluate - if select_dirs is not None: - example_dirs = get_select_dirs(select_dirs, example_dirs) - if len(example_dirs) < 1: - msg = "Selected models not available in test" - print(msg) - - # determine if the specified package(s) is in the test models to evaluate - if select_packages is not None: - example_dirs = get_select_packages( - select_packages, example_basedir, example_dirs - ) - if len(example_dirs) < 1: - msg = "Selected packages not available [" - for pak in select_packages: - msg += f" {pak}" - msg += "]" - print(msg) - - return example_dirs - -def run_mf6(sim): - """ - Run the MODFLOW 6 simulation and compare to existing head file or - appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. - """ - print(os.getcwd()) - src = os.path.join(example_basedir, sim.name) - dst = os.path.join("temp", sim.name) - sim.setup(src, dst) - sim.run() - sim.compare() - sim.teardown() - - -def set_make_comparison(test): - compare_tests = { - "ex-gwf-capture": ("6.2.1",), - "ex-gwf-sagehen": ("6.2.1",), - "ex-gwf-sfr-p01b": ("6.2.1",), - "ex-gwf-nwt-p02a": ("6.2.1",), - "ex-gwf-lak-p01": ("6.2.1",), - "ex-gwf-lak-p02": ("6.2.1",), - "ex-gwf-nwt-p02b": ("6.2.1",), - "ex-gwf-advtidal": ("6.2.1",), - "ex-gwf-sfr-p01": ("6.2.1",), - "ex-gwf-lgr": ("6.2.2",), - "ex-gwt-rotate": ("6.2.2",), - "ex-gwt-gwtgwt-mt3dms-p10": ("6.3.0",), - } - make_comparison = True - if test in compare_tests.keys(): - version = get_mf6_version() - print(f"MODFLOW version='{version}'") - version = get_mf6_version(version="mf6-regression") - print(f"MODFLOW regression version='{version}'") - if version in compare_tests[test]: - print( - f"Test {test} does not run with versions {compare_tests[test]}" - ) - print( - f"Skipping regression test of sim {test} because the version is {version}" - ) - make_comparison = False - return make_comparison - - -mf6_models = get_mf6_models() - - -@pytest.mark.parametrize( - "exdir", - mf6_models, -) -def test_mf6model(exdir): - # run the test model - run_mf6( - Simulation( - exdir, - mf6_regression=True, - cmp_verbose=False, - make_comparison=set_make_comparison(exdir), - ) - ) - - -def main(): - # write message - tnam = os.path.splitext(os.path.basename(__file__))[0] - msg = f"Running {tnam} test" - print(msg) - - # get a list of test models to run - example_dirs = get_mf6_models() - - # run the test model - for on_dir in example_dirs: - mf6_regression = True - make_comparison = set_make_comparison(on_dir) - if not make_comparison: - mf6_regression = False +excluded = ["ex-gwf-csub-p02c"] +comparisons = { + "ex-gwf-capture": ("6.2.1",), + "ex-gwf-sagehen": ("6.2.1",), + "ex-gwf-sfr-p01b": ("6.2.1",), + "ex-gwf-nwt-p02a": ("6.2.1",), + "ex-gwf-lak-p01": ("6.2.1",), + "ex-gwf-lak-p02": ("6.2.1",), + "ex-gwf-nwt-p02b": ("6.2.1",), + "ex-gwf-advtidal": ("6.2.1",), + "ex-gwf-sfr-p01": ("6.2.1",), + "ex-gwf-lgr": ("6.2.2",), + "ex-gwt-rotate": ("6.2.2",), + "ex-gwt-gwtgwt-mt3dms-p10": ("6.3.0",), +} + + +def test_scenario(function_tmpdir, example_scenario, targets): + name, namefiles = example_scenario + exdirs = [nf.parent for nf in namefiles] + + if name in excluded: + pytest.skip(f"Excluding mf6 model: {name}") + + for exdir in exdirs: + model_name = f"{name}_{exdir.name}" + if exdir.name in ["mf6gwt"]: + pytest.skip(f"Skipping coupled GWT model: {name}/{exdir.name}") + + workspace = function_tmpdir / model_name sim = Simulation( - on_dir, - mf6_regression=mf6_regression, + name=model_name, + mf6_regression=True, cmp_verbose=False, - make_comparison=make_comparison, + make_comparison=should_compare(name, comparisons, targets), + simpath=str(exdir) ) - run_mf6(sim) - - return - - -if __name__ == "__main__": - - print(f"standalone run of {os.path.basename(__file__)}") - delFiles = True - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--keep": - if len(sys.argv) > idx + 1: - delFiles = False - break + src = sim.simpath + dst = str(workspace) - # run main routine - main() + # Run the MODFLOW 6 simulation and compare to existing head file or + # appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. + sim.setup(src, dst) + sim.run() + sim.compare() + sim.teardown() diff --git a/autotest/test_z03_largetestmodels.py b/autotest/test_z03_largetestmodels.py index dc3fa8e2c8f..08196fbf66b 100644 --- a/autotest/test_z03_largetestmodels.py +++ b/autotest/test_z03_largetestmodels.py @@ -1,197 +1,38 @@ -import os -import sys - import pytest -try: - import pymake -except: - msg = "Error. Pymake package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install https://github.com/modflowpy/pymake/zipball/master" - raise Exception(msg) - -try: - import flopy -except: - msg = "Error. FloPy package is not available.\n" - msg += "Try installing using the following command:\n" - msg += " pip install flopy" - raise Exception(msg) - -from common_regression import ( - get_example_basedir, - get_example_dirs, - get_home_dir, - get_select_dirs, - get_select_packages, - is_directory_available, - set_mf6_regression, -) +from conftest import should_compare from simulation import Simulation -from targets import get_mf6_version - -home = get_home_dir() - -find_dir = "modflow6-largetestmodels" -example_basedir = get_example_basedir(home, find_dir) - - -def get_mf6_models(): - """ - Get a list of test models - """ - - # determine if largetest directory exists - dir_available = is_directory_available(example_basedir) - if not dir_available: - return [] - # determine if running on CI - is_CI = "CI" in os.environ - # tuple of example files to exclude - exclude = (None,) +excluded = [] +comparisons = { + "test1004_mvlake_laksfr_tr": ("6.2.2",), + "test1004_mvlake_lak_tr": ("6.2.1",), + "test1003_MNW2_Fig28": ("6.2.1",), + "test1001_Peterson": ("6.2.1",), +} - # update exclude - if is_CI: - exclude_CI = (None,) - exclude = exclude + exclude_CI - exclude = list(exclude) - # write a summary of the files to exclude - print("list of tests to exclude:") - for idx, ex in enumerate(exclude): - print(f" {idx + 1}: {ex}") +def test_model(function_tmpdir, large_test_model, targets, original_regression): + exdir = large_test_model.parent + name = exdir.name - # build list of directories with valid example files - if example_basedir is not None: - example_dirs = get_example_dirs( - example_basedir, exclude, prefix="test" - ) - else: - example_dirs = [] - - # determine if only a selection of models should be run - select_dirs = None - select_packages = None - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--sim": - if len(sys.argv) > idx + 1: - select_dirs = sys.argv[idx + 1 :] - break - elif arg.lower() == "--pak": - if len(sys.argv) > idx + 1: - select_packages = sys.argv[idx + 1 :] - select_packages = [item.upper() for item in select_packages] - break - - # determine if the selection of model is in the test models to evaluate - if select_dirs is not None: - example_dirs = get_select_dirs(select_dirs, example_dirs) - if len(example_dirs) < 1: - msg = "Selected models not available in test" - print(msg) - - # determine if the specified package(s) is in the test models to evaluate - if select_packages is not None: - example_dirs = get_select_packages( - select_packages, example_basedir, example_dirs - ) - if len(example_dirs) < 1: - msg = "Selected packages not available [" - for pak in select_packages: - msg += f" {pak}" - msg += "]" - print(msg) - - return example_dirs + if name in excluded: + pytest.skip(f"Excluding large mf6 model: {name}") + sim = Simulation( + name=name, + mf6_regression=not original_regression, + cmp_verbose=False, + make_comparison=should_compare(name, comparisons, targets), + simpath=str(exdir) + ) -def run_mf6(sim): - """ - Run the MODFLOW 6 simulation and compare to existing head file or - appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. + src = sim.simpath + dst = str(function_tmpdir) - """ - print(os.getcwd()) - src = os.path.join(example_basedir, sim.name) - dst = os.path.join("temp", sim.name) + # Run the MODFLOW 6 simulation and compare to existing head file or + # appropriate MODFLOW-2005, MODFLOW-NWT, MODFLOW-USG, or MODFLOW-LGR run. sim.setup(src, dst) sim.run() sim.compare() - sim.teardown() - - -def set_make_comparison(test): - compare_tests = { - "test1004_mvlake_laksfr_tr": ("6.2.2",), - "test1004_mvlake_lak_tr": ("6.2.1",), - "test1003_MNW2_Fig28": ("6.2.1",), - "test1001_Peterson": ("6.2.1",), - } - make_comparison = True - if test in compare_tests.keys(): - version = get_mf6_version() - print(f"MODFLOW version='{version}'") - version = get_mf6_version(version="mf6-regression") - print(f"MODFLOW regression version='{version}'") - if version in compare_tests[test]: - make_comparison = False - return make_comparison - - -mf6_models = get_mf6_models() - - -@pytest.mark.parametrize( - "exdir", - mf6_models, -) -def test_mf6model(exdir): - # run the test model - run_mf6( - Simulation( - exdir, - mf6_regression=set_mf6_regression(), - cmp_verbose=False, - make_comparison=set_make_comparison(exdir), - ) - ) - - -def main(): - # write message - tnam = os.path.splitext(os.path.basename(__file__))[0] - msg = f"Running {tnam} test" - print(msg) - - # get a list of test models to run - example_dirs = get_mf6_models() - - # run the test model - for on_dir in example_dirs: - sim = Simulation( - on_dir, - mf6_regression=set_mf6_regression(), - cmp_verbose=False, - make_comparison=set_make_comparison(on_dir), - ) - run_mf6(sim) - - return - - -if __name__ == "__main__": - - print(f"standalone run of {os.path.basename(__file__)}") - - delFiles = True - for idx, arg in enumerate(sys.argv): - if arg.lower() == "--keep": - if len(sys.argv) > idx + 1: - delFiles = False - break - - # run main routine - main()