Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(datafiles): use 0-based kstp/kper indexing #2000

Merged
merged 4 commits into from
Nov 22, 2023
Merged
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
55 changes: 26 additions & 29 deletions flopy/utils/binaryfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,10 +458,12 @@ def _build_index(self):

if self.nrow < 0 or self.ncol < 0:
raise Exception("negative nrow, ncol")
if self.nrow > 1 and self.nrow * self.ncol > 10000000:
s = "Possible error. ncol ({}) * nrow ({}) > 10,000,000 "
s = s.format(self.ncol, self.nrow)
warnings.warn(s)

warn_threshold = 10000000
if self.nrow > 1 and self.nrow * self.ncol > warn_threshold:
warnings.warn(
f"Very large grid, ncol ({self.ncol}) * nrow ({self.nrow}) > {warn_threshold}"
)
self.file.seek(0, 2)
self.totalbytes = self.file.tell()
self.file.seek(0, 0)
Expand All @@ -473,14 +475,12 @@ def _build_index(self):
continue
if ipos == 0:
self.times.append(header["totim"])
kstpkper = (header["kstp"], header["kper"])
self.kstpkper.append(kstpkper)
self.kstpkper.append((header["kstp"], header["kper"]))
else:
totim = header["totim"]
if totim != self.times[-1]:
self.times.append(totim)
kstpkper = (header["kstp"], header["kper"])
self.kstpkper.append(kstpkper)
self.kstpkper.append((header["kstp"], header["kper"]))
ipos = self.file.tell()
self.iposarray.append(ipos)
databytes = self.get_databytes(header)
Expand Down Expand Up @@ -609,7 +609,7 @@ class HeadFile(BinaryLayerFile):
>>> import flopy.utils.binaryfile as bf
>>> hdobj = bf.HeadFile('model.hds', precision='single')
>>> hdobj.list_records()
>>> rec = hdobj.get_data(kstpkper=(1, 50))
>>> rec = hdobj.get_data(kstpkper=(0, 49))

>>> ddnobj = bf.HeadFile('model.ddn', text='drawdown', precision='single')
>>> ddnobj.list_records()
Expand Down Expand Up @@ -762,7 +762,7 @@ class UcnFile(BinaryLayerFile):
>>> import flopy.utils.binaryfile as bf
>>> ucnobj = bf.UcnFile('MT3D001.UCN', precision='single')
>>> ucnobj.list_records()
>>> rec = ucnobj.get_data(kstpkper=(1,1))
>>> rec = ucnobj.get_data(kstpkper=(0, 0))

"""

Expand Down Expand Up @@ -829,7 +829,7 @@ class HeadUFile(BinaryLayerFile):
>>> import flopy.utils.binaryfile as bf
>>> hdobj = bf.HeadUFile('model.hds')
>>> hdobj.list_records()
>>> usgheads = hdobj.get_data(kstpkper=(1, 50))
>>> usgheads = hdobj.get_data(kstpkper=(0, 49))

"""

Expand Down Expand Up @@ -1505,19 +1505,16 @@ def _unique_package_names(self):

def get_kstpkper(self):
"""
Get a list of unique stress periods and time steps in the file
Get a list of unique tuples (stress period, time step) in the file.
Indices are 0-based, use the `kstpkper` attribute for 1-based.

Returns
----------
out : list of (kstp, kper) tuples
List of unique kstp, kper combinations in binary file. kstp and
kper values are zero-based.

-------
list of (kstp, kper) tuples
List of unique combinations of stress period &
time step indices (0-based) in the binary file
"""
kstpkper = []
for kstp, kper in self.kstpkper:
kstpkper.append((kstp - 1, kper - 1))
return kstpkper
return [(kstp - 1, kper - 1) for kstp, kper in self.kstpkper]

def get_indices(self, text=None):
"""
Expand Down Expand Up @@ -1764,25 +1761,25 @@ def get_ts(self, idx, text=None, times=None):
# Initialize result array and put times in first column
result = self._init_result(nstation)

kk = self.get_kstpkper()
timesint = self.get_times()
kstpkper = self.get_kstpkper()
nsteps = len(kstpkper)
if len(timesint) < 1:
if times is None:
timesint = [x + 1 for x in range(len(kk))]
timesint = [x + 1 for x in range(nsteps)]
else:
if isinstance(times, np.ndarray):
times = times.tolist()
if len(times) != len(kk):
raise Exception(
"times passed to CellBudgetFile get_ts() "
"method must be equal to {} "
"not {}".format(len(kk), len(times))
if len(times) != nsteps:
raise ValueError(
f"number of times provided ({len(times)}) must equal "
f"number of time steps in cell budget file ({nsteps})"
)
timesint = times
for idx, t in enumerate(timesint):
result[idx, 0] = t

for itim, k in enumerate(kk):
for itim, k in enumerate(kstpkper):
try:
v = self.get_data(kstpkper=k, text=text, full3D=True)
# skip missing data - required for storage
Expand Down
34 changes: 12 additions & 22 deletions flopy/utils/datafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,8 @@ def get_values(self):

class LayerFile:
"""
The LayerFile class is the abstract base class from which specific derived
classes are formed. LayerFile This class should not be instantiated
directly.
Base class for layered output files.
Do not instantiate directly.

"""

Expand Down Expand Up @@ -477,19 +476,16 @@ def get_times(self):

def get_kstpkper(self):
"""
Get a list of unique stress periods and time steps in the file
Get a list of unique tuples (stress period, time step) in the file.
Indices are 0-based, use the `kstpkper` attribute for 1-based.

Returns
----------
out : list of (kstp, kper) tuples
List of unique kstp, kper combinations in binary file. kstp and
kper values are presently zero-based.

-------
list of (kstp, kper) tuples
List of unique combinations of stress period &
time step indices (0-based) in the binary file
"""
kstpkper = []
for kstp, kper in self.kstpkper:
kstpkper.append((kstp - 1, kper - 1))
return kstpkper
return [(kstp - 1, kper - 1) for kstp, kper in self.kstpkper]

def get_data(self, kstpkper=None, idx=None, totim=None, mflay=None):
"""
Expand All @@ -498,10 +494,9 @@ def get_data(self, kstpkper=None, idx=None, totim=None, mflay=None):
Parameters
----------
idx : int
The zero-based record number. The first record is record 0.
The zero-based record number. The first record is record 0.
kstpkper : tuple of ints
A tuple containing the time step and stress period (kstp, kper).
These are zero-based kstp and kper values.
A tuple (kstep, kper) of zero-based time step and stress period.
totim : float
The simulation time.
mflay : integer
Expand All @@ -514,14 +509,9 @@ def get_data(self, kstpkper=None, idx=None, totim=None, mflay=None):
Array has size (nlay, nrow, ncol) if mflay is None or it has size
(nrow, ncol) if mlay is specified.

See Also
--------

Notes
-----
if both kstpkper and totim are None, will return the last entry
Examples
--------
If both kstpkper and totim are None, the last entry will be returned.

"""
# One-based kstp and kper for pulling out of recarray
Expand Down
7 changes: 2 additions & 5 deletions flopy/utils/formattedfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _build_index(self):
Build the recordarray and iposarray, which maps the header information
to the position in the formatted file.
"""
self.kstpkper # array of time step/stress periods with data available
self.kstpkper # 1-based array of time step/stress periods
self.recordarray # array of data headers
self.iposarray # array of seek positions for each record
self.nlay # Number of model layers
Expand Down Expand Up @@ -155,7 +155,6 @@ def _build_index(self):
self.recordarray = np.array(self.recordarray, self.header.get_dtype())
self.iposarray = np.array(self.iposarray)
self.nlay = np.max(self.recordarray["ilay"])
return

def _store_record(self, header, ipos):
"""
Expand Down Expand Up @@ -356,10 +355,9 @@ class FormattedHeadFile(FormattedLayerFile):
>>> import flopy.utils.formattedfile as ff
>>> hdobj = ff.FormattedHeadFile('model.fhd', precision='single')
>>> hdobj.list_records()
>>> rec = hdobj.get_data(kstpkper=(1, 50))
>>> rec = hdobj.get_data(kstpkper=(0, 49))
>>> rec2 = ddnobj.get_data(totim=100.)


"""

def __init__(
Expand All @@ -372,7 +370,6 @@ def __init__(
):
self.text = text
super().__init__(filename, precision, verbose, kwargs)
return

def _get_text_header(self):
"""
Expand Down