Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions autotest/test_mf6_optional_default_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""
Test that optional package variables with default values aren't written to
input files when write_defaults=False.

Reproduces https://github.com/modflowpy/flopy/issues/2710.
"""

from pathlib import Path

import pytest

import flopy

pytestmark = pytest.mark.mf6


def _build_prt_sim(ws, coordinate_check_method="eager"):
sim = flopy.mf6.MFSimulation(sim_name="prt", sim_ws=str(ws))
flopy.mf6.ModflowTdis(sim, nper=1, perioddata=[(1.0, 1, 1.0)])
ems = flopy.mf6.ModflowEms(sim)
prt = flopy.mf6.ModflowPrt(sim, modelname="prt")
flopy.mf6.ModflowPrtdis(
prt,
nlay=1,
nrow=1,
ncol=3,
delr=1.0,
delc=1.0,
top=1.0,
botm=0.0,
)
flopy.mf6.ModflowPrtmip(prt, porosity=0.1)
flopy.mf6.ModflowPrtprp(
prt,
nreleasepts=1,
packagedata=[(0, (0, 0, 0), 0.5, 0.5, 0.5)],
perioddata={0: ["FIRST"]},
coordinate_check_method=coordinate_check_method,
)
flopy.mf6.ModflowPrtoc(prt, track_filerecord=[("prt.trk",)])
sim.register_solution_package(ems, [prt.name])
return sim


def _prp_text(ws):
prp_files = list(Path(ws).glob("*.prp"))
assert len(prp_files) == 1, f"expected one .prp file, found: {prp_files}"
return prp_files[0].read_text().upper()


def test_coordinate_check_method(function_tmpdir):
# write_defaults=True, default value
ws = Path(function_tmpdir) / "write_defaults"
ws.mkdir()
sim = _build_prt_sim(ws, coordinate_check_method="eager")
sim.write_simulation()
text = _prp_text(ws)
assert "COORDINATE_CHECK_METHOD" in text
assert "EAGER" in text

# write_defaults=False, default value
ws = Path(function_tmpdir) / "eager"
ws.mkdir()
sim = _build_prt_sim(ws, coordinate_check_method="eager")
sim.simulation_data.write_defaults = False
sim.write_simulation()
assert "COORDINATE_CHECK_METHOD" not in _prp_text(ws)

# write_defaults=False, non-default value
ws = Path(function_tmpdir) / "none"
ws.mkdir()
sim = _build_prt_sim(ws, coordinate_check_method="none")
sim.simulation_data.write_defaults = False
sim.write_simulation()
text = _prp_text(ws)
assert "COORDINATE_CHECK_METHOD" in text
assert "NONE" in text

# write_defaults=False passed directly to write_simulation(), default
# value, overriding simulation_data.write_defaults for this call only
ws = Path(function_tmpdir) / "write_simulation_kwarg"
ws.mkdir()
sim = _build_prt_sim(ws, coordinate_check_method="eager")
sim.write_simulation(write_defaults=False)
assert "COORDINATE_CHECK_METHOD" not in _prp_text(ws)
assert sim.simulation_data.write_defaults is True
28 changes: 28 additions & 0 deletions flopy/mf6/data/mfdatascalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,28 @@ def add_one(self):
self._simulation_data.debug,
)

@staticmethod
def _matches_default(data_item, current):
# only compare scalar types. composite types are always written
if data_item.type == DatumType.integer:
try:
return int(float(current)) == int(float(data_item.default_value))
except (ValueError, TypeError):
return False
elif data_item.type == DatumType.double_precision:
try:
return float(current) == float(data_item.default_value)
except (ValueError, TypeError):
return False
elif data_item.type == DatumType.string:
return (
str(current).lower().strip()
== data_item.default_value.lower().strip()
)
elif data_item.type == DatumType.keyword:
return data_item.default_value.lower().strip() == "true"
return False

def get_file_entry(
self,
values_only=False,
Expand Down Expand Up @@ -361,6 +383,12 @@ def get_file_entry(
self._simulation_data.debug,
ex,
)
if self.structure.optional and not self._simulation_data.write_defaults:
data_item = self.structure.data_item_structures[0]
if data_item.default_value is not None and self._matches_default(
data_item, storage.get_data()
):
return ""
if (
self.structure.type == DatumType.keyword
or self.structure.type == DatumType.record
Expand Down
2 changes: 1 addition & 1 deletion flopy/mf6/data/mfstructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ def set_value(self, line, common):
self.ucase = bool(arr_line[1])
elif arr_line[0] == "preserve_case":
self.preserve_case = self._get_boolean_val(arr_line)
elif arr_line[0] == "default_value":
elif arr_line[0] in ("default_value", "default"):
self.default_value = " ".join(arr_line[1:])
elif arr_line[0] == "numeric_index":
self.numeric_index = self._get_boolean_val(arr_line)
Expand Down
21 changes: 20 additions & 1 deletion flopy/mf6/mfsimbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ def __init__(self, path: Union[str, PathLike], mfsim):
self._verbosity_level = VerbosityLevel.normal
self._max_columns_set_by = None # Can be None, 'user', or 'auto'
self.use_pandas = True
self.write_defaults = True

self._update_str_format()

Expand Down Expand Up @@ -1717,7 +1718,10 @@ def set_all_data_internal(self, check_data=True):
package.set_all_data_internal(check_data)

def write_simulation(
self, ext_file_action=ExtFileAction.copy_relative_paths, silent=False
self,
ext_file_action=ExtFileAction.copy_relative_paths,
silent=False,
write_defaults=None,
):
"""
Write the simulation to files.
Expand All @@ -1730,10 +1734,23 @@ def write_simulation(
by absolute paths fixed.
silent : bool
Writes out the simulation in silent mode (verbosity_level = 0)
write_defaults : bool
Whether to write optional variables whose value equals the
MODFLOW 6 default for that variable. Defaults to None, which
defers to simulation_data.write_defaults (True unless changed
by the user). Set to False to omit such variables from the
input files, which can be useful when a MODFLOW 6 version
does not yet support an option flopy otherwise writes by
default.

"""
self._auto_set_max_columns()

sim_data = self.simulation_data
if write_defaults is not None:
saved_write_defaults = sim_data.write_defaults
sim_data.write_defaults = write_defaults

saved_verb_lvl = self.simulation_data.verbosity_level
if silent:
self.simulation_data.verbosity_level = VerbosityLevel.quiet
Expand Down Expand Up @@ -1795,6 +1812,8 @@ def write_simulation(

if silent:
self.simulation_data.verbosity_level = saved_verb_lvl
if write_defaults is not None:
sim_data.write_defaults = saved_write_defaults

def set_sim_path(self, path: Union[str, PathLike]):
"""Return a list of output data keys.
Expand Down