-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: harmonize OC setup between MF6 and MFNWT; add support for e…
…mpty periods to turn off output writing
- Loading branch information
Showing
16 changed files
with
302 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
=========================================================== | ||
Input instructions by package | ||
=========================================================== | ||
.. toctree:: | ||
:maxdepth: 1 | ||
|
||
Output Control <oc.rst> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
=========================================================== | ||
MODFLOW Output Control | ||
=========================================================== | ||
|
||
Stress period input format | ||
-------------------------- | ||
Regardless of the model version (MODFLOW-2005-style or MODFLOW 6), output control can be specified in a format similar to native MODFLOW 6 input: | ||
|
||
.. code-block:: yaml | ||
oc: | ||
period_options: | ||
0: ['save head last', 'save budget last'] | ||
10: [] | ||
15: ['save head last', 'save budget last'] | ||
The above ``period_options:`` block would save the head and cell budget output on the last timestep of stress periods 0 through 9, and from 15 on, but turn off output saving for stress periods 10 through 14. This behavior is consistent with MODFLOW 6 but differs from MODFLOW-2005, where each stress periods must be explicitly included for output to be written. Other options besides ``'last'`` include ``all, first, frequency <frequency>, and steps <steps(<nstp)>``; see the MODFLOW 6 input instructions for more details. | ||
|
||
Output filenames and other arguments | ||
------------------------------------ | ||
For MODFLOW 6 models, the ``head_fileout_fmt`` and ``budget_fileout_fmt`` arguments can also be supplied to tell Flopy where to save the head and cell budget files, and how to name them. Modflow-setup fills any format specifiers (``'{}'``) with the model name, and passes the resulting strings to the ``head_filerecord`` and ``budget_filerecord`` arguments to the :py:class:`flopy.mf6.ModflowGwfoc <flopy.mf6.modflow.mfgwfoc.ModflowGwfoc>` constructor. | ||
|
||
.. code-block:: yaml | ||
oc: | ||
head_fileout_fmt: '{}.hds' | ||
budget_fileout_fmt: '{}.cbc' | ||
period_options: | ||
0: ['save head last', 'save budget last'] | ||
Any other valid arguments to the :py:class:`flopy.mf6.ModflowGwfoc <flopy.mf6.modflow.mfgwfoc.ModflowGwfoc>` and :py:class:`flopy.modflow.ModflowOc <flopy.modflow.mfoc.ModflowOc>` constructors can be supplied as keys in the ``oc:`` dictionary block. For example: | ||
|
||
.. code-block:: yaml | ||
oc: | ||
unitnumber: [14, 51, 52, 53, 0] | ||
would set the unit numbers for the head, drawdown, budget, and ibound output files. See the Flopy documentation for more details. Invalid arguments are filtered out prior to calling the constructor. | ||
|
||
Alternative stress period input formats | ||
---------------------------------------- | ||
As with other arguments, stress period input can also be directly specified in the Flopy input formats. For example, ``stress_period_data`` could be supplied for a MODFLOW-2005 model as it would be supplied to the :py:class:`flopy.modflow.ModflowOc <flopy.modflow.mfoc.ModflowOc>` constructor: | ||
|
||
.. code-block:: yaml | ||
oc: | ||
stress_period_data: | ||
(0, 1): ['save head', 'save budget'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
"""Functions for handling MODFLOW output control | ||
""" | ||
from collections import defaultdict | ||
|
||
|
||
def parse_oc_period_input(period_input, nstp=None, output_fmt='mf6'): | ||
"""Parse both flopy and mf6-style stress period output control input | ||
into flopy input. | ||
Parameters | ||
---------- | ||
period_input : dict | ||
Dictionary of stress period input (see examples) | ||
nstp : list-like | ||
Number of timesteps in each stress period | ||
output_fmt : str | ||
'mf6' for MODFLOW 6 style input (to :py:func:`flopy.mf6.ModflowGwfoc`), otherwise, | ||
input for :py:func:`flopy.modflow.ModflowOc` is produced. | ||
Returns | ||
------- | ||
flopy_input : dict | ||
Input to the flopy output control package constructor. | ||
Examples | ||
-------- | ||
>>> period_input = {'saverecord': {0: {'head': 'last', 'budget': 'last'}} | ||
{0: [('head', 'last'), ('budget', 'last')]} | ||
""" | ||
if nstp is not None: | ||
nstp = list(nstp) | ||
|
||
flopy_input = {} | ||
mf6_flopy_input = {} | ||
for rec in ['printrecord', 'saverecord']: | ||
if rec in period_input: | ||
if output_fmt != 'mf6': | ||
msg = ("MODFLOW 6 Flopy-style OC input (printrecord or " | ||
"saverecord arguments) only supported for MODFLOW 6 models.") | ||
raise NotImplementedError(msg) | ||
data = period_input[rec] | ||
mf6_record_input = {} | ||
for kper, words in data.items(): | ||
mf6_record_input[kper] = [] | ||
for var, instruction in words.items(): | ||
mf6_record_input[kper].append((var, instruction)) | ||
mf6_flopy_input[rec] = mf6_record_input | ||
elif 'period_options' in period_input: | ||
mf6_record_input = defaultdict(list) | ||
mf_record_input = defaultdict(list) | ||
for kper, options in period_input['period_options'].items(): | ||
# empty period for turning off output | ||
if len(options) == 0: | ||
mf6_record_input[kper] = [] | ||
mf_record_input[(kper, 0)] = [] | ||
else: | ||
for words in options: | ||
type, var, *instruction = words.split() | ||
if type == rec.replace('record', ''): | ||
if output_fmt == 'mf6': | ||
mf6_record_input[kper].append((var, *instruction)) | ||
else: | ||
if nstp is None: | ||
raise ValueError("MODFLOW 2005-style OC input requires " | ||
"timestep information.") | ||
# parse MF6-style instructions | ||
kstp = 0 | ||
nstep_idx = kper if kper < len(nstp) else -1 | ||
instruction, *values = instruction | ||
instruction = instruction.lower() | ||
if instruction == 'all': | ||
for kstp in range(nstp[nstep_idx]): | ||
mf_record_input[(kper, kstp)].append(f"{type} {var}") | ||
elif 'frequency' in instruction: | ||
if len(values) == 0: | ||
raise ValueError("mfsetup.oc.parse_oc: " | ||
"'frequency' instruction needs a value") | ||
freq = int(values[0]) | ||
steps = list(range(nstp[nstep_idx]))[::freq] | ||
for kstp in steps: | ||
mf_record_input[(kper, kstp)].append(f"{type} {var}") | ||
elif 'steps' in instruction: | ||
if len(values) == 0: | ||
raise ValueError("mfsetup.oc.parse_oc: " | ||
"'steps' instruction needs one or more values") | ||
for kstp in values: | ||
mf_record_input[(kper, int(kstp))].append(f"{type} {var}") | ||
elif instruction == 'first': | ||
mf_record_input[(kper, 0)].append(f"{type} {var}") | ||
elif instruction == 'last': | ||
kstp = nstp[nstep_idx] - 1 | ||
mf_record_input[(kper, int(kstp))].append(f"{type} {var}") | ||
else: | ||
raise ValueError("mfsetup.oc.parse_oc: instruction " | ||
f"'{instruction}' not understood") | ||
if len(mf6_record_input) > 0: | ||
mf6_flopy_input[rec] = dict(mf6_record_input) | ||
if len(mf_record_input) > 0: | ||
mf_record_input = fill_oc_stress_period_data(mf_record_input, nper=len(nstp)) | ||
flopy_input['stress_period_data'] = dict(mf_record_input) | ||
if output_fmt == 'mf6': | ||
return mf6_flopy_input | ||
return flopy_input | ||
|
||
|
||
def fill_oc_stress_period_data(stress_period_data, nper): | ||
"""For MODFLOW 2005-style models, repeat last entry in stress_period_data | ||
for subsequent stress periods (until another entry is encountered), | ||
as is done by default in MODFLOW 6. | ||
""" | ||
filled_spd = {} | ||
last_period_data = {} | ||
for period in range(nper): | ||
for (kper, kstp), data in stress_period_data.items(): | ||
if kper == period: | ||
last_period_data[(kper, kstp)] = data | ||
last_period_data = {(period, kstp): data for (kper, kstp), data | ||
in last_period_data.items()} | ||
filled_spd.update(last_period_data) | ||
return filled_spd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.