model_output package#

Submodules#

ATS output readers#

Read ATS visualization HDF5 files (field / cycle / scalar cell values).

Supports current ats_vis_*_data.h5 and older visdump_data.h5 files. The domain is read from the filename (ats_vis_data.h5 is the subsurface domain, ats_vis_surface_data.h5 the surface), and a variable is found under its plain name or with that domain’s prefix (domain-saturation_liquid), as ATS names them; other names are selected with variable_map. Cell centres come from the companion *_mesh.h5 file ATS writes beside the data.

class PyHydroGeophysX.model_output.ats_output.ATSOutput(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: HDF5ModelOutput

ATS subsurface scalar output; returns one-dimensional cell arrays.

filename is relative to model_directory or absolute. When omitted, exactly one of the standard subsurface filenames must exist. time_unit overrides missing file metadata; otherwise an unknown unit remains None.

property mesh_filename#

The mesh file ATS writes beside the data file (*_mesh.h5).

output_cell_centers(timestep_idx=0, *, axes=None)[source]#

Element centres from ATS’s mesh file, in the cell order of the data.

The mesh of this timestep’s cycle is used when ATS wrote one (a deforming mesh), else the latest mesh written before it, else the only one (a fixed mesh).

read_field(variable, timestep_idx)[source]#

Read a canonical field or an exact HDF5 variable name.

class PyHydroGeophysX.model_output.ats_output.ATSPorosity(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: ATSOutput

Read porosity with its own available cycles.

class PyHydroGeophysX.model_output.ats_output.ATSSaturation(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: ATSOutput

Read liquid saturation in native ATS cell order.

class PyHydroGeophysX.model_output.ats_output.ATSWaterContent(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: ATSOutput

Read volumetric liquid water content calculated as porosity * saturation.

PFLOTRAN output readers#

Read PFLOTRAN snapshot HDF5 output with Time: <value> <unit> groups.

class PyHydroGeophysX.model_output.pflotran_output.PFLOTRANOutput(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: HDF5ModelOutput

Read single-file or multiple-file snapshots in numerical time order.

Select a run using run_name (matches run_name.h5 and the numbered files run_name-NNN.h5 of PFLOTRAN’s multiple-file output, and no other run’s files), or provide filename explicitly. Unstructured arrays and structured arrays retain their HDF5 shape and axes - (nx, ny, nz) for a structured grid, as PFLOTRAN writes it - and output_cell_centers() gives the cell centres in their C-order flattening. Mixed time units and duplicate snapshot times are rejected to avoid silently joining unrelated runs or restarts.

output_cell_centers(timestep_idx=0, *, axes=None)[source]#

Cell centres from the run’s own grid, in the C-order flattening of load_timestep(timestep_idx).

A structured grid’s Coordinates group holds the cell edges along x, y and z, and its fields are (nx, ny, nz); an unstructured grid’s Domain group holds the vertices and the XDMF cell connectivity. The snapshot’s own file is looked in first, then the run’s other files.

read_field(variable, timestep_idx)[source]#

Read a canonical field or an exact HDF5 variable name.

class PyHydroGeophysX.model_output.pflotran_output.PFLOTRANPorosity(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: PFLOTRANOutput

Read snapshot porosity.

class PyHydroGeophysX.model_output.pflotran_output.PFLOTRANSaturation(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: PFLOTRANOutput

Read liquid saturation without changing HDF5 axis order.

class PyHydroGeophysX.model_output.pflotran_output.PFLOTRANWaterContent(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: PFLOTRANOutput

Read volumetric liquid water content calculated as porosity * saturation.

PyHydroGeophysX.model_output.base module#

Base classes for model output processing.

class PyHydroGeophysX.model_output.base.HydroModelOutput(model_directory: str)[source]#

Bases: ABC

Base class for all hydrological model outputs.

calculate_saturation(water_content: ndarray, porosity: float | ndarray) → ndarray[source]#

Calculate saturation from water content and porosity.

Parameters:
  • water_content – Water content array

  • porosity – Porosity value(s)

Returns:

Saturation array

abstractmethod get_timestep_info() → List[Tuple][source]#

Get information about each timestep.

Returns:

List of timestep information tuples

abstractmethod load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs) → ndarray[source]#

Load data for a range of timesteps.

Parameters:
  • start_idx – Starting timestep index

  • end_idx – Ending timestep index (exclusive)

  • **kwargs – Additional parameters specific to the model type

Returns:

Data array for the specified timestep range

abstractmethod load_timestep(timestep_idx: int, **kwargs) → ndarray[source]#

Load data for a specific timestep.

Parameters:
  • timestep_idx – Index of the timestep to load

  • **kwargs – Additional parameters specific to the model type

Returns:

Data array for the specified timestep

PyHydroGeophysX.model_output.modflow_output module#

Deprecated in 0.5.0 and removed in 0.6.0: a compatibility path that warns and re-exports the MODFLOW readers from PyHydroGeophysX.model_output.water_content, documented below.

PyHydroGeophysX.model_output.parflow_output module#

Module for processing ParFlow model outputs.

This module provides classes to handle specific types of ParFlow outputs, such as saturation and porosity, by reading ParFlow Binary Files (PFB). It includes a standalone PFB reader that works without the parflow package, falling back to the parflow package if available for maximum compatibility.

class PyHydroGeophysX.model_output.parflow_output.ParflowOutput(model_directory: str, run_name: str)[source]#

Bases: HydroModelOutput

Base class for processing ParFlow model outputs.

This class handles common ParFlow output functionalities, such as identifying available timesteps and reading PFB files. It uses the standalone PFB reader that works without the parflow package installed.

get_pfb_dimensions(pfb_file_path: str) → Tuple[int, int, int][source]#

Reads a PFB file and returns its data dimensions (nz, ny, nx).

Parameters:

pfb_file_path (str) – The full path to the PFB file.

Returns:

The dimensions of the data in the PFB file,

typically in (nz, ny, nx) order for ParFlow.

Return type:

Tuple[int, int, int]

Raises:
  • FileNotFoundError – If the pfb_file_path does not exist.

  • Exception – If self.read_pfb (from parflow.tools.io) fails to read the file.

class PyHydroGeophysX.model_output.parflow_output.ParflowPorosity(model_directory: str, run_name: str)[source]#

Bases: ParflowOutput

Processes porosity data from ParFlow simulations. Porosity in ParFlow is typically static (time-invariant) and stored in a single PFB file (e.g., <run_name>.out.porosity.pfb or similar).

get_timestep_info() → List[Tuple[int, float]][source]#

Returns timestep information, typically based on other ParFlow outputs (like saturation) as porosity itself is static.

Returns:

A list of (timestep_number, time_value) tuples,

derived from self.available_timesteps.

Return type:

List[Tuple[int, float]]

load_mask() → ndarray[source]#

Load the domain mask data from a ParFlow model. The mask file (.out.mask.pfb) indicates active (1) and inactive (0) cells.

Returns:

A 3D NumPy array representing the domain mask (nz, ny, nx).

Values are typically 0 or 1.

Return type:

np.ndarray

Raises:
  • FileNotFoundError – If no standard mask PFB file can be found.

  • ValueError – If there’s an error reading or processing the PFB file.

load_porosity() → ndarray[source]#

Load the static porosity data from the ParFlow model.

It searches for common ParFlow porosity filename patterns within the model directory.

Returns:

A 3D NumPy array of porosity values (nz, ny, nx).

Return type:

np.ndarray

Raises:
  • FileNotFoundError – If no standard porosity PFB file can be found.

  • ValueError – If there’s an error reading or processing the PFB file.

load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs: Any) → ndarray[source]#

Load porosity data for a conceptual range of timesteps. Since porosity is time-invariant, this method returns a 4D array where the static 3D porosity data is repeated along the time axis.

The number of repetitions along the time axis (nt) is determined by the length of self.available_timesteps (discovered from saturation/pressure files) if end_idx is None, or by min(end_idx - start_idx, len(available_timesteps)). A minimum of 1 repetition is ensured if any timesteps are notionally available.

Parameters:
  • start_idx (int, optional) – Starting timestep index (used to determine nt). Defaults to 0.

  • end_idx (Optional[int], optional) – Ending timestep index (exclusive, used for nt). Defaults to None (use all available timesteps).

  • **kwargs (Any) – Additional keyword arguments (not used).

Returns:

A 4D NumPy array of porosity values (nt, nz, ny, nx).

All slices along the time dimension are identical.

Return type:

np.ndarray

load_timestep(timestep_idx: int, **kwargs: Any) → ndarray[source]#

Load porosity data. For ParFlow, porosity is typically time-invariant. This method returns the static porosity array, ignoring timestep_idx.

Parameters:
  • timestep_idx (int) – Index of the timestep (ignored, as porosity is static).

  • **kwargs (Any) – Additional keyword arguments (not used).

Returns:

A 3D NumPy array of porosity values (nz, ny, nx).

Return type:

np.ndarray

class PyHydroGeophysX.model_output.parflow_output.ParflowSaturation(model_directory: str, run_name: str)[source]#

Bases: ParflowOutput

Processes saturation data from ParFlow simulations (.out.satur.*.pfb files).

get_timestep_info() → List[Tuple[int, float]][source]#

Provides information about available ParFlow timesteps.

For ParFlow, the timestep number from the filename often directly corresponds to the simulation time (e.g., if output is every 1 hour, timestep 24 is 24 hours). This method returns a list of tuples: (timestep_number, simulation_time). Currently, simulation_time is simply cast from timestep_number. More accurate time mapping would require parsing ParFlow timing files if complex.

Returns:

A list where each tuple is (timestep_number, time_value).

Time_value is float representation of timestep_number.

Return type:

List[Tuple[int, float]]

load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs: Any) → ndarray[source]#

Load saturation data for a specified range of zero-based timestep indices.

Parameters:
  • start_idx (int, optional) – Starting zero-based timestep index. Defaults to 0.

  • end_idx (Optional[int], optional) – Ending zero-based timestep index (exclusive). If None, loads up to the last available timestep. Defaults to None.

  • **kwargs (Any) – Additional keyword arguments (not used).

Returns:

A 4D NumPy array of saturation values (num_timesteps, nz, ny, nx).

Returns an empty 4D array if the range is invalid or no data is found.

Return type:

np.ndarray

Raises:

ValueError – If no timesteps are available, or if the specified range is invalid (e.g., start_idx out of bounds, end_idx <= start_idx leading to empty range).

load_timestep(timestep_idx: int, **kwargs: Any) → ndarray[source]#

Load saturation data for a specific, zero-based timestep index.

Parameters:
  • timestep_idx (int) – The zero-based index of the timestep to load from the list of available timesteps discovered during initialization.

  • **kwargs (Any) – Additional keyword arguments (not used by this method).

Returns:

A 3D NumPy array of saturation values (nz, ny, nx).

Return type:

np.ndarray

Raises:

ValueError – If no timesteps are available or if timestep_idx is out of range.

PyHydroGeophysX.model_output.parflow_output.read_pfb(filename: str) → ndarray[source]#

Read a ParFlow Binary (PFB) file.

Attempts to use the parflow package if available, otherwise falls back to the standalone pure-Python reader.

Parameters:

filename – Path to the .pfb file

Returns:

3D array of shape (nz, ny, nx) containing the data

Return type:

np.ndarray

PyHydroGeophysX.model_output.parflow_output.read_pfb_standalone(filename: str) → ndarray[source]#

Read a ParFlow Binary (PFB) file without requiring the parflow package.

This is a pure Python/NumPy implementation that parses the PFB file format directly. PFB files store 3D data in a hierarchical subgrid structure.

Parameters:

filename – Path to the .pfb file

Returns:

3D array of shape (nz, ny, nx) containing the data

Return type:

np.ndarray

Raises:
  • FileNotFoundError – If the file does not exist

  • ValueError – If the file format is invalid

PyHydroGeophysX.model_output.water_content module#

Module for processing MODFLOW model outputs.

class PyHydroGeophysX.model_output.water_content.MODFLOWPorosity(model_directory: str, model_name: str)[source]#

Bases: HydroModelOutput

Legacy estimate of porosity from MODFLOW specific yield.

STO/UPW/LPF specific yield is drainable storage, not total porosity. This compatibility reader retains that proxy with a RuntimeWarning. Specific storage (inverse length) is never used as a porosity. Use an independently supplied porosity for quantitative petrophysical conversion. See the MODFLOW 6 GWF-STO and GWT-MST input guides.

get_timestep_info() → List[Tuple][source]#

Get information about each timestep in the model. Returns a minimal placeholder since porosity doesn’t vary with time.

Returns:

List with single dummy timestep info

load_porosity() → ndarray[source]#

Estimate porosity using the legacy specific-yield proxy, with a warning.

Supports MODFLOW 6 and earlier versions. A missing storage package keeps the historical uniform 0.3 fallback, also with a RuntimeWarning. Specific storage is not a porosity and is never returned here.

Returns:

3D array of porosity values (nlay, nrow, ncol)

load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs) → ndarray[source]#

Load porosity for a range of timesteps. Since porosity is typically constant, this returns a stack of identical arrays.

Parameters:
  • start_idx – Starting timestep index (unused)

  • end_idx – Ending timestep index (unused)

Returns:

4D array of porosity values (nt, nlay, nrow, ncol) where all timesteps are identical

load_timestep(timestep_idx: int, **kwargs) → ndarray[source]#

Load porosity for a specific timestep. Note: For MODFLOW, porosity is typically constant over time, so this returns the same array regardless of timestep.

Parameters:

timestep_idx – Index of the timestep (unused)

Returns:

3D array of porosity values

class PyHydroGeophysX.model_output.water_content.MODFLOWWaterContent(model_directory: str, idomain: ndarray)[source]#

Bases: HydroModelOutput

Class for processing water content data from MODFLOW simulations.

get_timestep_info() → List[Tuple[int, int, float, float]][source]#

Get information about each timestep in the WaterContent file.

Returns:

List of tuples (kstp, kper, pertim, totim) for each timestep

load_time_range(start_idx: int = 0, end_idx: int | None = None, nlay: int = 3) → ndarray[source]#

Load water content for a range of timesteps.

Parameters:
  • start_idx – Starting timestep index (default: 0)

  • end_idx – Ending timestep index (exclusive, default: None loads all)

  • nlay – Number of layers in the model (default: 3). Each record’s header states how many values it holds; a count other than nlay x the UZF cells raises ValueError instead of being read across the record boundary.

Returns:

Water content array with shape (timesteps, nlay, nrows, ncols)

load_timestep(timestep_idx: int, nlay: int = 3) → ndarray[source]#

Load water content for a specific timestep.

Parameters:
  • timestep_idx – Index of the timestep to load

  • nlay – Number of layers in the model

Returns:

Water content array with shape (nlay, nrows, ncols)

property sim_ws: str#

The simulation folder under its 0.3.0 name; use model_directory.

PyHydroGeophysX.model_output.water_content.binaryread(file: Any, vartype: type | List[Tuple[str, str]], shape: Tuple[int, ...] = (1,), charlen: int = 16) → bytes | ndarray | void[source]#

Read one item from an open MODFLOW binary output file.

Parameters:
  • file – Open file object in binary read mode (file_obj before 0.5.0). Any object with read works, an io.BytesIO included.

  • vartype – str, a NumPy dtype, or a structured dtype given as a list of (name, format) pairs.

  • shape – Output shape for a plain dtype (default: (1,)).

  • charlen – Number of bytes read for str (default: 16).

Returns:

bytes for str; for a structured dtype, the one record read, a numpy.void whose fields are read by name; otherwise an array of shape. These are the types 0.3.0 returned.

Raises:

EOFError – When the file ends before the whole item is read.

Examples

>>> import io
>>> header = [("kstp", "<i4"), ("kper", "<i4")]
>>> record = binaryread(io.BytesIO(np.array([(3, 7)], dtype=header).tobytes()), header)
>>> int(record["kstp"]), int(record["kper"])
(3, 7)
>>> binaryread(io.BytesIO(np.arange(4.0).tobytes()), np.float64, shape=(2, 2)).tolist()
[[0.0, 1.0], [2.0, 3.0]]

Module contents#

Module for processing model outputs from various hydrological models.

class PyHydroGeophysX.model_output.ATSOutput(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: HDF5ModelOutput

ATS subsurface scalar output; returns one-dimensional cell arrays.

filename is relative to model_directory or absolute. When omitted, exactly one of the standard subsurface filenames must exist. time_unit overrides missing file metadata; otherwise an unknown unit remains None.

base_aliases = {'porosity': ('porosity', 'porosity.cell.0'), 'pressure': ('pressure', 'pressure.cell.0'), 'saturation': ('saturation_liquid', 'saturation_liquid.cell.0'), 'temperature': ('temperature', 'temperature.cell.0')}#
property mesh_filename#

The mesh file ATS writes beside the data file (*_mesh.h5).

output_cell_centers(timestep_idx=0, *, axes=None)[source]#

Element centres from ATS’s mesh file, in the cell order of the data.

The mesh of this timestep’s cycle is used when ATS wrote one (a deforming mesh), else the latest mesh written before it, else the only one (a fixed mesh).

read_field(variable, timestep_idx)[source]#

Read a canonical field or an exact HDF5 variable name.

class PyHydroGeophysX.model_output.ATSPorosity(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: ATSOutput

Read porosity with its own available cycles.

variable = 'porosity'#
class PyHydroGeophysX.model_output.ATSSaturation(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: ATSOutput

Read liquid saturation in native ATS cell order.

variable = 'saturation'#
class PyHydroGeophysX.model_output.ATSWaterContent(model_directory, filename=None, *, variable_map=None, cell_centers=None, time_unit=None)[source]#

Bases: ATSOutput

Read volumetric liquid water content calculated as porosity * saturation.

variable = 'water_content'#
class PyHydroGeophysX.model_output.HydroModelOutput(model_directory: str)[source]#

Bases: ABC

Base class for all hydrological model outputs.

calculate_saturation(water_content: ndarray, porosity: float | ndarray) → ndarray[source]#

Calculate saturation from water content and porosity.

Parameters:
  • water_content – Water content array

  • porosity – Porosity value(s)

Returns:

Saturation array

abstractmethod get_timestep_info() → List[Tuple][source]#

Get information about each timestep.

Returns:

List of timestep information tuples

abstractmethod load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs) → ndarray[source]#

Load data for a range of timesteps.

Parameters:
  • start_idx – Starting timestep index

  • end_idx – Ending timestep index (exclusive)

  • **kwargs – Additional parameters specific to the model type

Returns:

Data array for the specified timestep range

abstractmethod load_timestep(timestep_idx: int, **kwargs) → ndarray[source]#

Load data for a specific timestep.

Parameters:
  • timestep_idx – Index of the timestep to load

  • **kwargs – Additional parameters specific to the model type

Returns:

Data array for the specified timestep

class PyHydroGeophysX.model_output.MODFLOWPorosity(model_directory: str, model_name: str)[source]#

Bases: HydroModelOutput

Legacy estimate of porosity from MODFLOW specific yield.

STO/UPW/LPF specific yield is drainable storage, not total porosity. This compatibility reader retains that proxy with a RuntimeWarning. Specific storage (inverse length) is never used as a porosity. Use an independently supplied porosity for quantitative petrophysical conversion. See the MODFLOW 6 GWF-STO and GWT-MST input guides.

get_timestep_info() → List[Tuple][source]#

Get information about each timestep in the model. Returns a minimal placeholder since porosity doesn’t vary with time.

Returns:

List with single dummy timestep info

load_porosity() → ndarray[source]#

Estimate porosity using the legacy specific-yield proxy, with a warning.

Supports MODFLOW 6 and earlier versions. A missing storage package keeps the historical uniform 0.3 fallback, also with a RuntimeWarning. Specific storage is not a porosity and is never returned here.

Returns:

3D array of porosity values (nlay, nrow, ncol)

load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs) → ndarray[source]#

Load porosity for a range of timesteps. Since porosity is typically constant, this returns a stack of identical arrays.

Parameters:
  • start_idx – Starting timestep index (unused)

  • end_idx – Ending timestep index (unused)

Returns:

4D array of porosity values (nt, nlay, nrow, ncol) where all timesteps are identical

load_timestep(timestep_idx: int, **kwargs) → ndarray[source]#

Load porosity for a specific timestep. Note: For MODFLOW, porosity is typically constant over time, so this returns the same array regardless of timestep.

Parameters:

timestep_idx – Index of the timestep (unused)

Returns:

3D array of porosity values

class PyHydroGeophysX.model_output.MODFLOWWaterContent(model_directory: str, idomain: ndarray)[source]#

Bases: HydroModelOutput

Class for processing water content data from MODFLOW simulations.

get_timestep_info() → List[Tuple[int, int, float, float]][source]#

Get information about each timestep in the WaterContent file.

Returns:

List of tuples (kstp, kper, pertim, totim) for each timestep

load_time_range(start_idx: int = 0, end_idx: int | None = None, nlay: int = 3) → ndarray[source]#

Load water content for a range of timesteps.

Parameters:
  • start_idx – Starting timestep index (default: 0)

  • end_idx – Ending timestep index (exclusive, default: None loads all)

  • nlay – Number of layers in the model (default: 3). Each record’s header states how many values it holds; a count other than nlay x the UZF cells raises ValueError instead of being read across the record boundary.

Returns:

Water content array with shape (timesteps, nlay, nrows, ncols)

load_timestep(timestep_idx: int, nlay: int = 3) → ndarray[source]#

Load water content for a specific timestep.

Parameters:
  • timestep_idx – Index of the timestep to load

  • nlay – Number of layers in the model

Returns:

Water content array with shape (nlay, nrows, ncols)

property sim_ws: str#

The simulation folder under its 0.3.0 name; use model_directory.

class PyHydroGeophysX.model_output.PFLOTRANOutput(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: HDF5ModelOutput

Read single-file or multiple-file snapshots in numerical time order.

Select a run using run_name (matches run_name.h5 and the numbered files run_name-NNN.h5 of PFLOTRAN’s multiple-file output, and no other run’s files), or provide filename explicitly. Unstructured arrays and structured arrays retain their HDF5 shape and axes - (nx, ny, nz) for a structured grid, as PFLOTRAN writes it - and output_cell_centers() gives the cell centres in their C-order flattening. Mixed time units and duplicate snapshot times are rejected to avoid silently joining unrelated runs or restarts.

aliases = {'porosity': ('Porosity', 'Porosity [ ]', 'Porosity [-]'), 'pressure': ('Liquid_Pressure', 'Liquid_Pressure [Pa]'), 'saturation': ('Liquid_Saturation', 'Liquid_Saturation [ ]', 'Liquid_Saturation [-]'), 'temperature': ('Temperature', 'Temperature [C]')}#
output_cell_centers(timestep_idx=0, *, axes=None)[source]#

Cell centres from the run’s own grid, in the C-order flattening of load_timestep(timestep_idx).

A structured grid’s Coordinates group holds the cell edges along x, y and z, and its fields are (nx, ny, nz); an unstructured grid’s Domain group holds the vertices and the XDMF cell connectivity. The snapshot’s own file is looked in first, then the run’s other files.

read_field(variable, timestep_idx)[source]#

Read a canonical field or an exact HDF5 variable name.

class PyHydroGeophysX.model_output.PFLOTRANPorosity(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: PFLOTRANOutput

Read snapshot porosity.

variable = 'porosity'#
class PyHydroGeophysX.model_output.PFLOTRANSaturation(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: PFLOTRANOutput

Read liquid saturation without changing HDF5 axis order.

variable = 'saturation'#
class PyHydroGeophysX.model_output.PFLOTRANWaterContent(model_directory, run_name=None, *, filename=None, variable_map=None, cell_centers=None)[source]#

Bases: PFLOTRANOutput

Read volumetric liquid water content calculated as porosity * saturation.

variable = 'water_content'#
class PyHydroGeophysX.model_output.ParflowPorosity(model_directory: str, run_name: str)[source]#

Bases: ParflowOutput

Processes porosity data from ParFlow simulations. Porosity in ParFlow is typically static (time-invariant) and stored in a single PFB file (e.g., <run_name>.out.porosity.pfb or similar).

get_timestep_info() → List[Tuple[int, float]][source]#

Returns timestep information, typically based on other ParFlow outputs (like saturation) as porosity itself is static.

Returns:

A list of (timestep_number, time_value) tuples,

derived from self.available_timesteps.

Return type:

List[Tuple[int, float]]

load_mask() → ndarray[source]#

Load the domain mask data from a ParFlow model. The mask file (.out.mask.pfb) indicates active (1) and inactive (0) cells.

Returns:

A 3D NumPy array representing the domain mask (nz, ny, nx).

Values are typically 0 or 1.

Return type:

np.ndarray

Raises:
  • FileNotFoundError – If no standard mask PFB file can be found.

  • ValueError – If there’s an error reading or processing the PFB file.

load_porosity() → ndarray[source]#

Load the static porosity data from the ParFlow model.

It searches for common ParFlow porosity filename patterns within the model directory.

Returns:

A 3D NumPy array of porosity values (nz, ny, nx).

Return type:

np.ndarray

Raises:
  • FileNotFoundError – If no standard porosity PFB file can be found.

  • ValueError – If there’s an error reading or processing the PFB file.

load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs: Any) → ndarray[source]#

Load porosity data for a conceptual range of timesteps. Since porosity is time-invariant, this method returns a 4D array where the static 3D porosity data is repeated along the time axis.

The number of repetitions along the time axis (nt) is determined by the length of self.available_timesteps (discovered from saturation/pressure files) if end_idx is None, or by min(end_idx - start_idx, len(available_timesteps)). A minimum of 1 repetition is ensured if any timesteps are notionally available.

Parameters:
  • start_idx (int, optional) – Starting timestep index (used to determine nt). Defaults to 0.

  • end_idx (Optional[int], optional) – Ending timestep index (exclusive, used for nt). Defaults to None (use all available timesteps).

  • **kwargs (Any) – Additional keyword arguments (not used).

Returns:

A 4D NumPy array of porosity values (nt, nz, ny, nx).

All slices along the time dimension are identical.

Return type:

np.ndarray

load_timestep(timestep_idx: int, **kwargs: Any) → ndarray[source]#

Load porosity data. For ParFlow, porosity is typically time-invariant. This method returns the static porosity array, ignoring timestep_idx.

Parameters:
  • timestep_idx (int) – Index of the timestep (ignored, as porosity is static).

  • **kwargs (Any) – Additional keyword arguments (not used).

Returns:

A 3D NumPy array of porosity values (nz, ny, nx).

Return type:

np.ndarray

class PyHydroGeophysX.model_output.ParflowSaturation(model_directory: str, run_name: str)[source]#

Bases: ParflowOutput

Processes saturation data from ParFlow simulations (.out.satur.*.pfb files).

get_timestep_info() → List[Tuple[int, float]][source]#

Provides information about available ParFlow timesteps.

For ParFlow, the timestep number from the filename often directly corresponds to the simulation time (e.g., if output is every 1 hour, timestep 24 is 24 hours). This method returns a list of tuples: (timestep_number, simulation_time). Currently, simulation_time is simply cast from timestep_number. More accurate time mapping would require parsing ParFlow timing files if complex.

Returns:

A list where each tuple is (timestep_number, time_value).

Time_value is float representation of timestep_number.

Return type:

List[Tuple[int, float]]

load_time_range(start_idx: int = 0, end_idx: int | None = None, **kwargs: Any) → ndarray[source]#

Load saturation data for a specified range of zero-based timestep indices.

Parameters:
  • start_idx (int, optional) – Starting zero-based timestep index. Defaults to 0.

  • end_idx (Optional[int], optional) – Ending zero-based timestep index (exclusive). If None, loads up to the last available timestep. Defaults to None.

  • **kwargs (Any) – Additional keyword arguments (not used).

Returns:

A 4D NumPy array of saturation values (num_timesteps, nz, ny, nx).

Returns an empty 4D array if the range is invalid or no data is found.

Return type:

np.ndarray

Raises:

ValueError – If no timesteps are available, or if the specified range is invalid (e.g., start_idx out of bounds, end_idx <= start_idx leading to empty range).

load_timestep(timestep_idx: int, **kwargs: Any) → ndarray[source]#

Load saturation data for a specific, zero-based timestep index.

Parameters:
  • timestep_idx (int) – The zero-based index of the timestep to load from the list of available timesteps discovered during initialization.

  • **kwargs (Any) – Additional keyword arguments (not used by this method).

Returns:

A 3D NumPy array of saturation values (nz, ny, nx).

Return type:

np.ndarray

Raises:

ValueError – If no timesteps are available or if timestep_idx is out of range.

PyHydroGeophysX.model_output.binaryread(file: Any, vartype: type | List[Tuple[str, str]], shape: Tuple[int, ...] = (1,), charlen: int = 16) → bytes | ndarray | void[source]#

Read one item from an open MODFLOW binary output file.

Parameters:
  • file – Open file object in binary read mode (file_obj before 0.5.0). Any object with read works, an io.BytesIO included.

  • vartype – str, a NumPy dtype, or a structured dtype given as a list of (name, format) pairs.

  • shape – Output shape for a plain dtype (default: (1,)).

  • charlen – Number of bytes read for str (default: 16).

Returns:

bytes for str; for a structured dtype, the one record read, a numpy.void whose fields are read by name; otherwise an array of shape. These are the types 0.3.0 returned.

Raises:

EOFError – When the file ends before the whole item is read.

Examples

>>> import io
>>> header = [("kstp", "<i4"), ("kper", "<i4")]
>>> record = binaryread(io.BytesIO(np.array([(3, 7)], dtype=header).tobytes()), header)
>>> int(record["kstp"]), int(record["kper"])
(3, 7)
>>> binaryread(io.BytesIO(np.arange(4.0).tobytes()), np.float64, shape=(2, 2)).tolist()
[[0.0, 1.0], [2.0, 3.0]]