Source code for flopy.utils.utils_def

"""
Generic classes and utility functions
"""

from datetime import timedelta
from typing import Literal, get_args
from warnings import warn

import numpy as np

Precision = Literal["single", "double"]


[docs]class FlopyBinaryData: """ Defines integer, floating point, and character data types for MODFLOW binary files. """ def __init__(self): self.precision = "double" @property def integer(self) -> type: return np.int32 @property def integerbyte(self) -> int: return self.integer(1).nbytes @property def character(self) -> type: return np.uint8 @property def textbyte(self) -> int: return 1 @property def precision(self) -> Precision: return self._precision @precision.setter def precision(self, value: Precision): if value not in get_args(Precision): raise ValueError( f"Invalid floating precision '{value}', expected 'single' or 'double'" ) value = value.lower() self._precision = value if value == "double": self._real = np.float64 self._floattype = "f8" else: self._real = np.float32 self._floattype = "f4" self._realbyte = self.real(1).nbytes @property def real(self) -> type: return self._real @property def realbyte(self) -> int: return self._realbyte @property def floattype(self) -> str: return self._floattype
[docs] def set_float(self, precision): """ Set floating point precision. .. deprecated:: 3.5 This method will be removed in Flopy 3.10+. Use ``precision`` property setter instead. """ warn( "set_float() is deprecated, use precision property setter instead", PendingDeprecationWarning, ) self.precision = precision
[docs] def read_text(self, nchar=20): bytesvalue = self._read_values(self.character, nchar).tobytes() return bytesvalue.decode().strip()
[docs] def read_integer(self): return self._read_values(self.integer, 1)[0]
[docs] def read_real(self): return self._read_values(self.real, 1)[0]
[docs] def read_record(self, count, dtype=None): if dtype is None: dtype = self.dtype return self._read_values(dtype, count)
def _read_values(self, dtype, count): return np.fromfile(self.file, dtype, count)
[docs]def totim_to_datetime(totim, start="1-1-1970", timeunit="D"): """ Parameters ---------- totim : list or numpy array start : str Starting date for simulation. (default is 1-1-1970). timeunit : string time unit of the simulation time. Valid values are 'S'econds, 'M'inutes, 'H'ours, 'D'ays, 'Y'ears. (default is 'D'). Returns ------- out : list datetime object calculated from start and totim values """ key = None fact = 1.0 if timeunit.upper() == "S": key = "seconds" elif timeunit.upper() == "M": key = "minutes" elif timeunit.upper() == "H": key = "hours" elif timeunit.upper() == "D": key = "days" elif timeunit.upper() == "Y": key = "days" fact = 365.25 else: err = ( "'S'econds, 'M'inutes, 'H'ours, 'D'ays, 'Y'ears are the " + "only timeunit values that can be passed to totim_" + "to_datetime() function" ) raise Exception(err) out = [] kwargs = {} for to in totim: kwargs[key] = to * fact t = timedelta(**kwargs) out.append(start + t) return out
[docs]def get_pak_vals_shape(model, vals): """Function to define shape of package input data for Util2d. Parameters ---------- model : flopy model object vals : Package input values (dict of arrays or scalars, or ndarray, or single scalar). Returns ------- shape: tuple shape of input data for Util2d """ nrow, ncol, nlay, nper = model.nrow_ncol_nlay_nper if nrow is None: # unstructured if isinstance(vals, dict): try: # check for iterable _ = (v for v in next(iter(vals.values()))) except: return (1, ncol[0]) # default to layer 1 node count return np.array(next(iter(vals.values())), ndmin=2).shape else: # check for single iterable try: _ = (v for v in vals) except: return (1, ncol[0]) # default to layer 1 node count return np.array(vals, ndmin=2).shape else: return (nrow, ncol) # structured
[docs]def get_util2d_shape_for_layer(model, layer=0): """ Define nrow and ncol for array (Util2d) shape of a given layer in structured and/or unstructured models. Parameters ---------- model : model object model for which Util2d shape is sought. layer : int layer (base 0) for which Util2d shape is sought. Returns ------- (nrow,ncol) : tuple of ints util2d shape for the given layer """ nr, nc, _, _ = model.get_nrow_ncol_nlay_nper() if nr is None: # unstructured nrow = 1 ncol = nc[layer] else: # structured nrow = nr ncol = nc return (nrow, ncol)
[docs]def get_unitnumber_from_ext_unit_dict(model, pak_class, ext_unit_dict=None, ipakcb=0): """ For a given modflow package, defines input file unit number, plus package input and (optionally) output (budget) save file names. Parameters ---------- model : model object model for which the unit number is sought. pak_class : modflow package class for which the unit number is sought. ext_unit_dict : external unit dictionary, optional. If not provided, unitnumber and filenames will be returned as None. ipakcb : int, optional Modflow package unit number on which budget is saved. Default is 0, in which case the returned output file is None. Returns ------- unitnumber : int file unit number for the given modflow package (or None) filenames : list list of [package input file name, budget file name], """ unitnumber = None filenames = [None, None] if ext_unit_dict is not None: unitnumber, filenames[0] = model.get_ext_dict_attr( ext_unit_dict, filetype=pak_class._ftype() ) if ipakcb > 0: _, filenames[1] = model.get_ext_dict_attr(ext_unit_dict, unit=ipakcb) model.add_pop_key_list(ipakcb) return unitnumber, filenames
[docs]def type_from_iterable(_iter, index=0, _type=int, default_val=0): """Returns value of specified type from iterable. Parameters ---------- _iter : iterable index : int Iterable index to try to convert _type : Python type default_val : default value (0) Returns ------- val : value of type _type, or default_val """ try: val = _type(_iter[index]) except ValueError: val = default_val except IndexError: val = default_val return val
[docs]def get_open_file_object(fname_or_fobj, read_write="rw"): """Returns an open file object for either a file name or open file object.""" openfile = not (hasattr(fname_or_fobj, "read") or hasattr(fname_or_fobj, "write")) if openfile: filename = fname_or_fobj f_obj = open(filename, read_write) else: f_obj = fname_or_fobj return f_obj
[docs]def get_dis(model): """Returns dis or disu object from a given model object.""" dis = model.dis if not model.structured: dis = model.disu return dis