__author__ = "emorway"
import numpy as np
from ..pakbase import Package
from ..utils import Transient2d, Util2d
[docs]class Mt3dUzt(Package):
"""
MT3D-USGS Unsaturated-Zone Transport package class
Parameters
----------
model : model object
The model object (of type :class:`flopy.mt3dms.mt.Mt3dms`) to which
this package will be added.
icbcuz : int
Is the unit number to which unsaturated-zone concentration will be
written out.
iet : int
Is a flag that indicates whether or not ET is being simulated in the
UZF1 flow package (=0 indicates that ET is not being simulated).
If ET is not being simulated, IET informs FMI package not to look
for UZET and GWET arrays in the flow-transport link file.
iuzfbnd : array of ints
Specifies which row/column indices variably-saturated transport will
be simulated in.
>0 indicates variably-saturated transport will be simulated;
=0 indicates variably-saturated transport will not be simulated;
<0 Corresponds to IUZFBND < 0 in the UZF1 input package, meaning
that user-supplied values for FINF are specified recharge and
therefore transport through the unsaturated zone is not
simulated.
incuzinf : int
(This value is repeated for each stress period as explained next) A
flag indicating whether an array containing the concentration of
infiltrating water (FINF) for each simulated species (ncomp) will be
read for the current stress period. If INCUZINF >= 0, an array
containing the concentration of infiltrating flux for each species
will be read. If INCUZINF < 0, the concentration of infiltrating flux
will be reused from the previous stress period. If INCUZINF < 0 is
specified for the first stress period, then by default the
concentration of positive infiltrating flux (source) is set equal to
zero. There is no possibility of a negative infiltration flux being
specified. If infiltrating water is rejected due to an infiltration
rate exceeding the vertical hydraulic conductivity, or because
saturation is reached in the unsaturated zone and the water table is
therefore at land surface, the concentration of the runoff will be
equal to CUZINF specified next. The runoff is routed if IRNBND is
specified in the MODFLOW simulation.
cuzinf : array of floats
Is the concentration of the infiltrating flux for a particular species.
An array for each species will be read.
incuzet : int
(This value is repeated for each stress period as explained next) A
flag indicating whether an array containing the concentration of
evapotranspiration flux originating from the unsaturated zone will be
read for the current stress period. If INCUZET >= 0, an array
containing the concentration of evapotranspiration flux originating
from the unsaturated zone for each species will be read. If
INCUZET < 0, the concentration of evapotranspiration flux for each
species will be reused from the last stress period. If INCUZET < 0
is specified for the first stress period, then by default, the
concentration of negative evapotranspiration flux (sink) is set
equal to the aquifer concentration, while the concentration of
positive evapotranspiration flux (source) is set to zero.
cuzet : array of floats
Is the concentration of ET fluxes originating from the unsaturated
zone. As a default, this array is set equal to 0 and only overridden
if the user specifies INCUZET > 1. If empirical evidence suggest
volatilization of simulated constituents from the unsaturated zone,
this may be one mechanism for simulating this process, though it would
depend on the amount of simulated ET originating from the unsaturated
zone. An array for each species will be read.
incgwet : int
(This value is repeated for each stress period as explained next) Is
a flag indicating whether an array containing the concentration of
evapotranspiration flux originating from the saturated zone will be
read for the current stress period. If INCGWET >= 0, an array
containing the concentration of evapotranspiration flux originating
from the saturated zone for each species will be read. If
INCGWET < 0, the concentration of evapotranspiration flux for each
species will be reused from the last stress period. If INCUZET < 0
is specified for the first stress period, then by default, the
concentration of negative evapotranspiration flux (sink) is set to
the aquifer concentration, while the concentration of positive
evapotranspiration flux (source) is set to zero.
cgwet : array of floats
Is the concentration of ET fluxes originating from the saturated zone.
As a default, this array is set equal to 0 and only overridden if the
user specifies INCUZET > 1. An array for each species will be read.
extension : string
Filename extension (default is 'uzt')
unitnumber : int
File unit number (default is None).
filenames : str or list of str
Filenames to use for the package and the output files. If
filenames=None the package name will be created using the model name
and package extension and the uzf output name will be created using
the model name and uzf concentration observation extension
(for example, modflowtest.cbc and modflowtest.uzcobs.out), if icbcuz
is a number greater than zero. If a single string is passed the
package will be set to the string and uzf concentration observation
output name will be created using the model name and .uzcobs.out
extension, if icbcuz is a number greater than zero. To define the
names for all package files (input and output) the length of the list
of strings should be 2. Default is None.
Attributes
----------
Methods
-------
See Also
--------
Notes
-----
Parameters are not supported in FloPy.
Examples
--------
>>> import flopy
>>> datadir = 'examples/data/mt3d_test/mfnwt_mt3dusgs/keat_uzf'
>>> mt = flopy.mt3d.Mt3dms.load(
... 'Keat_UZF_mt.nam', exe_name='mt3d-usgs_1.0.00.exe',
... model_ws=datadir, load_only='btn')
>>> uzt = flopy.mt3d.Mt3dUzt('Keat_UZF.uzt', mt)
"""
def __init__(
self,
model,
icbcuz=None,
iet=0,
iuzfbnd=None,
cuzinf=None,
cuzet=None,
cgwet=None,
extension="uzt",
unitnumber=None,
filenames=None,
**kwargs,
):
# set default unit number of one is not specified
if unitnumber is None:
unitnumber = Mt3dUzt._defaultunit()
elif unitnumber == 0:
unitnumber = Mt3dUzt._reservedunit()
# set filenames
filenames = self._prepare_filenames(filenames, 2)
if icbcuz is not None:
model.add_output_file(
icbcuz,
fname=filenames[1],
extension="uzcobs.out",
binflag=False,
package=self._ftype(),
)
else:
icbcuz = 0
# call base package constructor
super().__init__(
model,
extension=extension,
name=self._ftype(),
unit_number=unitnumber,
filenames=filenames[0],
)
# Set dimensions
nrow = model.nrow
ncol = model.ncol
nlay = model.nlay
ncomp = model.ncomp
mcomp = model.mcomp
# Set package specific parameters
self.heading1 = "# UZT for MT3D-USGS, generated by Flopy"
self.icbcuz = icbcuz
self.iet = iet
if iuzfbnd is not None:
self.iuzfbnd = Util2d(
self.parent,
(nrow, ncol),
np.int32,
iuzfbnd,
name="iuzfbnd",
locat=self.unit_number[0],
)
# set iuzfbnd based on UZF input file
else:
arr = np.zeros((nrow, ncol), dtype=np.int32)
self.iuzfbnd = Util2d(
self.parent,
(nrow, ncol),
np.int32,
arr,
name="iuzfbnd",
locat=self.unit_number[0],
)
# Note: list is used for multi-species, NOT for stress periods!
if cuzinf is not None:
self.cuzinf = []
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
cuzinf,
name="cuzinf1",
locat=self.unit_number[0],
)
self.cuzinf.append(t2d)
if ncomp > 1:
for icomp in range(2, ncomp + 1):
val = 0.0
name = f"cuzinf{icomp}"
if name in list(kwargs.keys()):
val = kwargs.pop(name)
else:
print(
"UZT: setting cuzinf for component "
f"{icomp} to zero. kwarg name {name}"
)
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
val,
name=name,
locat=self.unit_number[0],
)
self.cuzinf.append(t2d)
if cuzet is not None:
self.cuzet = []
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
cuzet,
name="cuzet1",
locat=self.unit_number[0],
)
self.cuzet.append(t2d)
if ncomp > 1:
for icomp in range(2, ncomp + 1):
val = 0.0
name = f"cuzet{icomp}"
if name in list(kwargs.keys()):
val = kwargs.pop(name)
else:
print(
"UZT: setting cuzet for component "
f"{icomp} to zero. kwarg name {name}"
)
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
val,
name=name,
locat=self.unit_number[0],
)
self.cuzet.append(t2d)
if cgwet is not None:
self.cgwet = []
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
cgwet,
name="cgwet1",
locat=self.unit_number[0],
)
self.cgwet.append(t2d)
if ncomp > 1:
for icomp in range(2, ncomp + 1):
val = 0.0
name = f"cgwet{icomp}"
if name in list(kwargs.keys()):
val = kwargs.pop(name)
else:
print(
"UZT: setting cgwet for component "
f"{icomp} to zero. kwarg name {name}"
)
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
val,
name=name,
locat=self.unit_number[0],
)
self.cgwet.append(t2d)
self.parent.add_package(self)
return
[docs] def write_file(self):
"""
Write the package file
Returns
-------
None
"""
# Open file for writing
f_uzt = open(self.fn_path, "w")
# Write header
f_uzt.write(f"#{self.heading1}\n")
# Item 2
f_uzt.write(
f"{self.icbcuz:10d}{self.iet:10d} #ICBCUZ, IET\n"
)
# Item 3
f_uzt.write(self.iuzfbnd.get_file_entry())
# Items 4-9
# (Loop through each stress period and write uzt information)
nper = self.parent.nper
for kper in range(nper):
if f_uzt.closed == True:
f_uzt = open(f_uzt.name, "a")
# Concentrations associated with distributed stresses (Infil, ET)
if self.cuzinf is not None:
# If any species needs to be written, then all need to be
# written
incuzinf = -1
for t2d in self.cuzinf:
incuzinficomp, file_entry = t2d.get_kper_entry(kper)
incuzinf = max(incuzinf, incuzinficomp)
if incuzinf == 1:
break
f_uzt.write(
f"{incuzinf:10d} # INCUZINF - SP {kper + 1:5d}\n"
)
if incuzinf == 1:
for t2d in self.cuzinf:
u2d = t2d[kper]
file_entry = u2d.get_file_entry()
f_uzt.write(file_entry)
if self.iet != 0:
if self.cuzet is not None:
# If any species needs to be written, then all need to be
# written
incuzet = -1
for t2d in self.cuzet:
incuzeticomp, file_entry = t2d.get_kper_entry(kper)
incuzet = max(incuzet, incuzeticomp)
if incuzet == 1:
break
f_uzt.write(
f"{incuzet:10d} # INCUZET - SP {kper + 1:5d}\n"
)
if incuzet == 1:
for t2d in self.cuzet:
u2d = t2d[kper]
file_entry = u2d.get_file_entry()
f_uzt.write(file_entry)
if self.cgwet is not None:
# If any species needs to be written, then all need to be
# written
incgwet = -1
for t2d in self.cgwet:
incgweticomp, file_entry = t2d.get_kper_entry(kper)
incgwet = max(incgwet, incgweticomp)
if incgwet == 1:
break
f_uzt.write(
f"{incgwet:10d} # INCGWET - SP {kper + 1:5d}\n"
)
if incgwet == 1:
for t2d in self.cgwet:
u2d = t2d[kper]
file_entry = u2d.get_file_entry()
f_uzt.write(file_entry)
f_uzt.write("\n")
f_uzt.close()
return
[docs] @classmethod
def load(
cls,
f,
model,
nlay=None,
nrow=None,
ncol=None,
nper=None,
ncomp=None,
ext_unit_dict=None,
):
"""
Load an existing package.
Parameters
----------
f : filename or file handle
File to load.
model : model object
The model object (of type :class:`flopy.mt3d.mt.Mt3dms`) to
which this package will be added.
ext_unit_dict : dictionary, optional
If the arrays in the file are specified using EXTERNAL,
or older style array control records, then `f` should be a file
handle. In this case ext_unit_dict is required, which can be
constructed using the function
:class:`flopy.utils.mfreadnam.parsenamefile`.
Returns
-------
uzt : Mt3dSsm object
Mt3dUzt object.
Examples
--------
>>> import flopy
>>> mt = flopy.mt3d.Mt3dms()
>>> uzt = flopy.mt3d.Mt3dUzt.load('test.uzt', mt)
"""
if model.verbose:
print("loading uzt package file...\n")
# Open file if necessary
openfile = not hasattr(f, "read")
if openfile:
filename = f
f = open(filename, "r")
# Set dimensions if necessary
if nlay is None:
nlay = model.nlay
if nrow is None:
nrow = model.nrow
if ncol is None:
ncol = model.ncol
if nper is None:
nper = model.nper
if ncomp is None:
ncomp = model.ncomp
# Item 1 (comments, must be preceded by '#')
if model.verbose:
print(" Reading off comment lines...")
line = f.readline()
while line[0:1] == "#":
i = 1
if model.verbose:
print(f" Comment Line {i}: {line.strip()}")
i += 1
line = f.readline()
# Item 2 (ICBCUZ, IET)
if line[0:1] != "#":
# Don't yet read the next line because the current line
# contains the values in item 2
m_arr = line.strip().split()
icbcuz = int(m_arr[0])
iet = int(m_arr[1])
# Item 3 [IUZFBND(NROW,NCOL) (one array for each layer)]
if model.verbose:
print(" loading IUZFBND...")
iuzfbnd = Util2d.load(
f, model, (nrow, ncol), np.int32, "iuzfbnd", ext_unit_dict
)
# kwargs needed to construct cuzinf2, cuzinf3, etc. for multispecies
kwargs = {}
cuzinf = None
# At least one species being simulated, so set up a place holder
t2d = Transient2d(
model, (nrow, ncol), np.float32, 0.0, name="cuzinf", locat=0
)
cuzinf = {0: t2d}
if ncomp > 1:
for icomp in range(2, ncomp + 1):
name = f"cuzinf{icomp}"
t2d = Transient2d(
model, (nrow, ncol), np.float32, 0.0, name=name, locat=0
)
kwargs[name] = {0: t2d}
# Repeat cuzinf initialization procedure for cuzet only if iet != 0
if iet != 0:
cuzet = None
t2d = Transient2d(
model, (nrow, ncol), np.float32, 0.0, name="cuzet", locat=0
)
cuzet = {0: t2d}
if ncomp > 1:
for icomp in range(2, ncomp + 1):
name = f"cuzet{icomp}"
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
0.0,
name=name,
locat=0,
)
kwargs[name] = {0: t2d}
# Repeat cuzinf initialization procedures for cgwet
cgwet = None
t2d = Transient2d(
model, (nrow, ncol), np.float32, 0.0, name="cgwet", locat=0
)
cgwet = {0: t2d}
if ncomp > 1:
for icomp in range(2, ncomp + 1):
name = f"cgwet{icomp}"
t2d = Transient2d(
model,
(nrow, ncol),
np.float32,
0.0,
name=name,
locat=0,
)
kwargs[name] = {0: t2d}
elif iet == 0:
cuzet = None
cgwet = None
# Start of transient data
for iper in range(nper):
if model.verbose:
print(f" loading UZT data for kper {iper + 1:5d}")
# Item 4 (INCUZINF)
line = f.readline()
m_arr = line.strip().split()
incuzinf = int(m_arr[0])
# Item 5 (CUZINF)
if incuzinf >= 0:
if model.verbose:
print(f" Reading CUZINF array for kper {iper + 1:5d}")
t = Util2d.load(
f, model, (nrow, ncol), np.float32, "cuzinf", ext_unit_dict
)
cuzinf[iper] = t
# Load each multispecies array
if ncomp > 1:
for icomp in range(2, ncomp + 1):
name = f"cuzinf{icomp}"
if model.verbose:
print(f" loading {name}...")
t = Util2d.load(
f,
model,
(nrow, ncol),
np.float32,
name,
ext_unit_dict,
)
cuzinficomp = kwargs[name]
cuzinficomp[iper] = t
elif incuzinf < 0 and iper == 0:
if model.verbose:
print(
" INCUZINF < 0 in first stress period. Setting "
"CUZINF to default value of 0.00 for all calls"
)
# This happens implicitly and is taken care of my
# existing functionality within flopy. This elif
# statement exist for the purpose of printing the message
# above
pass
elif incuzinf < 0 and iper > 0:
if model.verbose:
print(
f" Reusing CUZINF array from kper {iper:5d}"
f" in kper {iper + 1:5d}"
)
if iet != 0:
# Item 6 (INCUZET)
line = f.readline()
m_arr = line.strip().split()
incuzet = int(m_arr[0])
# Item 7 (CUZET)
if incuzet >= 0:
if model.verbose:
print(f" Reading CUZET array for kper {iper + 1:5d}")
t = Util2d.load(
f,
model,
(nrow, ncol),
np.float32,
"cuzet",
ext_unit_dict,
)
cuzet[iper] = t
# Load each multispecies array
if ncomp > 1:
for icomp in range(2, ncomp + 1):
name = f"cuzet{icomp}"
if model.verbose:
print(f" loading {name}")
t = Util2d.load(
f,
model,
(nrow, ncol),
np.float32,
name,
ext_unit_dict,
)
cuzeticomp = kwargs[name]
cuzeticomp[iper] = t
elif incuzet < 0 and iper == 0:
if model.verbose:
print(
" INCUZET < 0 in first stress period. Setting "
"CUZET to default value of 0.00 for all calls"
)
# This happens implicitly and is taken care of my
# existing functionality within flopy. This elif
# statement exist for the purpose of printing the message
# above
pass
else:
if model.verbose:
print(
f" Reusing CUZET array from kper {iper:5d}"
f" in kper {iper + 1:5d}"
)
# Item 8 (INCGWET)
line = f.readline()
m_arr = line.strip().split()
incgwet = int(m_arr[0])
# Item 9 (CGWET)
if model.verbose:
if incuzet >= 0:
print(f" Reading CGWET array for kper {iper + 1:5d}")
t = Util2d.load(
f,
model,
(nrow, ncol),
np.float32,
"cgwet",
ext_unit_dict,
)
cgwet[iper] = t
# Load each multispecies array
if ncomp > 1:
for icomp in range(2, ncomp + 1):
name = f"cgwet{icomp}"
if model.verbose:
print(f" loading {name}...")
t = Util2d.load(
f,
model,
(nrow, ncol),
np.float32,
name,
ext_unit_dict,
)
cgweticomp = kwargs[name]
cgweticomp[iper] = t
elif incuzet < 0 and iper == 0:
if model.verbose:
print(
" INCGWET < 0 in first stress period. Setting "
"CGWET to default value of 0.00 for all calls"
)
# This happens implicitly and is taken care of my
# existing functionality within flopy. This elif
# statement exist for the purpose of printing the
# message above
pass
elif incgwet < 0 and iper > 0:
if model.verbose:
print(
f" Reusing CGWET array from kper {iper:5d}"
f" in kper {iper + 1:5d}"
)
if openfile:
f.close()
unitnumber = None
filenames = [None, None]
if ext_unit_dict is not None:
unitnumber, filenames[0] = model.get_ext_dict_attr(
ext_unit_dict, filetype=Mt3dUzt._ftype()
)
if icbcuz > 0:
iu, filenames[1] = model.get_ext_dict_attr(
ext_unit_dict, unit=icbcuz
)
model.add_pop_key_list(icbcuz)
# Construct and return uzt package
return cls(
model,
icbcuz=icbcuz,
iet=iet,
iuzfbnd=iuzfbnd,
cuzinf=cuzinf,
cuzet=cuzet,
cgwet=cgwet,
unitnumber=unitnumber,
filenames=filenames,
**kwargs,
)
@staticmethod
def _ftype():
return "UZT2"
@staticmethod
def _defaultunit():
return 7
@staticmethod
def _reservedunit():
return 7