Skip to content
Draft
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
83 changes: 83 additions & 0 deletions autotest/test_sim_tolerate_unknown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
Test that the TOLERATE_UNKNOWN simulation option causes unrecognized
top-level input tags to produce a warning rather than an error.

Two cases:
1. Without the option: an unknown tag in a package OPTIONS block is an error.
2. With the option: the same unknown tag produces a warning and the
simulation completes normally.
"""

import subprocess

import flopy


def run_mf6(exe, ws):
proc = subprocess.Popen(
[exe], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=ws
)
result, _ = proc.communicate()
buff = result.decode("utf-8").splitlines() if result else []
return proc.returncode, buff


def build_sim(ws, exe, tolerate_unknown=False):
"""Build and write a minimal GWF simulation."""
name = "tolunknown"
simkwargs = {"tolerate_unknown": True} if tolerate_unknown else {}
sim = flopy.mf6.MFSimulation(
sim_name=name, version="mf6", exe_name=exe, sim_ws=ws, **simkwargs
)
flopy.mf6.ModflowTdis(sim, time_units="DAYS", nper=1, perioddata=[(1.0, 1, 1.0)])
gwf = flopy.mf6.ModflowGwf(sim, modelname=name)
flopy.mf6.ModflowIms(sim, print_option="SUMMARY")
flopy.mf6.ModflowGwfdis(gwf, nlay=1, nrow=3, ncol=3, top=0.0, botm=[-1.0])
flopy.mf6.ModflowGwfic(gwf, strt=0.0)
flopy.mf6.ModflowGwfnpf(gwf)
flopy.mf6.ModflowGwfchd(
gwf, stress_period_data={0: [[(0, 0, 0), 0.0], [(0, 2, 2), 1.0]]}
)
sim.write_simulation()
return name


def inject_bogus_option(ws, name):
"""Prepend an unrecognized keyword to the NPF OPTIONS block."""
npf_file = ws / f"{name}.npf"
with open(npf_file, "w") as f:
f.write("BEGIN options\n")
f.write(" BOGUS_OPTION\n")
f.write("END options\n\n")
f.write("BEGIN griddata\n")
f.write(" ICELLTYPE\n")
f.write(" CONSTANT 0\n")
f.write(" K\n")
f.write(" CONSTANT 1.0\n")
f.write("END griddata\n")


def test_tolerate_unknown_error(function_tmpdir, targets):
"""Without TOLERATE_UNKNOWN, an unrecognized option tag is a fatal error."""
mf6 = targets["mf6"]
name = build_sim(function_tmpdir, mf6, tolerate_unknown=False)
inject_bogus_option(function_tmpdir, name)

returncode, _ = run_mf6(mf6, function_tmpdir)
assert returncode != 0, "mf6 should have failed with an unknown input tag"


def test_tolerate_unknown_warning(function_tmpdir, targets):
"""With TOLERATE_UNKNOWN, an unrecognized option tag is a warning and the
simulation completes normally."""
mf6 = targets["mf6"]
name = build_sim(function_tmpdir, mf6, tolerate_unknown=True)
inject_bogus_option(function_tmpdir, name)

returncode, buff = run_mf6(mf6, function_tmpdir)
assert returncode == 0, "mf6 failed unexpectedly:\n" + "\n".join(buff)

lst = (function_tmpdir / "mfsim.lst").read_text()
assert "BOGUS_OPTION" in lst, "expected unknown tag name in listing file warning"
assert "ignored" in lst.lower(), "expected 'ignored' in listing file warning"
assert "Normal termination" in lst, "expected normal termination in listing file"
8 changes: 8 additions & 0 deletions doc/mf6io/mf6ivar/dfn/sim-nam.dfn
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ optional true
longname print input to listing file
description keyword to activate printing of simulation input summaries to the simulation list file (mfsim.lst). With this keyword, input summaries will be written for those packages that support newer input data model routines. Not all packages are supported yet by the newer input data model routines.

block options
name tolerate_unknown
type keyword
reader urword
optional true
longname tolerate unknown options
description keyword to indicate that unrecognized top-level input option tags should be treated as warnings rather than errors. When this keyword is specified, any top-level tag (not a subfield of a record) that is not recognized for a package will generate a warning and be skipped. This allows input files written for a newer version of MODFLOW 6 to be used with an older version, provided the unrecognized options are not required.

block options
name hpc_filerecord
type record hpc6 filein hpc6_filename
Expand Down
21 changes: 21 additions & 0 deletions src/Idm/sim-namidm.f90
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module SimNamInputModule
logical :: prprof = .false.
logical :: maxerrors = .false.
logical :: print_input = .false.
logical :: tolerate_unknown = .false.
logical :: hpc_filerecord = .false.
logical :: hpc6 = .false.
logical :: filein = .false.
Expand Down Expand Up @@ -158,6 +159,25 @@ module SimNamInputModule
.false. & ! timeseries
)

type(InputParamDefinitionType), parameter :: &
simnam_tolerate_unknown = InputParamDefinitionType &
( &
'SIM', & ! component
'NAM', & ! subcomponent
'OPTIONS', & ! block
'TOLERATE_UNKNOWN', & ! tag name
'TOLERATE_UNKNOWN', & ! fortran variable
'KEYWORD', & ! type
'', & ! shape
'tolerate unknown options', & ! longname
.false., & ! required
.false., & ! developmode
.false., & ! multi-record
.false., & ! preserve case
.false., & ! layered
.false. & ! timeseries
)

type(InputParamDefinitionType), parameter :: &
simnam_hpc_filerecord = InputParamDefinitionType &
( &
Expand Down Expand Up @@ -471,6 +491,7 @@ module SimNamInputModule
simnam_prprof, &
simnam_maxerrors, &
simnam_print_input, &
simnam_tolerate_unknown, &
simnam_hpc_filerecord, &
simnam_hpc6, &
simnam_filein, &
Expand Down
11 changes: 9 additions & 2 deletions src/SimulationCreate.f90
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ subroutine options_create()
use MemoryManagerModule, only: mem_setptr
use SimVariablesModule, only: idm_context
use MemoryManagerModule, only: mem_set_print_option
use SimVariablesModule, only: isimcontinue, isimcheck
use SimVariablesModule, only: isimcontinue, isimcheck, itolerate_unknown
! -- dummy
! -- locals
character(len=LENMEMPATH) :: input_mempath
integer(I4B), pointer :: simcontinue, nocheck, maxerror
integer(I4B), pointer :: simcontinue, nocheck, maxerror, tolerateunknown
character(len=:), pointer :: prmem
character(len=LINELENGTH) :: errmsg
!
Expand All @@ -112,11 +112,13 @@ subroutine options_create()
! -- set pointers to input context option params
call mem_setptr(simcontinue, 'CONTINUE', input_mempath)
call mem_setptr(nocheck, 'NOCHECK', input_mempath)
call mem_setptr(tolerateunknown, 'TOLERATE_UNKNOWN', input_mempath)
call mem_setptr(prmem, 'PRMEM', input_mempath)
call mem_setptr(maxerror, 'MAXERRORS', input_mempath)
!
! -- update sim options
isimcontinue = simcontinue
itolerate_unknown = tolerateunknown
if (nocheck == 1) then
isimcheck = 0
else
Expand Down Expand Up @@ -156,6 +158,11 @@ subroutine options_create()
'MEMORY_PRINT_OPTION SET TO "', trim(prmem), '".'
end if
!
if (itolerate_unknown == 1) then
write (iout, '(4x, a)') &
'UNRECOGNIZED TOP-LEVEL INPUT OPTIONS WILL BE WARNED AND SKIPPED.'
end if
!
write (iout, '(1x,a)') 'END OF SIMULATION OPTIONS'
end if
end subroutine options_create
Expand Down
2 changes: 2 additions & 0 deletions src/Utilities/Idm/IdmLoad.f90
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,8 @@ subroutine allocate_simnam_int(input_mempath, idt)
intvar = 1
case ('PRINT_INPUT')
intvar = 0
case ('TOLERATE_UNKNOWN')
intvar = 0
case default
write (errmsg, '(a,a)') &
'Idm SIMNAM Load default value setting '&
Expand Down
34 changes: 26 additions & 8 deletions src/Utilities/Idm/mf6blockfile/LoadMf6File.f90
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
module LoadMf6FileModule

use KindModule, only: DP, I4B, LGP
use SimVariablesModule, only: errmsg
use SimModule, only: store_error
use SimVariablesModule, only: errmsg, warnmsg, itolerate_unknown
use SimModule, only: store_error, store_warning
use ConstantsModule, only: LINELENGTH, LENVARNAME
use BlockParserModule, only: BlockParserType
use LayeredArrayReaderModule, only: read_dbl1d_layered, &
Expand Down Expand Up @@ -279,6 +279,7 @@ recursive subroutine parse_block(this, iblk, recursive_call)
logical(LGP) :: found, required
type(MemoryType), pointer :: mt
character(len=LINELENGTH) :: tag
character(len=:), allocatable :: line
type(InputParamDefinitionType), pointer :: idt

! disu vertices/cell2d blocks are contingent on NVERT dimension
Expand Down Expand Up @@ -315,12 +316,29 @@ recursive subroutine parse_block(this, iblk, recursive_call)
if (endOfBlock) exit
! process line as tag(s)
call this%parser%GetStringCaps(tag)
idt => get_param_definition_type( &
this%mf6_input%param_dfns, &
this%mf6_input%component_type, &
this%mf6_input%subcomponent_type, &
this%mf6_input%block_dfns(iblk)%blockname, &
tag, this%filename)
if (itolerate_unknown == 1) then
idt => get_param_definition_type( &
this%mf6_input%param_dfns, &
this%mf6_input%component_type, &
this%mf6_input%subcomponent_type, &
this%mf6_input%block_dfns(iblk)%blockname, &
tag, this%filename, found=found)
if (.not. found) then
write (warnmsg, '(5a)') 'Unrecognized top-level input tag "', &
trim(tag), '" in file "', trim(this%filename), &
'" ignored.'
call store_warning(warnmsg)
call this%parser%GetRemainingLine(line)
cycle
end if
else
idt => get_param_definition_type( &
this%mf6_input%param_dfns, &
this%mf6_input%component_type, &
this%mf6_input%subcomponent_type, &
this%mf6_input%block_dfns(iblk)%blockname, &
tag, this%filename)
end if
if (idt%in_record) then
call this%parse_record_tag(iblk, idt, .false.)
else
Expand Down
1 change: 1 addition & 0 deletions src/Utilities/SimVariables.f90
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module SimVariablesModule
integer(I4B) :: iout !< file unit number for simulation output
integer(I4B) :: isimcnvg !< simulation convergence flag (1) if all objects have converged, (0) otherwise
integer(I4B) :: isimcontinue = 0 !< simulation continue flag (1) to continue if isimcnvg = 0, (0) to terminate
integer(I4B) :: itolerate_unknown = 0 !< tolerate unknown input options (1) warn and skip, (0) error
integer(I4B) :: nocheck = 0 !< nocheck option (0) to check input, (1) to ignore checks
integer(I4B) :: isimcheck = 1 !< simulation input check flag (1) to check input, (0) to ignore checks
integer(I4B) :: numnoconverge = 0 !< number of times the simulation did not converge
Expand Down
10 changes: 9 additions & 1 deletion src/mf6core.f90
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,19 @@ subroutine static_input_load()
load_models, load_exchanges
use MemoryHelperModule, only: create_mem_path
use MemoryManagerModule, only: mem_setptr, mem_allocate
use SimVariablesModule, only: iparamlog
use SimVariablesModule, only: iparamlog, idm_context, itolerate_unknown
! -- locals
character(len=LENMEMPATH) :: input_mempath
integer(I4B), pointer :: tolerateunknown
!
! -- load simnam input context
call simnam_load(iparamlog)
!
! -- read TOLERATE_UNKNOWN before loading packages so parse_block can honor it
input_mempath = create_mem_path('SIM', 'NAM', idm_context)
call mem_setptr(tolerateunknown, 'TOLERATE_UNKNOWN', input_mempath)
itolerate_unknown = tolerateunknown
!
! -- load tdis to input context
call simtdis_load()
!
Expand Down
Loading