Skip to content

Commit

Permalink
refactor(datafile): ignore "text" parameter, add attributes from file
Browse files Browse the repository at this point in the history
  • Loading branch information
mwtoews committed Jun 14, 2024
1 parent e2a85a3 commit 6d2abab
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 48 deletions.
82 changes: 81 additions & 1 deletion autotest/test_binaryfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
See also test_cellbudgetfile.py for similar tests.
"""

import warnings
from itertools import repeat

import numpy as np
Expand Down Expand Up @@ -104,6 +105,8 @@ def test_headfile_build_index(example_data_path):
assert hds.ncol == 20
assert hds.nlay == 3
assert not hasattr(hds, "nper")
assert hds.text == "head"
assert hds.text_bytes == b"HEAD".rjust(16)
assert hds.totalbytes == 10_676_004
assert len(hds.recordarray) == 3291
assert type(hds.recordarray) == np.ndarray
Expand Down Expand Up @@ -150,7 +153,80 @@ def test_headfile_build_index(example_data_path):
)


def test_concentration_build_index(example_data_path):
@pytest.mark.parametrize(
"pth, expected",
[
pytest.param(
"mf6-freyberg/freyberg.hds",
{
"precision": "double",
"nlay, nrow, ncol": (1, 40, 20),
"text": "head",
"text_bytes": b"HEAD".ljust(16),
"len(obj)": 1,
},
id="freyberg.hds",
),
pytest.param(
"mf6/create_tests/test_transport/expected_output/gwt_mst03.ucn",
{
"precision": "double",
"nlay, nrow, ncol": (1, 1, 1),
"text": "concentration",
"text_bytes": b"CONCENTRATION".ljust(16),
"len(obj)": 28,
},
id="gwt_mst03.ucn",
),
pytest.param(
"mfusg_test/03A_conduit_unconfined/output/ex3A.cln.hds",
{
"precision": "single",
"nlay, nrow, ncol": (1, 1, 2),
"text": "cln_heads",
"text_bytes": b"CLN HEADS".rjust(16),
"len(obj)": 1,
},
id="ex3A.cln.hds",
),
pytest.param(
"mfusg_test/03A_conduit_unconfined/output/ex3A.ddn",
{
"precision": "single",
"nlay, nrow, ncol": (2, 100, 100),
"text": "drawdown",
"text_bytes": b"DRAWDOWN".rjust(16),
"len(obj)": 2,
},
id="ex3A.ddn",
),
],
)
def test_headfile_examples(example_data_path, pth, expected):
with HeadFile(example_data_path / pth) as obj:
assert obj.precision == expected["precision"]
assert (obj.nlay, obj.nrow, obj.ncol) == expected["nlay, nrow, ncol"]
assert obj.text == expected["text"]
assert obj.text_bytes == expected["text_bytes"]
assert len(obj) == expected["len(obj)"]


@pytest.mark.parametrize(
"pth",
[
"mt3d_test/mf96mt3d/P01/case1b/MT3D001.UCN",
"unstructured/headu.githds",
],
)
def test_not_headfile(example_data_path, pth):
# These examples pass get_headfile_precision, but are not HeadFiles
with pytest.raises(ValueError, match="cannot read file with HeadFile"):
with warnings.catch_warnings():
warnings.simplefilter("ignore")
HeadFile(example_data_path / pth)


def test_ucnfile_build_index(example_data_path):
# test low-level BinaryLayerFile._build_index() method with UCN file
pth = example_data_path / "mt3d_test/mf2005mt3d/P07/MT3D001.UCN"
with UcnFile(pth) as ucn:
Expand All @@ -159,6 +235,8 @@ def test_concentration_build_index(example_data_path):
assert ucn.ncol == 21
assert ucn.nlay == 8
assert not hasattr(ucn, "nper")
assert ucn.text == "concentration"
assert ucn.text_bytes == b"CONCENTRATION".ljust(16)
assert ucn.totalbytes == 10_432
assert len(ucn.recordarray) == 8
assert type(ucn.recordarray) == np.ndarray
Expand Down Expand Up @@ -296,6 +374,8 @@ def test_headu_file_data(function_tmpdir, example_data_path):
headobj = HeadUFile(fname)
assert isinstance(headobj, HeadUFile)
assert headobj.nlay == 3
assert headobj.text == "headu"
assert headobj.text_bytes == b"HEADU".rjust(16)

# ensure recordarray is has correct data
ra = headobj.recordarray
Expand Down
1 change: 1 addition & 0 deletions autotest/test_formattedfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def test_headfile_build_index(example_data_path):
assert hds.ncol == 10
assert hds.nlay == 1
assert not hasattr(hds, "nper")
assert hds.text == "head"
assert hds.totalbytes == 1613
assert len(hds.recordarray) == 1
assert type(hds.recordarray) == np.ndarray
Expand Down
4 changes: 2 additions & 2 deletions flopy/export/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def output_helper(
times,
shape3d,
out_obj,
"concentration",
out_obj.text,
logger=logger,
mask_vals=mask_vals,
mask_array3d=mask_array3d,
Expand All @@ -446,7 +446,7 @@ def output_helper(
times,
shape3d,
out_obj,
out_obj.text.decode(),
out_obj.text,
logger=logger,
mask_vals=mask_vals,
mask_array3d=mask_array3d,
Expand Down
6 changes: 2 additions & 4 deletions flopy/mf6/utils/binaryfile_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def _get_binary_file_object(self, path, bintype, key):

elif bintype == "DDN":
try:
return bf.HeadFile(path, text="drawdown", precision="double")
return bf.HeadFile(path, precision="double")
except AssertionError:
raise AssertionError(f"{self.dataDict[key]} does not exist")

Expand Down Expand Up @@ -333,9 +333,7 @@ def _setbinarykeys(self, binarypathdict):

elif key[1] == "DDN":
try:
readddn = bf.HeadFile(
path, text="drawdown", precision="double"
)
readddn = bf.HeadFile(path, precision="double")
self.dataDict[(key[0], key[1], "DRAWDOWN")] = path
readddn.close()

Expand Down
83 changes: 44 additions & 39 deletions flopy/utils/binaryfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ def _build_index(self):
header = self._get_header()
self.nrow = header["nrow"]
self.ncol = header["ncol"]
self.text_bytes = header["text"]
self.text = (
self.text_bytes.decode("ascii").strip().lower().replace(" ", "_")
)
if header["ilay"] > self.nlay:
self.nlay = header["ilay"]

Expand All @@ -488,8 +492,12 @@ def _build_index(self):
while ipos < self.totalbytes:
header = self._get_header()
self.recordarray.append(header)
if self.text.upper() not in header["text"]:
continue
if header["text"] != self.text_bytes:
warnings.warn(
"inconsistent text headers changing from "
f"{self.text_bytes!r} to {header['text']!r}",
UserWarning,
)
if ipos == 0:
self.times.append(header["totim"])
self.kstpkper.append((header["kstp"], header["kper"]))
Expand All @@ -501,6 +509,8 @@ def _build_index(self):
ipos = self.file.tell()
self.iposarray.append(ipos)
databytes = self.get_databytes(header)
if ipos + databytes > self.totalbytes:
raise EOFError(f"attempting to seek {ipos + databytes}")
self.file.seek(databytes, 1)
ipos = self.file.tell()

Expand Down Expand Up @@ -617,14 +627,13 @@ class HeadFile(BinaryLayerFile):
----------
filename : str or PathLike
Path of the head file.
text : string
Name of the text string in the head file. Default is 'head'.
precision : string
Precision of floating point head data in the value. Accepted
values are 'auto', 'single' or 'double'. Default is 'auto',
which enables automatic detection of precision.
verbose : bool
Toggle logging output. Default is False.
text : str
Ignored.
precision : {'auto', 'single', 'double'}
Precision of floating point head data in the value. Default
'auto' enables automatic detection of precision.
verbose : bool, default False
Toggle logging output.
Examples
--------
Expand All @@ -634,7 +643,7 @@ class HeadFile(BinaryLayerFile):
>>> hdobj.list_records()
>>> rec = hdobj.get_data(kstpkper=(0, 49))
>>> ddnobj = bf.HeadFile('model.ddn', text='drawdown', precision='single')
>>> ddnobj = bf.HeadFile('model.ddn', precision='single')
>>> ddnobj.list_records()
>>> rec = ddnobj.get_data(totim=100.)
Expand All @@ -643,12 +652,11 @@ class HeadFile(BinaryLayerFile):
def __init__(
self,
filename: Union[str, os.PathLike],
text="head",
text="head", # noqa ARG002
precision="auto",
verbose=False,
**kwargs,
):
self.text = text.encode()
if precision == "auto":
precision = get_headfile_precision(filename)
if precision == "unknown":
Expand Down Expand Up @@ -749,14 +757,15 @@ class UcnFile(BinaryLayerFile):
Parameters
----------
filename : string
Name of the concentration file
text : string
Name of the text string in the ucn file. Default is 'CONCENTRATION'
precision : string
'auto', 'single' or 'double'. Default is 'auto'.
verbose : bool
Write information to the screen. Default is False.
filename : str or PathLike
Path of the concentration file.
text : str
Ignored.
precision : {'auto', 'single', 'double'}
Precision of floating point values. Default 'auto' enables automatic
detection of precision.
verbose : bool, default False
Write information to the screen.
Attributes
----------
Expand Down Expand Up @@ -792,12 +801,11 @@ class UcnFile(BinaryLayerFile):
def __init__(
self,
filename,
text="concentration",
text="concentration", # noqa ARG002
precision="auto",
verbose=False,
**kwargs,
):
self.text = text.encode()
if precision == "auto":
precision = get_headfile_precision(filename)
if precision == "unknown":
Expand All @@ -821,14 +829,13 @@ class HeadUFile(BinaryLayerFile):
----------
filename : str or PathLike
Path of the head file
text : string
Name of the text string in the head file. Default is 'headu'.
precision : string
Precision of the floating point head data in the file. Accepted
values are 'auto', 'single' or 'double'. Default is 'auto', which
enables precision to be automatically detected.
verbose : bool
Toggle logging output. Default is False.
text : str
Ignored.
precision : {'auto', 'single', 'double'}
Precision of floating point values. Default 'auto' enables automatic
detection of precision.
verbose : bool, default False
Toggle logging output.
Notes
-----
Expand Down Expand Up @@ -859,15 +866,14 @@ class HeadUFile(BinaryLayerFile):
def __init__(
self,
filename: Union[str, os.PathLike],
text="headu",
text="headu", # noqa ARG002
precision="auto",
verbose=False,
**kwargs,
):
"""
Class constructor
"""
self.text = text.encode()
if precision == "auto":
precision = get_headfile_precision(filename)
if precision == "unknown":
Expand Down Expand Up @@ -990,11 +996,11 @@ class CellBudgetFile:
----------
filename : str or PathLike
Path of the cell budget file.
precision : string
Precision of floating point budget data in the file. Accepted
values are 'single' or 'double'. Default is 'single'.
verbose : bool
Toggle logging output. Default is False.
precision : {'auto', 'single', 'double'}
Precision of floating point values. Default 'auto' enables automatic
detection of precision.
verbose : bool, default False
Toggle logging output.
Examples
--------
Expand Down Expand Up @@ -2217,7 +2223,6 @@ def reverse(self, filename: Optional[os.PathLike] = None):
Parameters
----------
filename : str or PathLike, optional
Path of the new reversed binary cell budget file to create.
"""
Expand Down
9 changes: 7 additions & 2 deletions flopy/utils/datafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,13 @@ def __init__(
args = ",".join(kwargs.keys())
raise Exception(f"LayerFile error: unrecognized kwargs: {args}")

# read through the file and build the pointer index
self._build_index()
try:
# read through the file and build the pointer index
self._build_index()
except EOFError:
raise ValueError(
f"cannot read file with {self.__class__.__name__}"
)

# now that we read the data and know nrow and ncol,
# we can make a generic mg if needed
Expand Down

0 comments on commit 6d2abab

Please sign in to comment.