Skip to content

Commit

Permalink
feat(simulation+model options): Dynamically generate simulation optio…
Browse files Browse the repository at this point in the history
…ns from simulation namefile dfn (#1842)

* feat(simulation+model options):

Simulation and model file initialization parameters and attributes are now dynamically generated to include the namefile options found in the dfn file.

* lint + imports

---------

Co-authored-by: scottrp <[email protected]>
  • Loading branch information
spaulins-usgs and scottrp authored Jul 10, 2023
1 parent 0e13703 commit a16a379
Show file tree
Hide file tree
Showing 13 changed files with 2,860 additions and 2,526 deletions.
8 changes: 8 additions & 0 deletions autotest/regression/test_mf6.py
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,10 @@ def test021_twri(function_tmpdir, example_data_path):
version="mf6",
exe_name="mf6",
sim_ws=data_folder,
memory_print_option="SUMMARY",
)
sim.nocheck = True

sim.set_sim_path(function_tmpdir)
tdis_rc = [(86400.0, 1, 1.0)]
tdis_package = ModflowTdis(
Expand All @@ -927,6 +930,8 @@ def test021_twri(function_tmpdir, example_data_path):
model = ModflowGwf(
sim, modelname=model_name, model_nam_file=f"{model_name}.nam"
)
model.print_input = True

ims_package = ModflowIms(
sim,
print_option="SUMMARY",
Expand Down Expand Up @@ -1098,6 +1103,9 @@ def test021_twri(function_tmpdir, example_data_path):
strt2 = ic2.strt.get_data()
drn2 = model2.get_package("drn")
drn_spd = drn2.stress_period_data.get_data()
assert sim2.memory_print_option.get_data().lower() == "summary"
assert sim2.nocheck.get_data() is True
assert model2.print_input.get_data() is True
assert strt2[0, 0, 0] == 0.0
assert strt2[1, 0, 0] == 1.0
assert strt2[2, 0, 0] == 2.0
Expand Down
3 changes: 2 additions & 1 deletion autotest/test_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from flopy.mbase import ModelInterface
from flopy.mf6.data.mfdatalist import MFList, MFTransientList
from flopy.mf6.mfpackage import MFChildPackages, MFPackage
from flopy.mf6.modflow.mfsimulation import MFSimulation, MFSimulationData
from flopy.mf6.mfsimbase import MFSimulationData
from flopy.mf6.modflow.mfsimulation import MFSimulation
from flopy.modflow import Modflow
from flopy.utils import TemporalReference

Expand Down
4 changes: 1 addition & 3 deletions autotest/test_mf6.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os
import platform
from os.path import join
from pathlib import Path
from shutil import copytree, which

Expand Down Expand Up @@ -58,7 +57,7 @@
)
from flopy.mf6.data.mffileaccess import MFFileAccessArray
from flopy.mf6.data.mfstructure import MFDataItemStructure, MFDataStructure
from flopy.mf6.mfbase import MFFileMgmt
from flopy.mf6.mfsimbase import MFSimulationData
from flopy.mf6.modflow import (
mfgwf,
mfgwfdis,
Expand All @@ -72,7 +71,6 @@
mfims,
mftdis,
)
from flopy.mf6.modflow.mfsimulation import MFSimulationData
from flopy.utils import (
CellBudgetFile,
HeadFile,
Expand Down
2 changes: 2 additions & 0 deletions flopy/mf6/data/mfdatalist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1944,6 +1944,8 @@ def _set_data_record(
self._simulation_data.debug,
)
if key is None:
if data is None:
return
# search for a key
new_key_index = self.structure.first_non_keyword_index()
if new_key_index is not None and len(data) > new_key_index:
Expand Down
2 changes: 2 additions & 0 deletions flopy/mf6/data/mfdatascalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,8 @@ def set_data(self, data, key=None):
if `data` is a dictionary.
"""
if data is None and key is None:
return
if isinstance(data, dict):
# each item in the dictionary is a list for one stress period
# the dictionary key is the stress period the list is for
Expand Down
65 changes: 38 additions & 27 deletions flopy/mf6/data/mfdatautil.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,33 +103,34 @@ def convert_data(data, data_dimensions, data_type, data_item=None, sub_amt=1):
False,
)
elif data_type == DatumType.integer:
if data_item is not None and data_item.numeric_index:
return int(PyListUtil.clean_numeric(data)) - sub_amt
try:
return int(data)
except (ValueError, TypeError):
if data is not None:
if data_item is not None and data_item.numeric_index:
return int(PyListUtil.clean_numeric(data)) - sub_amt
try:
return int(PyListUtil.clean_numeric(data))
return int(data)
except (ValueError, TypeError):
message = (
'Data "{}" with value "{}" can not be '
"converted to int"
".".format(data_dimensions.structure.name, data)
)
type_, value_, traceback_ = sys.exc_info()
raise MFDataException(
data_dimensions.structure.get_model(),
data_dimensions.structure.get_package(),
data_dimensions.structure.path,
"converting data",
data_dimensions.structure.name,
inspect.stack()[0][3],
type_,
value_,
traceback_,
message,
False,
)
try:
return int(PyListUtil.clean_numeric(data))
except (ValueError, TypeError):
message = (
'Data "{}" with value "{}" can not be '
"converted to int"
".".format(data_dimensions.structure.name, data)
)
type_, value_, traceback_ = sys.exc_info()
raise MFDataException(
data_dimensions.structure.get_model(),
data_dimensions.structure.get_package(),
data_dimensions.structure.path,
"converting data",
data_dimensions.structure.name,
inspect.stack()[0][3],
type_,
value_,
traceback_,
message,
False,
)
elif data_type == DatumType.string and data is not None:
if data_item is None or not data_item.preserve_case:
# keep strings lower case
Expand Down Expand Up @@ -892,7 +893,7 @@ def add_parameter(
if model_parameter:
self.model_parameters.append(param_descr)

def get_doc_string(self, model_doc_string=False):
def get_doc_string(self, model_doc_string=False, sim_doc_string=False):
doc_string = '{}"""\n{}{}\n\n{}\n'.format(
self.indent, self.indent, self.description, self.parameter_header
)
Expand All @@ -912,7 +913,17 @@ def get_doc_string(self, model_doc_string=False):
else:
param_list = self.parameters
for parameter in param_list:
if sim_doc_string:
pclean = parameter.strip()
if (
pclean.startswith("simulation")
or pclean.startswith("loading_package")
or pclean.startswith("filename")
or pclean.startswith("pname")
or pclean.startswith("parent_file")
):
continue
doc_string += f"{parameter}\n"
if not model_doc_string:
if not (model_doc_string or sim_doc_string):
doc_string += f'\n{self.indent}"""'
return doc_string
29 changes: 23 additions & 6 deletions flopy/mf6/mfmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from ..utils import datautil
from ..utils.check import mf6check
from .coordinates import modeldimensions
from .data import mfstructure
from .data import mfdata, mfdatalist, mfstructure
from .data.mfdatautil import DataSearchOutput, iterable
from .mfbase import (
ExtFileAction,
Expand Down Expand Up @@ -182,6 +182,24 @@ def __getattr__(self, item):
return package
raise AttributeError(item)

def __setattr__(self, name, value):
if hasattr(self, name) and getattr(self, name) is not None:
attribute = object.__getattribute__(self, name)
if attribute is not None and isinstance(attribute, mfdata.MFData):
try:
if isinstance(attribute, mfdatalist.MFList):
attribute.set_data(value, autofill=True)
else:
attribute.set_data(value)
except MFDataException as mfde:
raise MFDataException(
mfdata_except=mfde,
model=self.name,
package="",
)
return
super().__setattr__(name, value)

def __repr__(self):
return self._get_data_str(True)

Expand Down Expand Up @@ -677,9 +695,9 @@ def check(self, f=None, verbose=True, level=1):

return self._check(chk, level)

@classmethod
@staticmethod
def load_base(
cls,
cls_child,
simulation,
structure,
modelname="NewModel",
Expand Down Expand Up @@ -729,9 +747,8 @@ def load_base(
Examples
--------
"""
instance = cls(
instance = cls_child(
simulation,
mtype,
modelname,
model_nam_file=model_nam_file,
version=version,
Expand Down Expand Up @@ -863,7 +880,7 @@ def inspect_cells(
--------
>>> import flopy
>>> sim = flopy.mf6.MFSimulation.load("name", "mf6", "mf6", ".")
>>> sim = flopy.mf6.MFSimulationBase.load("name", "mf6", "mf6", ".")
>>> model = sim.get_model()
>>> inspect_list = [(2, 3, 2), (0, 4, 2), (0, 2, 4)]
>>> out_file = os.path.join("temp", "inspect_AdvGW_tidal.csv")
Expand Down
Loading

0 comments on commit a16a379

Please sign in to comment.