From f6538664f36d04e45d0c5e71eb1dabb2534db195 Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Tue, 17 Jan 2023 17:32:30 -0500 Subject: [PATCH] fix(fixtures): fix package detection/selection --- modflow_devtools/misc.py | 45 +++++++++++++++---- modflow_devtools/test/test_misc.py | 72 ++++++++++++++++++++++++++---- 2 files changed, 99 insertions(+), 18 deletions(-) diff --git a/modflow_devtools/misc.py b/modflow_devtools/misc.py index c07bf251..f42468d2 100644 --- a/modflow_devtools/misc.py +++ b/modflow_devtools/misc.py @@ -1,6 +1,7 @@ import importlib import socket import sys +import traceback from contextlib import contextmanager from importlib import metadata from os import PathLike, chdir, environ, getcwd @@ -118,20 +119,46 @@ def get_current_branch() -> str: def get_packages(namefile_path: PathLike) -> List[str]: """ - Return a list of packages used by the model defined in the given namefile. + Return a list of packages used by the simulation or model defined in the given namefile. + The namefile may be for an entire simulation or for a GWF or GWT model. If a simulation + namefile is given, packages used in its component model namefiles will be included. Parameters ---------- namefile_path : PathLike - path to MODFLOW 6 name file + path to MODFLOW 6 simulation or model name file Returns ------- - list of package types + a list of packages used by the simulation or model """ - with open(namefile_path, "r") as f: - lines = f.readlines() - ftypes = [] + packages = [] + path = Path(namefile_path).expanduser().absolute() + lines = open(path, "r").readlines() + gwf_lines = [l for l in lines if l.strip().lower().startswith("gwf6 ")] + gwt_lines = [l for l in lines if l.strip().lower().startswith("gwt6 ")] + + def parse_model_namefile(line): + nf_path = [path.parent / s for s in line.split(" ") if s != ""][1] + if nf_path.suffix != ".nam": + raise ValueError( + f"Failed to parse GWF or GWT model namefile from simulation namefile line: {line}" + ) + return nf_path + + # load model namefiles + try: + for line in gwf_lines: + packages = ( + packages + get_packages(parse_model_namefile(line)) + ["gwf"] + ) + for line in gwt_lines: + packages = ( + packages + get_packages(parse_model_namefile(line)) + ["gwt"] + ) + except: + warn(f"Invalid namefile format: {traceback.format_exc()}") + for line in lines: # Skip over blank and commented lines ll = line.strip().split() @@ -149,9 +176,9 @@ def get_packages(namefile_path: PathLike) -> List[str]: # strip "6" from package name l = l.replace("6", "") - ftypes.append(l.lower()) + packages.append(l.lower()) - return list(set(ftypes)) + return list(set(packages)) def has_package(namefile_path: PathLike, package: str) -> bool: @@ -179,7 +206,7 @@ def get_namefile_paths( if not Path(path).is_dir(): return [] - # find namefiles + # find simulation namefiles paths = [ p for p in Path(path).rglob( diff --git a/modflow_devtools/test/test_misc.py b/modflow_devtools/test/test_misc.py index d7eaf541..70b2ac52 100644 --- a/modflow_devtools/test/test_misc.py +++ b/modflow_devtools/test/test_misc.py @@ -1,4 +1,5 @@ import os +import shutil from os import environ from pathlib import Path from typing import List @@ -24,6 +25,13 @@ def test_set_dir(tmp_path): if _repos_path is None: _repos_path = Path(__file__).parent.parent.parent.parent _repos_path = Path(_repos_path).expanduser().absolute() +_testmodels_repo_path = _repos_path / "modflow6-testmodels" +_testmodels_repo_paths_mf6 = sorted( + list((_testmodels_repo_path / "mf6").glob("test*")) +) +_testmodels_repo_paths_mf5to6 = sorted( + list((_testmodels_repo_path / "mf5to6").glob("test*")) +) _largetestmodels_repo_path = _repos_path / "modflow6-largetestmodels" _largetestmodel_paths = sorted(list(_largetestmodels_repo_path.glob("test*"))) _examples_repo_path = _repos_path / "modflow6-examples" @@ -35,20 +43,68 @@ def test_set_dir(tmp_path): ) -@pytest.mark.skipif(not any(_example_paths), reason="examples not found") +@pytest.mark.skipif( + not any(_testmodels_repo_paths_mf6), reason="mf6 test models not found" +) def test_get_packages(): - namefile_path = _example_paths[0] - packages = get_packages(namefile_path / "mfsim.nam") - assert set(packages) == {"tdis", "gwf", "ims"} + model_path = _testmodels_repo_paths_mf6[0] + + # simulation namefile + namefile_path = model_path / "mfsim.nam" + packages = get_packages(namefile_path) + assert model_path.name == "test001a_Tharmonic" + assert set(packages) >= {"tdis", "gwf", "ims", "npf", "chd", "oc", "dis"} + + # model namefile + namefile_path = model_path / "flow15.nam" + packages = get_packages(namefile_path) + assert model_path.name == "test001a_Tharmonic" + assert set(packages) >= {"npf", "chd", "oc", "dis"} + assert "tdis" not in packages + assert "gwf" not in packages + assert "ims" not in packages + + +@pytest.mark.skipif( + not any(_testmodels_repo_paths_mf6), reason="mf6 test models not found" +) +def test_get_packages_fails_on_invalid_namefile(module_tmpdir): + model_path = _testmodels_repo_paths_mf6[0] + new_model_path = module_tmpdir / model_path.name + namefile_path = new_model_path / "mfsim.nam" + shutil.copytree(model_path, new_model_path) + + # invalid gwf namefile reference - result should only contain packages from mfsim.nam + lines = open(namefile_path, "r").read().splitlines() + with open(namefile_path, "w") as f: + for line in lines: + if "GWF6" in line: + line = line.replace("GWF6", "GWF6 garbage") + f.write(line + os.linesep) + assert set(get_packages(namefile_path)) == {"gwf", "tdis", "ims"} + + # entirely unparseable namefile - result should be empty + lines = open(namefile_path, "r").read().splitlines() + with open(namefile_path, "w") as f: + for _ in lines: + f.write("garbage" + os.linesep) + assert not any(get_packages(namefile_path)) @pytest.mark.skipif(not any(_example_paths), reason="examples not found") def test_has_package(): - namefile_path = _example_paths[0] / "mfsim.nam" + model_path = _testmodels_repo_paths_mf6[0] + namefile_path = model_path / "mfsim.nam" + assert model_path.name == "test001a_Tharmonic" assert has_package(namefile_path, "tdis") assert has_package(namefile_path, "gwf") assert has_package(namefile_path, "ims") + assert has_package(namefile_path, "npf") + assert has_package(namefile_path, "chd") + assert has_package(namefile_path, "oc") + assert has_package(namefile_path, "dis") assert not has_package(namefile_path, "gwt") + assert not has_package(namefile_path, "wel") def get_expected_model_dirs(path, pattern="mfsim.nam") -> List[Path]: @@ -174,7 +230,5 @@ def test_get_namefile_paths_select_patterns(): @pytest.mark.skipif(not any(_example_paths), reason="examples not found") def test_get_namefile_paths_select_packages(): - paths = get_namefile_paths( - _examples_path, namefile="*.nam", packages=["wel"] - ) - assert len(paths) == 64 + paths = get_namefile_paths(_examples_path, packages=["wel"]) + assert len(paths) == 43