From f2e6637f3f2e1918749fe5a50b6efc19dc73a515 Mon Sep 17 00:00:00 2001 From: spaulins-usgs Date: Thu, 8 Dec 2022 08:43:22 -0800 Subject: [PATCH] fix(quotes+exe_path+nam_file): fixes for quoted strings, exe path, and nam file (#1645) * fix(quotes+exe_path+nam_file): Quotes properly removed when loading files with quoted strings. If all else fails trying to find exe_path, abs_path is used. Proper error message given when user attempts to create namefile. * test(quotes): added test for quoting of names in spaces Co-authored-by: jdhughes-usgs --- autotest/regression/test_mf6.py | 3 +- autotest/test_mf6.py | 54 +++++++++++++++++++++++++++++++ flopy/mbase.py | 3 ++ flopy/mf6/mfmodel.py | 21 ++++++++++-- flopy/mf6/mfpackage.py | 4 +++ flopy/mf6/modflow/mfsimulation.py | 12 +++++-- flopy/utils/datautil.py | 4 +-- 7 files changed, 94 insertions(+), 7 deletions(-) diff --git a/autotest/regression/test_mf6.py b/autotest/regression/test_mf6.py index 72e54f20c3..bef201963e 100644 --- a/autotest/regression/test_mf6.py +++ b/autotest/regression/test_mf6.py @@ -1379,7 +1379,7 @@ def test005_create_tests_advgw_tidal(tmpdir, example_data_path): obs_dict = { ("ghb_obs.csv", "binary"): [ - ("ghb-2-6-10", "GHB", (1, 5, 9)), + ("ghb- 2-6-10", "GHB", (1, 5, 9)), ("ghb-3-6-10", "GHB", (2, 5, 9)), ], "ghb_flows.csv": [ @@ -1743,6 +1743,7 @@ def test005_create_tests_advgw_tidal(tmpdir, example_data_path): # there should be only one assert not found_obs found_obs = True + assert value[0][0] == "ghb- 2-6-10" assert found_flows and found_obs # clean up diff --git a/autotest/test_mf6.py b/autotest/test_mf6.py index 4655515708..8211e40cf9 100644 --- a/autotest/test_mf6.py +++ b/autotest/test_mf6.py @@ -25,6 +25,7 @@ ModflowGwflak, ModflowGwfmaw, ModflowGwfmvr, + ModflowGwfnam, ModflowGwfnpf, ModflowGwfoc, ModflowGwfrch, @@ -41,6 +42,7 @@ ModflowGwtoc, ModflowGwtssm, ModflowIms, + ModflowNam, ModflowTdis, ModflowUtllaktab, ) @@ -1570,3 +1572,55 @@ def test_multi_model(tmpdir): # save and run updated model sim.write_simulation() sim.run_simulation() + + +@requires_exe("mf6") +def test_namefile_creation(tmpdir): + test_ex_name = "test_namefile" + # build MODFLOW 6 files + sim = MFSimulation( + sim_name=test_ex_name, + version="mf6", + exe_name="mf6", + sim_ws=str(tmpdir), + ) + + tdis_rc = [(6.0, 2, 1.0), (6.0, 3, 1.0), (6.0, 3, 1.0), (6.0, 3, 1.0)] + tdis = ModflowTdis(sim, time_units="DAYS", nper=4, perioddata=tdis_rc) + ims_package = ModflowIms( + sim, + pname="my_ims_file", + filename=f"{test_ex_name}.ims", + print_option="ALL", + complexity="SIMPLE", + outer_dvclose=0.0001, + outer_maximum=50, + under_relaxation="NONE", + inner_maximum=30, + inner_dvclose=0.0001, + linear_acceleration="CG", + preconditioner_levels=7, + preconditioner_drop_tolerance=0.01, + number_orthogonalizations=2, + ) + model = ModflowGwf( + sim, + modelname=test_ex_name, + model_nam_file="{}.nam".format(test_ex_name), + ) + + # try to create simulation name file + ex_happened = False + try: + nam = ModflowNam(sim) + except flopy.mf6.mfbase.FlopyException: + ex_happened = True + assert ex_happened + + # try to create model name file + ex_happened = False + try: + nam = ModflowGwfnam(model) + except flopy.mf6.mfbase.FlopyException: + ex_happened = True + assert ex_happened diff --git a/flopy/mbase.py b/flopy/mbase.py index 655ddd4afe..baea19c05c 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -1701,6 +1701,9 @@ def run_model( if exe_name.lower().endswith(".exe"): # try removing .exe suffix exe = which(exe_name[:-4]) + if exe is None: + # try abspath + exe = which(os.path.abspath(exe_name)) if exe is None: raise Exception( f"The program {exe_name} does not exist or is not executable." diff --git a/flopy/mf6/mfmodel.py b/flopy/mf6/mfmodel.py index 991df98bae..5bdcb9c658 100644 --- a/flopy/mf6/mfmodel.py +++ b/flopy/mf6/mfmodel.py @@ -146,7 +146,10 @@ def __init__( raise FlopyException(excpt_str) self.name_file = package_obj( - self, filename=self.model_nam_file, pname=self.name + self, + filename=self.model_nam_file, + pname=self.name, + _internal_package=True, ) def __init_subclass__(cls): @@ -1644,7 +1647,10 @@ def register_package( package.package_type ) if add_to_package_list and path in self._package_paths: - if not package_struct.multi_package_support: + if ( + package_struct is not None + and not package_struct.multi_package_support + ): # package of this type already exists, replace it self.remove_package(package.package_type) if ( @@ -1685,6 +1691,15 @@ def register_package( self._package_paths[path] = 1 if package.package_type.lower() == "nam": + if not package.internal_package: + excpt_str = ( + "Unable to register nam file. Do not create your own nam " + "files. Nam files are automatically created and managed " + "for you by FloPy." + ) + print(excpt_str) + raise FlopyException(excpt_str) + return path, self.structure.name_file_struct_obj package_extension = package.package_type @@ -1853,6 +1868,7 @@ def load_package( pname=dict_package_name, loading_package=True, parent_file=parent_package, + _internal_package=True, ) try: package.load(strict) @@ -1865,6 +1881,7 @@ def load_package( pname=dict_package_name, loading_package=True, parent_file=parent_package, + _internal_package=True, ) package.load(strict) diff --git a/flopy/mf6/mfpackage.py b/flopy/mf6/mfpackage.py index 5fbce65098..f7bf5ad1f0 100644 --- a/flopy/mf6/mfpackage.py +++ b/flopy/mf6/mfpackage.py @@ -1594,6 +1594,10 @@ def __init__( else: self.model_or_sim = parent self.parent_file = None + if "_internal_package" in kwargs and kwargs["_internal_package"]: + self.internal_package = True + else: + self.internal_package = False self._data_list = [] self._package_type = package_type if self.model_or_sim.type == "Model" and package_type.lower() != "nam": diff --git a/flopy/mf6/modflow/mfsimulation.py b/flopy/mf6/modflow/mfsimulation.py index 23dd78ac69..db100693f7 100644 --- a/flopy/mf6/modflow/mfsimulation.py +++ b/flopy/mf6/modflow/mfsimulation.py @@ -336,8 +336,7 @@ class MFSimulation(PackageContainer): version : str Version of MODFLOW 6 executable exe_name : str - Relative path to MODFLOW 6 executable from the simulation - working folder. + Path to MODFLOW 6 executable sim_ws : str Path to MODFLOW 6 simulation working folder. This is the folder containing the simulation name file. @@ -432,6 +431,7 @@ def __init__( continue_=continue_, nocheck=nocheck, memory_print_option=memory_print_option, + _internal_package=True, ) # try to build directory structure @@ -1902,6 +1902,14 @@ def register_package( # added during ims package registration self._add_package(package, path) if package.package_type.lower() == "nam": + if not package.internal_package: + excpt_str = ( + "Unable to register nam file. Do not create your own nam " + "files. Nam files are automatically created and managed " + "for you by FloPy." + ) + print(excpt_str) + raise FlopyException(excpt_str) return path, self.structure.name_file_struct_obj elif package.package_type.lower() == "tdis": self._tdis_file = package diff --git a/flopy/utils/datautil.py b/flopy/utils/datautil.py index 702e2c177b..6ff7ac26ff 100644 --- a/flopy/utils/datautil.py +++ b/flopy/utils/datautil.py @@ -358,8 +358,8 @@ def split_data_line(line, external_file=False, delimiter_conf_length=15): if item and item[0] in PyListUtil.quote_list: # starts with a quote, handle quoted text if item[-1] in PyListUtil.quote_list: - # if quoted on both ends, keep quotes - arr_fixed_line.append(item) + # if quoted on both ends, remove quotes + arr_fixed_line.append(item[1:-1]) else: arr_fixed_line.append(item[1:]) # loop until trailing quote found