Skip to content

Commit

Permalink
feat(#693) and fix(#691) (#696)
Browse files Browse the repository at this point in the history
* feat(#693): support for load_only option in modflow 6
fix(#691): forgive flag now defaults to false when loading models. bugs in t028_test and t012_test fixed

* fix(code style fixes): code clean up

* fix(load_only): Added support for package names in load_only lists and cleaned up documentation
  • Loading branch information
spaulins-usgs authored and langevin-usgs committed Oct 28, 2019
1 parent a3742dd commit 134557a
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 17 deletions.
65 changes: 62 additions & 3 deletions autotest/t504_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,43 @@ def test006_2models_mvr():
# clean up
sim.delete_output_files()

# test load_only
model_package_check = ['ic', 'maw', 'npf', 'oc']
load_only_lists = [['ic6', 'npf6', 'oc', 'gwf6-gwf6', 'ims'],
['ic', 'maw', 'npf', 'gwf-gwf', 'ims'],
['ic', 'maw6', 'npf']]
for load_only in load_only_lists:
sim = MFSimulation.load(sim_name, 'mf6', exe_name, pth,
load_only=load_only)
for model_name in model_names:
model = sim.get_model(model_name)
for package in model_package_check:
assert (package in model.package_type_dict or
package in sim.package_type_dict) == \
(package in load_only or '{}6'.format(package) in
load_only)
assert (len(sim._exchange_files) > 0) == ('gwf6-gwf6' in load_only or
'gwf-gwf' in load_only)
assert (len(sim._ims_files) > 0) == ('ims6' in load_only or
'ims' in load_only)

# load package by name
load_only_list = ['ic6', 'maw', 'npf_p1', 'oc_p2', 'ims']
sim = MFSimulation.load(sim_name, 'mf6', exe_name, pth,
load_only=load_only_list)
model_parent = sim.get_model('parent')
model_child = sim.get_model('child')
assert 'oc' not in model_parent.package_type_dict
assert 'oc' in model_child.package_type_dict
assert 'npf' in model_parent.package_type_dict
assert 'npf' not in model_child.package_type_dict

if run:
# test running a runnable load_only case
sim = MFSimulation.load(sim_name, 'mf6', exe_name, pth,
load_only=load_only_lists[0])
assert sim.run_simulation()[0]

return


Expand Down Expand Up @@ -684,6 +721,26 @@ def test001e_uzf_3lay():
outfile = os.path.join(save_folder, 'head_compare.dat')
assert pymake.compare_heads(None, None, files1=head_file, files2=head_new, outfile=outfile)

# test load_only
model_package_check = ['chd', 'ic', 'npf', 'oc', 'sto', 'uzf']
load_only_lists = [['chd6', 'ic6', 'ims', 'npf6', 'obs', 'oc', 'sto'],
['chd6', 'ims', 'npf6', 'obs', 'oc', 'sto', 'uzf6'],
['chd', 'ic', 'npf', 'obs', 'sto'],
['ic6', 'ims', 'obs6', 'oc6']]
for load_only in load_only_lists:
sim = MFSimulation.load(model_name, 'mf6', exe_name, pth,
load_only=load_only)
model = sim.get_model()
for package in model_package_check:
print(package)
assert (package in model.package_type_dict) == \
(package in load_only or '{}6'.format(package) in load_only)
if run:
# test running a runnable load_only case
sim = MFSimulation.load(model_name, 'mf6', exe_name, pth,
load_only=load_only_lists[0])
assert sim.run_simulation()[0]


def test045_lake2tr():
# init paths
Expand All @@ -699,8 +756,10 @@ def test045_lake2tr():
os.makedirs(save_folder)

expected_output_folder = os.path.join(pth, 'expected_output')
expected_head_file_a = os.path.join(expected_output_folder, 'lakeex2a_unch.hds')
expected_head_file_b = os.path.join(expected_output_folder, 'lakeex2a_adj.hds')
expected_head_file_a = os.path.join(expected_output_folder,
'lakeex2a_unch.hds')
expected_head_file_b = os.path.join(expected_output_folder,
'lakeex2a_adj.hds')

# load simulation
sim = MFSimulation.load(model_name, 'mf6', exe_name, pth)
Expand Down Expand Up @@ -868,12 +927,12 @@ def test027_timeseriestest():


if __name__ == '__main__':
test006_gwf3()
test001a_tharmonic()
test001e_uzf_3lay()
test003_gwfs_disv()
test005_advgw_tidal()
test006_2models_mvr()
test006_gwf3()
test027_timeseriestest()
test036_twrihfb()
test045_lake1ss_table()
Expand Down
4 changes: 2 additions & 2 deletions examples/data/mf6/test006_2models_mvr/model1.nam
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ END OPTIONS
BEGIN PACKAGES
DIS6 model1.dis
IC6 model1.ic
NPF6 model1.npf
NPF6 model1.npf npf_p1
CHD6 model1.chd
MAW6 model1.mawq
OC6 model1.oc
OC6 model1.oc oc_p1
END PACKAGES

2 changes: 1 addition & 1 deletion examples/data/mf6/test006_2models_mvr/model2.nam
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ BEGIN PACKAGES
IC6 model2.ic
NPF6 model2.npf
MAW6 model2.mawq
OC6 model2.oc
OC6 model2.oc oc_p2
END PACKAGES

1 change: 1 addition & 0 deletions examples/data/mt3d_test/mf2kmt3d/HSSTest/hsstest.WEL
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
2 0
2
1 16 16 1.
1 16 31 -1.
43 changes: 43 additions & 0 deletions flopy/mf6/mfbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,3 +625,46 @@ def get_package(self, name=None):
def register_package(self, package):
path = (package.package_name,)
return (path, None)

@staticmethod
def _load_only_dict(load_only):
if load_only is None:
return None
if isinstance(load_only, dict):
return load_only
if not isinstance(load_only, collections.Iterable):
raise FlopyException('load_only must be iterable or None. '
'load_only value of "{}" is '
'invalid'.format(load_only))
load_only_dict = {}
for item in load_only:
load_only_dict[item.lower()] = True
return load_only_dict

@staticmethod
def _in_pkg_list(pkg_list, pkg_type, pkg_name):
if pkg_type is not None:
pkg_type = pkg_type.lower()
if pkg_name is not None:
pkg_name = pkg_name.lower()
if pkg_type in pkg_list or pkg_name in pkg_list:
return True

# split to make cases like "gwf6-gwf6" easier to process
pkg_type = pkg_type.split('-')
try:
# if there is a number on the end of the package try
# excluding it
int(pkg_type[0][-1])
for key in pkg_list.keys():
key = key.split('-')
if len(key) == len(pkg_type):
matches = True
for key_item, pkg_item in zip(key, pkg_type):
if pkg_item[0:-1] != key_item and pkg_item != key_item:
matches = False
if matches:
return True
except ValueError:
return False
return False
27 changes: 25 additions & 2 deletions flopy/mf6/mfmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,8 @@ def verbose(self, verbose):
@classmethod
def load_base(cls, simulation, structure, modelname='NewModel',
model_nam_file='modflowtest.nam', mtype='gwf', version='mf6',
exe_name='mf6.exe', strict=True, model_rel_path='.'):
exe_name='mf6.exe', strict=True, model_rel_path='.',
load_only=None):
"""
Load an existing model.
Expand All @@ -493,6 +494,14 @@ def load_base(cls, simulation, structure, modelname='NewModel',
strict mode when loading files
model_rel_path : string
relative path of model folder to simulation folder
load_only : list
list of package abbreviations or package names corresponding to
packages that flopy will load. default is None, which loads all
packages. the discretization packages will load regardless of this
setting. subpackages, like time series and observations, will also
load regardless of this setting.
example list: ['ic', 'maw', 'npf', 'oc', 'my_well_package_1']
Returns
-------
model : MFModel
Expand All @@ -505,6 +514,10 @@ def load_base(cls, simulation, structure, modelname='NewModel',
version=version, exe_name=exe_name,
add_to_simulation=False, structure=structure,
model_rel_path=model_rel_path)

# build case consistent load_only dictionary for quick lookups
load_only = instance._load_only_dict(load_only)

# load name file
instance.name_file.load(strict)

Expand All @@ -527,9 +540,19 @@ def load_base(cls, simulation, structure, modelname='NewModel',
sim_struct = mfstructure.MFStructure().sim_struct
instance._ftype_num_dict = {}
for ftype, fname, pname in packages_ordered:
ftype_orig = ftype
ftype = ftype[0:-1].lower()
if ftype in structure.package_struct_objs or ftype in \
sim_struct.utl_struct_objs:
if load_only is not None and not \
instance._in_pkg_list(priority_packages, ftype_orig,
pname) \
and not instance._in_pkg_list(load_only, ftype_orig,
pname):
if simulation.simulation_data.verbosity_level.value >= \
VerbosityLevel.normal.value:
print(' skipping package {}...'.format(ftype))
continue
if model_rel_path and model_rel_path != '.':
# strip off model relative path from the file path
filemgr = simulation.simulation_data.mfpath
Expand Down Expand Up @@ -596,7 +619,7 @@ def get_grid_type(self):
Returns
-------
grid type : DiscritizationType
grid type : DiscretizationType
"""
package_recarray = self.name_file.packages
structure = mfstructure.MFStructure()
Expand Down
6 changes: 4 additions & 2 deletions flopy/mf6/modflow/mfgwf.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ def __init__(self, simulation, modelname='model', model_nam_file=None,
@classmethod
def load(cls, simulation, structure, modelname='NewModel',
model_nam_file='modflowtest.nam', version='mf6',
exe_name='mf6.exe', strict=True, model_rel_path='.'):
exe_name='mf6.exe', strict=True, model_rel_path='.',
load_only=None):
return mfmodel.MFModel.load_base(simulation, structure, modelname,
model_nam_file, 'gwf', version,
exe_name, strict, model_rel_path)
exe_name, strict, model_rel_path,
load_only)
32 changes: 30 additions & 2 deletions flopy/mf6/modflow/mfsimulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ def model_names(self):

@classmethod
def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe',
sim_ws='.', strict=True, verbosity_level=1):
sim_ws='.', strict=True, verbosity_level=1, load_only=None):
"""
Load an existing model.
Expand All @@ -465,6 +465,14 @@ def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe',
messages
2 : verbose mode with full error/warning/informational
messages. this is ideal for debugging
load_only : list
list of package abbreviations or package names corresponding to
packages that flopy will load. default is None, which loads all
packages. the discretization packages will load regardless of this
setting. subpackages, like time series and observations, will also
load regardless of this setting.
example list: ['ic', 'maw', 'npf', 'oc', 'ims', 'gwf6-gwf6']
Returns
-------
sim : MFSimulation object
Expand All @@ -480,6 +488,9 @@ def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe',
if verbosity_level.value >= VerbosityLevel.normal.value:
print('loading simulation...')

# build case consistent load_only dictionary for quick lookups
load_only = instance._load_only_dict(load_only)

# load simulation name file
if verbosity_level.value >= VerbosityLevel.normal.value:
print(' loading simulation name file...')
Expand Down Expand Up @@ -520,7 +531,7 @@ def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe',
instance._models[item[2]] = model_obj.load(
instance,
instance.structure.model_struct_objs[item[0].lower()], item[2],
name_file, version, exe_name, strict, path)
name_file, version, exe_name, strict, path, load_only)

# load exchange packages and dependent packages
try:
Expand All @@ -544,6 +555,14 @@ def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe',
package='nam',
message=message)
for exgfile in exch_data:
if load_only is not None and not \
instance._in_pkg_list(load_only, exgfile[0],
exgfile[2]):
if instance.simulation_data.verbosity_level.value >= \
VerbosityLevel.normal.value:
print(' skipping package {}..'
'.'.format(exgfile[0].lower()))
continue
# get exchange type by removing numbers from exgtype
exchange_type = ''.join([char for char in exgfile[0] if
not char.isdigit()]).upper()
Expand Down Expand Up @@ -604,6 +623,15 @@ def load(cls, sim_name='modflowsim', version='mf6', exe_name='mf6.exe',
message=message)
for solution_group in solution_group_dict.values():
for solution_info in solution_group:
if load_only is not None and \
not instance._in_pkg_list(load_only,
solution_info[0],
solution_info[2]):
if instance.simulation_data.verbosity_level.value >= \
VerbosityLevel.normal.value:
print(' skipping package {}..'
'.'.format(solution_info[0].lower()))
continue
ims_file = mfims.ModflowIms(instance, filename=solution_info[1],
pname=solution_info[2])
if verbosity_level.value >= VerbosityLevel.normal.value:
Expand Down
2 changes: 1 addition & 1 deletion flopy/modflow/mf.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ def load_results(self, **kwargs):

@staticmethod
def load(f, version='mf2005', exe_name='mf2005.exe', verbose=False,
model_ws='.', load_only=None, forgive=True, check=True):
model_ws='.', load_only=None, forgive=False, check=True):
"""
Load an existing MODFLOW model.
Expand Down
6 changes: 6 additions & 0 deletions flopy/modflow/mfbcf.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,12 @@ def load(f, model, ext_unit_dict=None):
istart = 0
for k in range(nlay):
lcode = line[istart:istart + 2]
if lcode.strip() == '':
# hit end of line before expected end of data
# read next line
line = f.readline()
istart = 0
lcode = line[istart:istart + 2]
lcode = lcode.replace(' ', '0')
t.append(lcode)
istart += 2
Expand Down
2 changes: 1 addition & 1 deletion flopy/modflowlgr/mflgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ def change_model_ws(self, new_pth=None, reset_external=False):

@staticmethod
def load(f, version='mflgr', exe_name='mflgr.exe', verbose=False,
model_ws='.', load_only=None, forgive=True, check=True):
model_ws='.', load_only=None, forgive=False, check=True):
"""
Load an existing model.
Expand Down
4 changes: 4 additions & 0 deletions flopy/mt3d/mt.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,10 @@ def load(f, version='mt3dms', exe_name='mt3dms.exe', verbose=False,
Filetype(s) to load (e.g. ['btn', 'adv'])
(default is None, which means that all will be loaded)
forgive : bool, optional
Option to raise exceptions on package load failure, which can be
useful for debugging. Default False.
modflowmodel : flopy.modflow.mf.Modflow
This is a flopy Modflow model object upon which this Mt3dms
model is based. (the default is None)
Expand Down
6 changes: 3 additions & 3 deletions flopy/seawat/swt.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,11 +428,11 @@ def load(f, version='seawat', exe_name='swtv4', verbose=False,
verbose=verbose)

mf = Modflow.load(f, version='mf2k', exe_name=None, verbose=verbose,
model_ws=model_ws, load_only=load_only, forgive=True,
check=False)
model_ws=model_ws, load_only=load_only,
forgive=False, check=False)

mt = Mt3dms.load(f, version='mt3dms', exe_name=None, verbose=verbose,
model_ws=model_ws, forgive=True)
model_ws=model_ws, forgive=False)

# set listing and global files using mf objects
ms.lst = mf.lst
Expand Down

0 comments on commit 134557a

Please sign in to comment.