"""
mfreadnam module. Contains the NamData class. Note that the user can access
the NamData class as `flopy.modflow.NamData`.
Additional information about the MODFLOW name file can be found at the `Online
MODFLOW Guide
<http://water.usgs.gov/ogw/modflow/MODFLOW-2005-Guide/name_file.htm>`_.
"""
import os
[docs]class NamData:
"""
MODFLOW Namefile Class.
Parameters
----------
pkgtype : string
String identifying the type of MODFLOW package. See the
mfnam_packages dictionary keys in the model object for a list
of supported packages. This dictionary is also passed in as packages.
name : string
Filename of the package file identified in the name file
handle : file handle
File handle referring to the file identified by `name`
packages : dictionary
Dictionary of package objects as defined in the
`mfnam_packages` attribute of :class:`flopy.modflow.mf.Modflow`.
Attributes
----------
filehandle : file handle
File handle to the package file. Read from `handle`.
filename : string
Filename of the package file identified in the name file.
Read from `name`.
filetype : string
String identifying the type of MODFLOW package. Read from
`pkgtype`.
package : string
Package type. Only assigned if `pkgtype` is found in the keys
of `packages`
Methods
-------
See Also
--------
Notes
-----
Examples
--------
"""
def __init__(self, pkgtype, name, handle, packages):
self.filehandle = handle
self.filename = name
self.filetype = pkgtype
self.package = None
if self.filetype.lower() in packages:
self.package = packages[self.filetype.lower()]
def __repr__(self):
return f"filename:{self.filename}, filetype:{self.filetype}"
[docs]def getfiletypeunit(nf, filetype):
"""
Method to return unit number of a package from a NamData instance
Parameters
----------
nf : NamData instance
filetype : string, name of package seeking information for
Returns
-------
cunit : int, unit number corresponding to the package type
"""
for cunit, cvals in nf.items():
if cvals.filetype.lower() == filetype.lower():
return cunit
print(f'Name file does not contain file of type "{filetype}"')
return None
[docs]def parsenamefile(namfilename, packages, verbose=True):
"""
Returns dict from the nam file with NamData keyed by unit number
Parameters
----------
namefilename : str
Name of the MODFLOW namefile to parse.
packages : dict
Dictionary of package objects as defined in the `mfnam_packages`
attribute of :class:`flopy.modflow.mf.Modflow`.
verbose : bool
Print messages to screen. Default is True.
Returns
-------
dict
For each file listed in the name file, a
:class:`flopy.utils.mfreadnam.NamData` instance
is stored in the returned dict keyed by unit number.
Raises
------
FileNotFoundError
If namfilename does not exist in the directory.
ValueError
For lines that cannot be parsed.
"""
# initiate the ext_unit_dict dictionary
ext_unit_dict = {}
if verbose:
print(f"Parsing the namefile --> {namfilename}")
if not os.path.isfile(namfilename):
# help diagnose the namfile and directory
raise FileNotFoundError(
f"Could not find {namfilename} "
f"in directory {os.path.dirname(namfilename)}"
)
with open(namfilename, "r") as fp:
lines = fp.readlines()
for ln, line in enumerate(lines, 1):
line = line.strip()
if len(line) == 0 or line.startswith("#"):
# skip blank lines or comments
continue
items = line.split()
# ensure we have at least three items
if len(items) < 3:
e = f"line number {ln} has fewer than 3 items: {line}"
raise ValueError(e)
ftype, key, fpath = items[0:3]
ftype = ftype.upper()
# remove quotes in file path
if '"' in fpath:
fpath = fpath.replace('"', "")
if "'" in fpath:
fpath = fpath.replace("'", "")
# need make filenames with paths system agnostic
if "/" in fpath:
raw = fpath.split("/")
elif "\\" in fpath:
raw = fpath.split("\\")
else:
raw = [fpath]
fpath = os.path.join(*raw)
fname = os.path.join(os.path.dirname(namfilename), fpath)
if not os.path.isfile(fname) or not os.path.exists(fname):
# change to lower and make comparison (required for linux)
dn = os.path.dirname(fname)
fls = os.listdir(dn)
lownams = [f.lower() for f in fls]
bname = os.path.basename(fname)
if bname.lower() in lownams:
idx = lownams.index(bname.lower())
fname = os.path.join(dn, fls[idx])
# open the file
kwargs = {}
if ftype == "DATA(BINARY)":
openmode = "rb"
else:
openmode = "r"
kwargs["errors"] = "replace"
try:
filehandle = open(fname, openmode, **kwargs)
except OSError:
if verbose:
print(f"could not set filehandle to {fpath}")
filehandle = None
# be sure the second value is an integer
try:
key = int(key)
except ValueError:
raise ValueError(
"line number {}: the unit number (second item) "
"is not an integer: {}".format(ln, line)
)
# Trap for the case where unit numbers are specified as zero
# In this case, the package must have a variable called
# unit number attached to it. If not, then the key is set
# to fname
if key == 0:
ftype_lower = ftype.lower()
if ftype_lower in packages:
key = packages[ftype_lower]._reservedunit()
else:
key = ftype
ext_unit_dict[key] = NamData(ftype, fname, filehandle, packages)
return ext_unit_dict