I/O Modules#

The I/O modules provide functionality for reading and writing IWFM model files in various formats.

Core I/O#

ASCII Module#

The ASCII module provides readers and writers for IWFM’s text-based file formats.

ASCII file I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM ASCII input files including node coordinates, element definitions, and stratigraphy data.

pyiwfm.io.ascii.read_nodes(filepath)[source]#

Read node coordinates from an IWFM node file.

Expected format (C2VSimFG format):

NNODES # Number of nodes FACT # Conversion factor (optional) ID X Y (one line per node)

Parameters:

filepath (Path | str) – Path to the node file

Returns:

Dictionary mapping node ID to Node object

Raises:

FileFormatError – If file format is invalid

Return type:

dict[int, Node]

pyiwfm.io.ascii.read_elements(filepath)[source]#

Read element definitions from an IWFM element file.

Expected format:

NELEM / Number of elements NSUBREGION / Number of subregions ID NAME (one line per subregion) ID V1 V2 V3 V4 SR (one line per element, V4=0 for triangles)

Parameters:

filepath (Path | str) – Path to the element file

Returns:

Tuple of (elements dict, number of subregions, subregion names dict). The names dict maps subregion ID to name string (may be empty if no names could be parsed from the file).

Raises:

FileFormatError – If file format is invalid

Return type:

tuple[dict[int, Element], int, dict[int, str]]

pyiwfm.io.ascii.read_stratigraphy(filepath)[source]#

Read stratigraphy data from an IWFM stratigraphy file.

IWFM format (matches Fortran Class_Stratigraphy.f90):

NL # Number of layers FACT # Conversion factor ID GS W(1) W(2) … (one line per node)

Where W values are alternating aquitard/aquifer thicknesses:

W(1) = aquitard thickness layer 1 W(2) = aquifer thickness layer 1 W(3) = aquitard thickness layer 2 W(4) = aquifer thickness layer 2 etc.

Top and bottom elevations are computed from GS and cumulative thicknesses.

Parameters:

filepath (Path | str) – Path to the stratigraphy file

Returns:

Stratigraphy object

Raises:

FileFormatError – If file format is invalid

Return type:

Stratigraphy

pyiwfm.io.ascii.write_nodes(filepath, nodes, header=None)[source]#

Write node coordinates to an IWFM node file.

Parameters:
  • filepath (Path | str) – Path to output file

  • nodes (dict[int, Node]) – Dictionary mapping node ID to Node object

  • header (str | None) – Optional header comment (default generates one)

pyiwfm.io.ascii.write_elements(filepath, elements, n_subregions, header=None)[source]#

Write element definitions to an IWFM element file.

Parameters:
  • filepath (Path | str) – Path to output file

  • elements (dict[int, Element]) – Dictionary mapping element ID to Element object

  • n_subregions (int) – Number of subregions

  • header (str | None) – Optional header comment

pyiwfm.io.ascii.write_stratigraphy(filepath, stratigraphy, header=None)[source]#

Write stratigraphy data to an IWFM stratigraphy file.

IWFM format uses THICKNESSES, not elevations:

NL / Number of layers FACT / Conversion factor ID GS W(1) W(2) … (one line per node)

Where W values are alternating aquitard/aquifer thicknesses:

W(1) = aquitard thickness layer 1 (gs - top_layer_1) W(2) = aquifer thickness layer 1 (top_layer_1 - bottom_layer_1) W(3) = aquitard thickness layer 2 (bottom_layer_1 - top_layer_2) W(4) = aquifer thickness layer 2 (top_layer_2 - bottom_layer_2) etc.

Parameters:
  • filepath (Path | str) – Path to output file

  • stratigraphy (Stratigraphy) – Stratigraphy object

  • header (str | None) – Optional header comment

Binary Module#

The binary module provides readers and writers for IWFM’s Fortran binary formats.

Binary file I/O handlers for IWFM model files.

This module provides classes for reading and writing IWFM binary files:

  • FortranBinaryReader / FortranBinaryWriter: Fortran unformatted sequential-access files (record markers around each write).

  • StreamAccessBinaryReader: Raw byte-stream files matching IWFM’s ACCESS='STREAM' (no record markers; caller supplies counts).

pyiwfm.io.binary.read_fortran_record(f, endian='<')[source]#

Read a single Fortran unformatted record from an open file.

Fortran unformatted sequential files bracket each record with 4-byte integer markers that encode the record length in bytes.

Parameters:
  • f (BinaryIO) – An open binary file object.

  • endian (str) – Byte order ('<' = little-endian, '>' = big-endian).

Returns:

The raw record data (without markers).

Return type:

bytes

class pyiwfm.io.binary.FortranBinaryReader(filepath, endian='<')[source]#

Bases: object

Reader for Fortran unformatted binary files.

Handles the record markers that Fortran writes before and after each record.

__init__(filepath, endian='<')[source]#

Initialize the reader.

Parameters:
  • filepath (Path | str) – Path to the binary file

  • endian (str) – Byte order (‘<’ = little-endian, ‘>’ = big-endian)

read_record()[source]#

Read a single Fortran record.

Returns:

Record data as bytes

Return type:

bytes

read_int()[source]#

Read a single integer record.

read_int_array()[source]#

Read an integer array record.

read_float()[source]#

Read a single float record (real*4).

read_float_array()[source]#

Read a float array record (real*4).

read_double()[source]#

Read a single double record (real*8).

read_double_array()[source]#

Read a double array record (real*8).

read_string(length=None)[source]#

Read a string record.

skip_records(n)[source]#

Skip n records without reading their data.

Parameters:

n (int) – Number of records to skip

get_position()[source]#

Get current file position.

Returns:

Current byte offset in the file

Return type:

int

seek_to_position(pos)[source]#

Seek to a specific position in the file.

Parameters:

pos (int) – Byte offset to seek to

read_mixed_record(dtype_spec)[source]#

Read a record containing mixed data types.

Parameters:

dtype_spec (list[tuple[str, int]]) – List of (dtype, count) tuples specifying the record format. dtype can be: ‘i4’ (int32), ‘i8’ (int64), ‘f4’ (float32), ‘f8’ (float64), ‘S’ followed by length (e.g., ‘S30’ for 30-char string)

Returns:

Tuple of values read from the record

Return type:

tuple[Any, …]

Example

>>> reader.read_mixed_record([('i4', 1), ('f8', 3), ('S30', 1)])
(42, array([1.0, 2.0, 3.0]), 'SOME_STRING')
read_character_record(length)[source]#

Read a character record of specified length.

Unlike read_string, this reads exactly ‘length’ bytes from the record without assuming the entire record is the string.

Parameters:

length (int) – Number of characters to read

Returns:

The string value

Return type:

str

peek_record_size()[source]#

Peek at the next record size without advancing the file position.

Returns:

Size of the next record in bytes

Return type:

int

at_eof()[source]#

Check if we’re at the end of the file.

Returns:

True if at end of file, False otherwise

Return type:

bool

class pyiwfm.io.binary.FortranBinaryWriter(filepath, endian='<')[source]#

Bases: object

Writer for Fortran unformatted binary files.

Writes record markers before and after each record.

__init__(filepath, endian='<')[source]#

Initialize the writer.

Parameters:
  • filepath (Path | str) – Path to the output file

  • endian (str) – Byte order (‘<’ = little-endian, ‘>’ = big-endian)

write_record(data)[source]#

Write a single Fortran record with markers.

Parameters:

data (bytes) – Record data as bytes

write_int(value)[source]#

Write a single integer record.

write_int_array(arr)[source]#

Write an integer array record.

write_float(value)[source]#

Write a single float record (real*4).

write_float_array(arr)[source]#

Write a float array record (real*4).

write_double(value)[source]#

Write a single double record (real*8).

write_double_array(arr)[source]#

Write a double array record (real*8).

write_string(s, length=None)[source]#

Write a string record.

class pyiwfm.io.binary.StreamAccessBinaryReader(filepath, endian='<')[source]#

Bases: object

Reader for IWFM stream-access binary files (ACCESS='STREAM').

Unlike FortranBinaryReader, IWFM preprocessor binary output is written with ACCESS='STREAM' which produces raw bytes without the 4-byte record-length markers that Fortran sequential writes add. The caller must supply explicit counts for every array read.

__init__(filepath, endian='<')[source]#
read_int()[source]#

Read a single 4-byte integer.

read_double()[source]#

Read a single 8-byte float (REAL*8).

read_ints(n)[source]#

Read n consecutive 4-byte integers.

read_doubles(n)[source]#

Read n consecutive 8-byte floats.

read_logical()[source]#

Read a single Fortran LOGICAL (4-byte int, non-zero = True).

read_logicals(n)[source]#

Read n consecutive Fortran LOGICALs.

read_string(length)[source]#

Read a fixed-length ASCII string, right-stripped.

get_position()[source]#
at_eof()[source]#
pyiwfm.io.binary.write_binary_mesh(filepath, grid, endian='<')[source]#

Write mesh data to a binary file.

Parameters:
  • filepath (Path | str) – Path to the output file

  • grid (AppGrid) – AppGrid instance to write

  • endian (str) – Byte order

pyiwfm.io.binary.write_binary_stratigraphy(filepath, strat, endian='<')[source]#

Write stratigraphy data to a binary file.

Parameters:
  • filepath (Path | str) – Path to the output file

  • strat (Stratigraphy) – Stratigraphy instance to write

  • endian (str) – Byte order

HDF5 Module#

The HDF5 module provides readers and writers for HDF5-based storage.

HDF5 file I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM model data in HDF5 format, which provides efficient storage for large datasets and supports compression.

class pyiwfm.io.hdf5.HDF5ModelWriter(filepath, compression='gzip')[source]#

Bases: object

Writer for IWFM model data in HDF5 format.

The HDF5 file structure:
/mesh/

nodes/x, y, area, is_boundary elements/vertices, subregion, area subregions/id, name

/stratigraphy/

gs_elev, top_elev, bottom_elev, active_node

/timeseries/

{variable}/{location}/times, values

/metadata/

name, version, created, …

__init__(filepath, compression='gzip')[source]#

Initialize the writer.

Parameters:
  • filepath (Path | str) – Path to the output HDF5 file

  • compression (str | None) – Compression algorithm (‘gzip’, ‘lzf’, or None)

write_mesh(mesh)[source]#

Write mesh data to the HDF5 file.

write_stratigraphy(strat)[source]#

Write stratigraphy data to the HDF5 file.

write_timeseries(ts, variable)[source]#

Write a time series to the HDF5 file.

Parameters:
  • ts (TimeSeries) – TimeSeries to write

  • variable (str) – Variable name (e.g., ‘head’, ‘flow’)

write_metadata(metadata)[source]#

Write model metadata to the HDF5 file.

write_model(model)[source]#

Write a complete IWFMModel to the HDF5 file.

Parameters:

model (IWFMModel) – IWFMModel instance to write

class pyiwfm.io.hdf5.HDF5ModelReader(filepath)[source]#

Bases: object

Reader for IWFM model data from HDF5 format.

__init__(filepath)[source]#

Initialize the reader.

Parameters:

filepath (Path | str) – Path to the HDF5 file

read_mesh()[source]#

Read mesh data from the HDF5 file.

read_stratigraphy()[source]#

Read stratigraphy data from the HDF5 file.

read_timeseries(variable, location)[source]#

Read a time series from the HDF5 file.

Parameters:
  • variable (str) – Variable name

  • location (str) – Location identifier

Returns:

TimeSeries instance

Return type:

TimeSeries

list_timeseries()[source]#

List all available time series.

Returns:

Dictionary mapping variable names to lists of location names

Return type:

dict[str, list[str]]

read_metadata()[source]#

Read model metadata from the HDF5 file.

read_model(name=None)[source]#

Read a complete IWFMModel from the HDF5 file.

Parameters:

name (str | None) – Model name (optional, read from metadata if not provided)

Returns:

IWFMModel instance

Return type:

IWFMModel

pyiwfm.io.hdf5.write_model_hdf5(filepath, model, compression='gzip')[source]#

Write an IWFMModel to an HDF5 file.

Parameters:
  • filepath (Path | str) – Path to the output file

  • model (IWFMModel) – IWFMModel to write

  • compression (str | None) – Compression algorithm

pyiwfm.io.hdf5.read_model_hdf5(filepath, name=None)[source]#

Read an IWFMModel from an HDF5 file.

Parameters:
  • filepath (Path | str) – Path to the HDF5 file

  • name (str | None) – Model name (optional)

Returns:

IWFMModel instance

Return type:

IWFMModel

Base Classes#

Abstract base classes for readers and writers.

Base classes for IWFM file I/O.

This module provides abstract base classes for reading and writing IWFM model files in various formats, including support for comment preservation during round-trip operations.

class pyiwfm.io.base.FileInfo(path, format, version=None, description='', metadata=<factory>)[source]#

Bases: object

Information about an IWFM file.

path: Path#
format: str#
version: str | None = None#
description: str = ''#
metadata: dict[str, Any]#
__init__(path, format, version=None, description='', metadata=<factory>)#
class pyiwfm.io.base.BaseReader(filepath)[source]#

Bases: ABC

Abstract base class for IWFM file readers.

__init__(filepath)[source]#

Initialize the reader.

Parameters:

filepath (Path | str) – Path to the file to read

abstractmethod read()[source]#

Read the file and return the parsed data.

Returns:

Parsed data (type depends on subclass)

Return type:

Any

abstract property format: str#

Return the file format identifier.

class pyiwfm.io.base.BaseWriter(filepath)[source]#

Bases: ABC

Abstract base class for IWFM file writers.

__init__(filepath)[source]#

Initialize the writer.

Parameters:

filepath (Path | str) – Path to the output file

abstractmethod write(data)[source]#

Write data to the file.

Parameters:

data (Any) – Data to write (type depends on subclass)

abstract property format: str#

Return the file format identifier.

class pyiwfm.io.base.ModelReader(filepath)[source]#

Bases: BaseReader

Abstract base class for reading complete IWFM models.

abstractmethod read()[source]#

Read the model from file(s).

Returns:

Complete IWFMModel instance

Return type:

IWFMModel

abstractmethod read_mesh()[source]#

Read only the mesh from the model files.

Returns:

AppGrid instance

Return type:

AppGrid

abstractmethod read_stratigraphy()[source]#

Read only the stratigraphy from the model files.

Returns:

Stratigraphy instance

Return type:

Stratigraphy

class pyiwfm.io.base.ModelWriter(filepath)[source]#

Bases: BaseWriter

Abstract base class for writing complete IWFM models.

abstractmethod write(model)[source]#

Write the model to file(s).

Parameters:

model (IWFMModel) – IWFMModel instance to write

abstractmethod write_mesh(mesh)[source]#

Write only the mesh.

Parameters:

mesh (AppGrid) – AppGrid instance to write

abstractmethod write_stratigraphy(stratigraphy)[source]#

Write only the stratigraphy.

Parameters:

stratigraphy (Stratigraphy) – Stratigraphy instance to write

class pyiwfm.io.base.BinaryReader(filepath, endian='<')[source]#

Bases: BaseReader

Base class for reading IWFM binary files.

RECORD_MARKER_SIZE = 4#
__init__(filepath, endian='<')[source]#

Initialize the binary reader.

Parameters:
  • filepath (Path | str) – Path to the binary file

  • endian (str) – Byte order (‘<’ = little-endian, ‘>’ = big-endian)

property format: str#

Return the file format identifier.

class pyiwfm.io.base.BinaryWriter(filepath, endian='<')[source]#

Bases: BaseWriter

Base class for writing IWFM binary files.

RECORD_MARKER_SIZE = 4#
__init__(filepath, endian='<')[source]#

Initialize the binary writer.

Parameters:
  • filepath (Path | str) – Path to the output file

  • endian (str) – Byte order (‘<’ = little-endian, ‘>’ = big-endian)

property format: str#

Return the file format identifier.

class pyiwfm.io.base.CommentAwareReader(filepath, preserve_comments=True)[source]#

Bases: BaseReader

Base class for readers that preserve comments.

This class extends BaseReader to extract and preserve comments from IWFM input files during reading. The extracted comment metadata can be used later for round-trip preservation.

Example

>>> reader = MyCommentAwareReader("Preprocessor.in", preserve_comments=True)
>>> data = reader.read()
>>> metadata = reader.comment_metadata
>>> metadata.save_for_file("Preprocessor.in")
Variables:
  • preserve_comments – Whether to extract and store comments.

  • _comment_metadata – Extracted comment metadata (lazy-loaded).

__init__(filepath, preserve_comments=True)[source]#

Initialize the comment-aware reader.

Parameters:
  • filepath (Path | str) – Path to the file to read.

  • preserve_comments (bool) – If True, extract and store comments.

property comment_metadata: CommentMetadata | None#

Get extracted comment metadata.

Returns None if preserve_comments is False or if comments have not been extracted yet.

extract_comments()[source]#

Extract comments from the file.

This method can be called explicitly to extract comments without reading the full file content.

Returns:

CommentMetadata containing all extracted comments.

Return type:

CommentMetadata

class pyiwfm.io.base.CommentAwareWriter(filepath, comment_metadata=None, use_templates_for_missing=True)[source]#

Bases: BaseWriter

Base class for writers that can restore preserved comments.

This class extends BaseWriter to support injecting preserved comments into output files, enabling round-trip preservation of user comments.

Example

>>> # Load metadata from sidecar file
>>> metadata = CommentMetadata.load_for_file("Preprocessor.in")
>>> writer = MyCommentAwareWriter("output/Preprocessor.in", metadata)
>>> writer.write(data)
Variables:
  • comment_metadata – CommentMetadata to use for restoration.

  • use_templates_for_missing – If True, use template defaults when no preserved comments exist.

__init__(filepath, comment_metadata=None, use_templates_for_missing=True)[source]#

Initialize the comment-aware writer.

Parameters:
  • filepath (Path | str) – Path to the output file.

  • comment_metadata (CommentMetadata | None) – CommentMetadata with preserved comments.

  • use_templates_for_missing (bool) – If True, use default templates when no preserved comments exist.

has_preserved_comments()[source]#

Check if preserved comments are available.

get_comment_writer()[source]#

Get a CommentWriter configured with our metadata.

Returns:

CommentWriter instance for restoring comments.

Return type:

CommentWriter

save_comment_metadata()[source]#

Save comment metadata as a sidecar file.

Returns:

Path to saved sidecar file, or None if no metadata.

Return type:

Path | None

Writer Base#

Template-based writer base classes used by all component writers.

Enhanced base writer classes for IWFM file generation.

This module provides abstract base classes for writing IWFM files, supporting both text and DSS output formats, with optional comment preservation for round-trip operations.

class pyiwfm.io.writer_base.TemplateWriter(output_dir, template_engine=None, comment_metadata=None)[source]#

Bases: ABC

Base class for template-based IWFM file writers.

Uses Jinja2 templates for file headers and structure, with numpy for efficient large data array output.

Supports optional comment preservation via CommentMetadata. When comment_metadata is provided, preserved comments from the original file will be injected into the output.

__init__(output_dir, template_engine=None, comment_metadata=None)[source]#

Initialize the template writer.

Parameters:
  • output_dir (Path | str) – Directory for output files

  • template_engine (TemplateEngine | None) – Optional TemplateEngine instance

  • comment_metadata (CommentMetadata | None) – Optional CommentMetadata for comment preservation

property comment_writer: CommentWriter#

Get or create the comment writer.

Returns:

CommentWriter instance configured with our comment_metadata.

has_preserved_comments()[source]#

Check if preserved comments are available.

render_header(template_name, **context)[source]#

Render a template header.

Parameters:
  • template_name (str) – Name of the template file

  • **context (Any) – Template context variables

Returns:

Rendered header string

Return type:

str

render_header_with_comments(template_name, section_name=None, **context)[source]#

Render a template header with preserved comments.

If comment_metadata is available and contains preserved comments, those comments are used instead of or in addition to the template.

Parameters:
  • template_name (str) – Name of the template file (fallback)

  • section_name (str | None) – Section name for looking up comments

  • **context (Any) – Template context variables

Returns:

Header string with preserved or template-based comments

Return type:

str

render_string(template_str, **context)[source]#

Render a template from a string.

Parameters:
  • template_str (str) – Template string

  • **context (Any) – Template context variables

Returns:

Rendered string

Return type:

str

write_data_block(file, data, fmt='%14.6f', header_comment=None)[source]#

Write a numpy array as a formatted data block.

Parameters:
  • file (TextIO) – Open file object

  • data (ndarray[tuple[Any, ...], dtype[float64]]) – Data array (1D or 2D)

  • fmt (str | Sequence[str]) – Format string(s) for values

  • header_comment (str | None) – Optional comment line before data

write_indexed_data(file, ids, data, id_fmt='%5d', data_fmt='%14.6f')[source]#

Write indexed data (ID column + data columns).

Parameters:
abstractmethod write(data)[source]#

Write data to file(s).

Parameters:

data (Any) – Data to write (type depends on subclass)

abstract property format: str#

Return the file format identifier.

class pyiwfm.io.writer_base.TimeSeriesSpec(name, dates, values, units='', location='', parameter='', interval='1DAY')[source]#

Bases: object

Specification for a time series to write.

name: str#
dates: Sequence[datetime] | ndarray[tuple[Any, ...], dtype[datetime64]]#
values: Sequence[float] | ndarray[tuple[Any, ...], dtype[float64]]#
units: str = ''#
location: str = ''#
parameter: str = ''#
interval: str = '1DAY'#
__init__(name, dates, values, units='', location='', parameter='', interval='1DAY')#
class pyiwfm.io.writer_base.TimeSeriesWriter(output_config, output_dir)[source]#

Bases: object

Writer for time series data with text/DSS format support.

Can write time series to: - ASCII text files (one or more columns) - HEC-DSS files (requires heclib)

__init__(output_config, output_dir)[source]#

Initialize the time series writer.

Parameters:
write_timeseries(ts_spec, text_file=None)[source]#

Write a single time series.

Parameters:
  • ts_spec (TimeSeriesSpec) – Time series specification

  • text_file (str | Path | None) – Text file name (required if format includes TEXT)

write_timeseries_table(dates, columns, text_file, header_lines=None)[source]#

Write multiple time series columns to a single text file.

Parameters:
close()[source]#

Close any open DSS files.

class pyiwfm.io.writer_base.ComponentWriter(output_dir, ts_config=None, template_engine=None, comment_metadata=None)[source]#

Bases: TemplateWriter

Base class for IWFM component writers (GW, Streams, Lakes, etc.).

Provides common functionality for writing component input files with support for both text and DSS time series formats.

Supports optional comment preservation via CommentMetadata.

__init__(output_dir, ts_config=None, template_engine=None, comment_metadata=None)[source]#

Initialize the component writer.

Parameters:
  • output_dir (Path | str) – Directory for output files

  • ts_config (TimeSeriesOutputConfig | None) – Time series output configuration

  • template_engine (TemplateEngine | None) – Optional TemplateEngine instance

  • comment_metadata (CommentMetadata | None) – Optional CommentMetadata for comment preservation

property ts_writer: TimeSeriesWriter#

Get or create the time series writer.

write_component_header(file, component_name, version='', description='')[source]#

Write a standard component file header.

Parameters:
  • file (TextIO) – Open file object

  • component_name (str) – Name of the component

  • version (str) – Optional version string

  • description (str) – Optional description

write_file_reference(file, ref_path, description='')[source]#

Write a file path reference.

Parameters:
  • file (TextIO) – Open file object

  • ref_path (Path | str | None) – Path to referenced file (None for blank)

  • description (str) – Description of the file

write_value_line(file, value, description='', width=20)[source]#

Write a value with optional description.

Parameters:
  • file (TextIO) – Open file object

  • value (Any) – Value to write

  • description (str) – Description of the value

  • width (int) – Width for value field

property format: str#

Return the file format identifier.

class pyiwfm.io.writer_base.IWFMModelWriter(model, output_dir, ts_format=OutputFormat.TEXT, template_engine=None, comment_metadata=None)[source]#

Bases: ABC

Abstract base class for writing complete IWFM models.

Coordinates all component writers to produce a complete, valid IWFM input file set.

Supports optional comment preservation via FileCommentMetadata.

__init__(model, output_dir, ts_format=OutputFormat.TEXT, template_engine=None, comment_metadata=None)[source]#

Initialize the model writer.

Parameters:
  • model (IWFMModel) – IWFMModel instance to write

  • output_dir (Path | str) – Directory for output files

  • ts_format (OutputFormat) – Output format for time series (TEXT, DSS, or BOTH)

  • template_engine (TemplateEngine | None) – Optional TemplateEngine instance

  • comment_metadata (CommentMetadata | None) – Optional CommentMetadata for comment preservation

abstractmethod write_preprocessor()[source]#

Write preprocessor files.

Returns:

Dict mapping file type to output path

Return type:

dict[str, Path]

abstractmethod write_simulation()[source]#

Write simulation files.

Returns:

Dict mapping file type to output path

Return type:

dict[str, Path]

write_all()[source]#

Write complete model file set.

Returns:

Dict mapping file type to output path

Return type:

dict[str, Path]

Writer Configuration#

Configuration dataclasses for the model writer system.

File configuration classes for IWFM model I/O.

These dataclasses define the file structure and naming conventions for IWFM model input/output, supporting both text and DSS formats.

class pyiwfm.io.config.OutputFormat(*values)[source]#

Bases: Enum

Output format for time series data.

TEXT = 'text'#
DSS = 'dss'#
BOTH = 'both'#
class pyiwfm.io.config.TimeSeriesOutputConfig(format=OutputFormat.TEXT, dss_file=None, dss_a_part='', dss_f_part='PYIWFM')[source]#

Bases: object

Configuration for time series output format.

format: OutputFormat = 'text'#
dss_file: str | None = None#
dss_a_part: str = ''#
dss_f_part: str = 'PYIWFM'#
get_dss_path(output_dir)[source]#

Get full path to DSS file.

__init__(format=OutputFormat.TEXT, dss_file=None, dss_a_part='', dss_f_part='PYIWFM')#
class pyiwfm.io.config.PreProcessorFileConfig(output_dir, main_file='Preprocessor.in', node_file='Nodes.dat', element_file='Elements.dat', stratigraphy_file='Stratigraphy.dat', stream_config_file='StreamConfig.dat', lake_config_file='LakeConfig.dat', binary_output_file='Preprocessor.bin', stream_version='5.0', lake_version='5.0')[source]#

Bases: object

Configuration for PreProcessor input/output files.

Defines file names for all preprocessor components.

output_dir: Path#
main_file: str = 'Preprocessor.in'#
node_file: str = 'Nodes.dat'#
element_file: str = 'Elements.dat'#
stratigraphy_file: str = 'Stratigraphy.dat'#
stream_config_file: str = 'StreamConfig.dat'#
lake_config_file: str = 'LakeConfig.dat'#
binary_output_file: str = 'Preprocessor.bin'#
stream_version: str = '5.0'#
lake_version: str = '5.0'#
property main_path: Path#
property node_path: Path#
property element_path: Path#
property stratigraphy_path: Path#
property stream_config_path: Path#
property lake_config_path: Path#
__init__(output_dir, main_file='Preprocessor.in', node_file='Nodes.dat', element_file='Elements.dat', stratigraphy_file='Stratigraphy.dat', stream_config_file='StreamConfig.dat', lake_config_file='LakeConfig.dat', binary_output_file='Preprocessor.bin', stream_version='5.0', lake_version='5.0')#
class pyiwfm.io.config.GWFileConfig(output_dir, main_file='Groundwater.dat', aquifer_params_file='AquiferParameters.dat', bc_main_file='BoundaryConditions.dat', specified_head_bc_file='SpecifiedHeadBC.dat', specified_flow_bc_file='SpecifiedFlowBC.dat', general_head_bc_file='GeneralHeadBC.dat', constrained_ghbc_file='ConstrainedGHBC.dat', pumping_main_file='Pumping.dat', well_spec_file='WellSpecs.dat', element_pumping_file='ElementPumping.dat', pumping_rates_file='PumpingRates.dat', tile_drain_file='TileDrains.dat', subsidence_file='Subsidence.dat', ts_config=<factory>)[source]#

Bases: object

Configuration for groundwater component files.

Supports both text and DSS output formats for time series.

output_dir: Path#
main_file: str = 'Groundwater.dat'#
aquifer_params_file: str = 'AquiferParameters.dat'#
bc_main_file: str = 'BoundaryConditions.dat'#
specified_head_bc_file: str = 'SpecifiedHeadBC.dat'#
specified_flow_bc_file: str = 'SpecifiedFlowBC.dat'#
general_head_bc_file: str = 'GeneralHeadBC.dat'#
constrained_ghbc_file: str = 'ConstrainedGHBC.dat'#
pumping_main_file: str = 'Pumping.dat'#
well_spec_file: str = 'WellSpecs.dat'#
element_pumping_file: str = 'ElementPumping.dat'#
pumping_rates_file: str = 'PumpingRates.dat'#
tile_drain_file: str = 'TileDrains.dat'#
subsidence_file: str = 'Subsidence.dat'#
ts_config: TimeSeriesOutputConfig#
get_path(filename)[source]#
property main_path: Path#
__init__(output_dir, main_file='Groundwater.dat', aquifer_params_file='AquiferParameters.dat', bc_main_file='BoundaryConditions.dat', specified_head_bc_file='SpecifiedHeadBC.dat', specified_flow_bc_file='SpecifiedFlowBC.dat', general_head_bc_file='GeneralHeadBC.dat', constrained_ghbc_file='ConstrainedGHBC.dat', pumping_main_file='Pumping.dat', well_spec_file='WellSpecs.dat', element_pumping_file='ElementPumping.dat', pumping_rates_file='PumpingRates.dat', tile_drain_file='TileDrains.dat', subsidence_file='Subsidence.dat', ts_config=<factory>)#
class pyiwfm.io.config.StreamFileConfig(output_dir, version='5.0', main_file='Streams.dat', inflow_file='StreamInflows.dat', diversion_spec_file='DiversionSpecs.dat', diversion_data_file='DiversionData.dat', bypass_spec_file='BypassSpecs.dat', bypass_data_file='BypassData.dat', surface_area_file='StreamSurfaceArea.dat', ts_config=<factory>)[source]#

Bases: object

Configuration for stream component files.

output_dir: Path#
version: str = '5.0'#
main_file: str = 'Streams.dat'#
inflow_file: str = 'StreamInflows.dat'#
diversion_spec_file: str = 'DiversionSpecs.dat'#
diversion_data_file: str = 'DiversionData.dat'#
bypass_spec_file: str = 'BypassSpecs.dat'#
bypass_data_file: str = 'BypassData.dat'#
surface_area_file: str = 'StreamSurfaceArea.dat'#
ts_config: TimeSeriesOutputConfig#
property main_path: Path#
__init__(output_dir, version='5.0', main_file='Streams.dat', inflow_file='StreamInflows.dat', diversion_spec_file='DiversionSpecs.dat', diversion_data_file='DiversionData.dat', bypass_spec_file='BypassSpecs.dat', bypass_data_file='BypassData.dat', surface_area_file='StreamSurfaceArea.dat', ts_config=<factory>)#
class pyiwfm.io.config.LakeFileConfig(output_dir, version='5.0', main_file='Lakes.dat', max_elevation_file='MaxLakeElevations.dat', lake_elements_file='LakeElements.dat', lake_outflow_file='LakeOutflows.dat', ts_config=<factory>)[source]#

Bases: object

Configuration for lake component files.

output_dir: Path#
version: str = '5.0'#
main_file: str = 'Lakes.dat'#
max_elevation_file: str = 'MaxLakeElevations.dat'#
lake_elements_file: str = 'LakeElements.dat'#
lake_outflow_file: str = 'LakeOutflows.dat'#
ts_config: TimeSeriesOutputConfig#
property main_path: Path#
__init__(output_dir, version='5.0', main_file='Lakes.dat', max_elevation_file='MaxLakeElevations.dat', lake_elements_file='LakeElements.dat', lake_outflow_file='LakeOutflows.dat', ts_config=<factory>)#
class pyiwfm.io.config.RootZoneFileConfig(output_dir, version='5.0', main_file='RootZone.dat', ag_water_supply_file='AgWaterSupply.dat', moisture_source_file='MoistureSource.dat', irrigation_period_file='IrrigationPeriod.dat', return_flow_file='ReturnFlow.dat', reuse_fraction_file='ReuseFraction.dat', nonponded_main_file='NonPondedCrops.dat', nonponded_area_file='NonPondedArea.dat', irrigation_target_file='IrrigationTarget.dat', min_soil_moisture_file='MinSoilMoisture.dat', min_perc_factor_file='MinPercFactor.dat', root_depth_file='RootDepth.dat', ponded_main_file='PondedCrops.dat', ponded_area_file='PondedArea.dat', pond_depth_file='PondDepth.dat', pond_operation_file='PondOperation.dat', native_main_file='NativeRiparian.dat', native_area_file='NativeArea.dat', urban_main_file='Urban.dat', urban_area_file='UrbanArea.dat', urban_water_use_file='UrbanWaterUse.dat', urban_population_file='UrbanPopulation.dat', ts_config=<factory>)[source]#

Bases: object

Configuration for root zone component files.

output_dir: Path#
version: str = '5.0'#
main_file: str = 'RootZone.dat'#
ag_water_supply_file: str = 'AgWaterSupply.dat'#
moisture_source_file: str = 'MoistureSource.dat'#
irrigation_period_file: str = 'IrrigationPeriod.dat'#
return_flow_file: str = 'ReturnFlow.dat'#
reuse_fraction_file: str = 'ReuseFraction.dat'#
nonponded_main_file: str = 'NonPondedCrops.dat'#
nonponded_area_file: str = 'NonPondedArea.dat'#
irrigation_target_file: str = 'IrrigationTarget.dat'#
min_soil_moisture_file: str = 'MinSoilMoisture.dat'#
min_perc_factor_file: str = 'MinPercFactor.dat'#
root_depth_file: str = 'RootDepth.dat'#
ponded_main_file: str = 'PondedCrops.dat'#
ponded_area_file: str = 'PondedArea.dat'#
pond_depth_file: str = 'PondDepth.dat'#
pond_operation_file: str = 'PondOperation.dat'#
native_main_file: str = 'NativeRiparian.dat'#
native_area_file: str = 'NativeArea.dat'#
urban_main_file: str = 'Urban.dat'#
urban_area_file: str = 'UrbanArea.dat'#
urban_water_use_file: str = 'UrbanWaterUse.dat'#
urban_population_file: str = 'UrbanPopulation.dat'#
ts_config: TimeSeriesOutputConfig#
property main_path: Path#
__init__(output_dir, version='5.0', main_file='RootZone.dat', ag_water_supply_file='AgWaterSupply.dat', moisture_source_file='MoistureSource.dat', irrigation_period_file='IrrigationPeriod.dat', return_flow_file='ReturnFlow.dat', reuse_fraction_file='ReuseFraction.dat', nonponded_main_file='NonPondedCrops.dat', nonponded_area_file='NonPondedArea.dat', irrigation_target_file='IrrigationTarget.dat', min_soil_moisture_file='MinSoilMoisture.dat', min_perc_factor_file='MinPercFactor.dat', root_depth_file='RootDepth.dat', ponded_main_file='PondedCrops.dat', ponded_area_file='PondedArea.dat', pond_depth_file='PondDepth.dat', pond_operation_file='PondOperation.dat', native_main_file='NativeRiparian.dat', native_area_file='NativeArea.dat', urban_main_file='Urban.dat', urban_area_file='UrbanArea.dat', urban_water_use_file='UrbanWaterUse.dat', urban_population_file='UrbanPopulation.dat', ts_config=<factory>)#
class pyiwfm.io.config.SmallWatershedFileConfig(output_dir, version='4.1', main_file='SmallWatersheds.dat')[source]#

Bases: object

Configuration for small watershed component files.

output_dir: Path#
version: str = '4.1'#
main_file: str = 'SmallWatersheds.dat'#
property main_path: Path#
__init__(output_dir, version='4.1', main_file='SmallWatersheds.dat')#
class pyiwfm.io.config.UnsatZoneFileConfig(output_dir, main_file='UnsatZone.dat')[source]#

Bases: object

Configuration for unsaturated zone component files.

output_dir: Path#
main_file: str = 'UnsatZone.dat'#
property main_path: Path#
__init__(output_dir, main_file='UnsatZone.dat')#
class pyiwfm.io.config.SimulationFileConfig(output_dir, main_file='Simulation.in', precip_file='Precipitation.dat', et_file='Evapotranspiration.dat', crop_coef_file='CropCoefficients.dat', supply_adjust_file='SupplyAdjustment.dat', _groundwater=None, _streams=None, _lakes=None, _rootzone=None, _small_watersheds=None, _unsatzone=None, ts_format=OutputFormat.TEXT, dss_file='Simulation.dss', gw_version='4.0', stream_version='5.0', lake_version='5.0', rootzone_version='5.0', small_watershed_version='4.1')[source]#

Bases: object

Configuration for complete simulation file set.

Orchestrates all component configurations.

output_dir: Path#
main_file: str = 'Simulation.in'#
precip_file: str = 'Precipitation.dat'#
et_file: str = 'Evapotranspiration.dat'#
crop_coef_file: str = 'CropCoefficients.dat'#
supply_adjust_file: str = 'SupplyAdjustment.dat'#
ts_format: OutputFormat = 'text'#
dss_file: str = 'Simulation.dss'#
gw_version: str = '4.0'#
stream_version: str = '5.0'#
lake_version: str = '5.0'#
rootzone_version: str = '5.0'#
small_watershed_version: str = '4.1'#
property main_path: Path#
property groundwater: GWFileConfig#
property streams: StreamFileConfig#
property lakes: LakeFileConfig#
property rootzone: RootZoneFileConfig#
property small_watersheds: SmallWatershedFileConfig#
property unsatzone: UnsatZoneFileConfig#
__init__(output_dir, main_file='Simulation.in', precip_file='Precipitation.dat', et_file='Evapotranspiration.dat', crop_coef_file='CropCoefficients.dat', supply_adjust_file='SupplyAdjustment.dat', _groundwater=None, _streams=None, _lakes=None, _rootzone=None, _small_watersheds=None, _unsatzone=None, ts_format=OutputFormat.TEXT, dss_file='Simulation.dss', gw_version='4.0', stream_version='5.0', lake_version='5.0', rootzone_version='5.0', small_watershed_version='4.1')#
class pyiwfm.io.config.BudgetFileConfig(output_dir, main_file='Budget.in')[source]#

Bases: object

Configuration for budget post-processor files.

output_dir: Path#
main_file: str = 'Budget.in'#
property main_path: Path#
__init__(output_dir, main_file='Budget.in')#
class pyiwfm.io.config.ZBudgetFileConfig(output_dir, main_file='ZBudget.in', zone_definition_file='ZoneDefinitions.dat')[source]#

Bases: object

Configuration for zone budget post-processor files.

output_dir: Path#
main_file: str = 'ZBudget.in'#
zone_definition_file: str = 'ZoneDefinitions.dat'#
property main_path: Path#
property zone_definition_path: Path#
__init__(output_dir, main_file='ZBudget.in', zone_definition_file='ZoneDefinitions.dat')#
class pyiwfm.io.config.ModelOutputConfig(output_dir, model_name='IWFM_Model', ts_format=OutputFormat.TEXT, stream_version='5.0', lake_version='5.0', rootzone_version='5.0', preprocessor_subdir='Preprocessor', simulation_subdir='Simulation', _preprocessor=None, _simulation=None)[source]#

Bases: object

Complete model output configuration.

Coordinates preprocessor and simulation file configs.

output_dir: Path#
model_name: str = 'IWFM_Model'#
ts_format: OutputFormat = 'text'#
stream_version: str = '5.0'#
lake_version: str = '5.0'#
rootzone_version: str = '5.0'#
preprocessor_subdir: str = 'Preprocessor'#
simulation_subdir: str = 'Simulation'#
property preprocessor: PreProcessorFileConfig#
property simulation: SimulationFileConfig#
ensure_directories()[source]#

Create all output directories.

__init__(output_dir, model_name='IWFM_Model', ts_format=OutputFormat.TEXT, stream_version='5.0', lake_version='5.0', rootzone_version='5.0', preprocessor_subdir='Preprocessor', simulation_subdir='Simulation', _preprocessor=None, _simulation=None)#
class pyiwfm.io.config.ModelWriteConfig(output_dir, ts_format=OutputFormat.TEXT, dss_file='model.dss', dss_a_part='', dss_f_part='PYIWFM', copy_source_ts=True, model_name='IWFM_Model', gw_version='4.0', stream_version='4.0', lake_version='4.0', rootzone_version='4.12', file_paths=<factory>)[source]#

Bases: object

Configuration for writing a complete IWFM model with per-file path control.

Every output file has a standardized key and a default relative path (producing a nested layout). Users can override any path via the file_paths dict. The writer uses get_path() for absolute paths and get_relative_path() for IWFM-compatible relative references between config files.

Examples

Default nested layout:

config = ModelWriteConfig(output_dir=Path("C:/models/my_model"))

Flat layout (all files in one directory):

config = ModelWriteConfig.flat(output_dir=Path("C:/models/flat"))

Custom layout:

config = ModelWriteConfig(output_dir=Path("C:/models/custom"))
config.set_file("gw_main", "groundwater/main.dat")
config.set_file("stream_main", "surface/streams.dat")
output_dir: Path#
ts_format: OutputFormat = 'text'#
dss_file: str = 'model.dss'#
dss_a_part: str = ''#
dss_f_part: str = 'PYIWFM'#
copy_source_ts: bool = True#
model_name: str = 'IWFM_Model'#
gw_version: str = '4.0'#
stream_version: str = '4.0'#
lake_version: str = '4.0'#
rootzone_version: str = '4.12'#
file_paths: dict[str, str]#
DEFAULT_PATHS: ClassVar[dict[str, str]] = {'dss_ts_file': 'Simulation/climate_data.dss', 'elements': 'Preprocessor/Elements.dat', 'et': 'Simulation/ET.dat', 'gw_bc_main': 'Simulation/GW/BC_MAIN.dat', 'gw_bound_tsd': 'Simulation/GW/BoundTSD.dat', 'gw_elem_pump': 'Simulation/GW/ElemPump.dat', 'gw_main': 'Simulation/GW/GW_MAIN.dat', 'gw_pump_main': 'Simulation/GW/Pump_MAIN.dat', 'gw_spec_flow_bc': 'Simulation/GW/SpecFlowBC.dat', 'gw_spec_head_bc': 'Simulation/GW/SpecHeadBC.dat', 'gw_subsidence': 'Simulation/GW/Subsidence.dat', 'gw_tile_drain': 'Simulation/GW/TileDrain.dat', 'gw_ts_pumping': 'Simulation/GW/TSPumping.dat', 'gw_well_spec': 'Simulation/GW/WellSpec.dat', 'irig_frac': 'Simulation/IrigFrac.dat', 'lake_config': 'Preprocessor/LakeConfig.dat', 'lake_main': 'Simulation/Lake/Lake_MAIN.dat', 'lake_max_elev': 'Simulation/Lake/MaxLakeElev.dat', 'nodes': 'Preprocessor/Nodes.dat', 'precipitation': 'Simulation/Precip.dat', 'preprocessor_bin': 'Simulation/PreProcessor.bin', 'preprocessor_main': 'Preprocessor/Preprocessor.in', 'results_diver_detail': 'Results/DiverDetail.hdf', 'results_final_lake_elev': 'Results/FinalLakeElev.out', 'results_final_uz': 'Results/FinalUZ.out', 'results_gw_budget': 'Results/GW.hdf', 'results_gw_head': 'Results/GWHeadAll.out', 'results_gw_zbudget': 'Results/GW_ZBud.hdf', 'results_lake_budget': 'Results/LakeBud.hdf', 'results_lwu_budget': 'Results/LWU.hdf', 'results_lwu_zbudget': 'Results/LWU_ZBud.hdf', 'results_pump_output': 'Results/ElemWellPumping.out', 'results_rz_budget': 'Results/RootZone.hdf', 'results_rz_zbudget': 'Results/RootZone_ZBud.hdf', 'results_strm_budget': 'Results/StrmBud.hdf', 'results_strm_hyd': 'Results/StrmHyd.out', 'results_strm_node_budget': 'Results/StrmNodeBud.hdf', 'results_uz_budget': 'Results/UZBud.hdf', 'results_uz_zbudget': 'Results/UZZBud.hdf', 'rootzone_irig_period': 'Simulation/RootZone/IrigPeriod.dat', 'rootzone_main': 'Simulation/RootZone/RootZone_MAIN.dat', 'rootzone_native': 'Simulation/RootZone/NativeRiparian.dat', 'rootzone_nonponded': 'Simulation/RootZone/NonPondedCrops.dat', 'rootzone_ponded': 'Simulation/RootZone/PondedCrops.dat', 'rootzone_return_flow': 'Simulation/RootZone/ReturnFlowFrac.dat', 'rootzone_reuse': 'Simulation/RootZone/ReuseFrac.dat', 'rootzone_surface_flow_dest': 'Simulation/RootZone/SurfaceFlowDest.dat', 'rootzone_urban': 'Simulation/RootZone/Urban.dat', 'simulation_main': 'Simulation/Simulation_MAIN.IN', 'stratigraphy': 'Preprocessor/Stratigraphy.dat', 'stream_bypass_specs': 'Simulation/Stream/BypassSpecs.dat', 'stream_config': 'Preprocessor/StreamConfig.dat', 'stream_diver_specs': 'Simulation/Stream/DiverSpecs.dat', 'stream_diversions': 'Simulation/Stream/Diversions.dat', 'stream_inflow': 'Simulation/Stream/StreamInflow.dat', 'stream_main': 'Simulation/Stream/Stream_MAIN.dat', 'supply_adjust': 'Simulation/SupplyAdjust.dat', 'swshed_main': 'Simulation/SmallWatersheds/SWatersheds.dat', 'unsatzone_main': 'Simulation/UnsatZone.dat'}#
get_path(file_key)[source]#

Return absolute path for a file key.

Parameters:

file_key (str) – Standardized key from DEFAULT_PATHS.

Returns:

Absolute path to the file.

Raises:

KeyError – If file_key is not in file_paths or DEFAULT_PATHS.

Return type:

Path

get_relative_path(from_key, to_key)[source]#

Compute relative path from one file’s directory to another file.

This produces the path string that IWFM writes in its config files. For example, Simulation_MAIN.IN references GW_MAIN.dat as GW\GW_MAIN.dat.

Uses os.path.relpath to handle arbitrary directory structures including paths that require .. traversal.

Parameters:
  • from_key (str) – File key of the referencing file.

  • to_key (str) – File key of the referenced file.

Returns:

Relative path string with OS-native separators.

Return type:

str

set_file(file_key, relative_path)[source]#

Set a custom path for a file key (relative to output_dir).

Parameters:
  • file_key (str) – Standardized key (must be in DEFAULT_PATHS).

  • relative_path (str) – Path relative to output_dir.

classmethod nested(output_dir, **kwargs)[source]#

Create config with standard nested layout (default).

classmethod flat(output_dir, **kwargs)[source]#

Create config with all files in one directory.

__init__(output_dir, ts_format=OutputFormat.TEXT, dss_file='model.dss', dss_a_part='', dss_f_part='PYIWFM', copy_source_ts=True, model_name='IWFM_Model', gw_version='4.0', stream_version='4.0', lake_version='4.0', rootzone_version='4.12', file_paths=<factory>)#

Writer Configuration Base#

Shared base dataclass (BaseComponentWriterConfig) for all component writer configs (groundwater, streams, lakes, root zone, small watersheds, unsaturated zone). Provides common fields: output_dir, version, length_factor, length_unit, volume_factor, volume_unit, subdir.

Base configuration for IWFM component file writers.

Provides BaseComponentWriterConfig, a dataclass base that encapsulates the fields common to every component writer config (groundwater, streams, lakes, root zone, small watersheds, and unsaturated zone).

Subclasses override _get_subdir() and _get_main_file() to provide the component-specific defaults, while inheriting the output_dir, version, component_dir, and main_path interface.

class pyiwfm.io.writer_config_base.BaseComponentWriterConfig(output_dir, version='4.0')[source]#

Bases: object

Base configuration shared by all IWFM component writers.

Variables:
  • output_dir (Path) – Base output directory (typically <model>/Simulation).

  • version (str) – IWFM component version string (e.g. "4.0").

output_dir: Path#
version: str = '4.0'#
property component_dir: Path#

Get the component subdirectory path.

property main_path: Path#

Get the main file path.

__init__(output_dir, version='4.0')#

PreProcessor I/O#

PreProcessor Module#

The preprocessor module provides functions for loading and saving complete IWFM models.

PreProcessor file I/O handlers for IWFM models.

This module provides functions for reading and writing IWFM PreProcessor input files, which define the model structure and input file paths.

class pyiwfm.io.preprocessor.PreProcessorConfig(base_dir, model_name='', nodes_file=None, elements_file=None, stratigraphy_file=None, subregions_file=None, streams_file=None, lakes_file=None, groundwater_file=None, rootzone_file=None, pumping_file=None, output_dir=None, budget_output_file=None, heads_output_file=None, n_layers=1, length_unit='FT', area_unit='ACRES', volume_unit='AF', metadata=<factory>)[source]#

Bases: object

Configuration from an IWFM PreProcessor main input file.

Contains paths to all component input files and model settings.

base_dir: Path#
model_name: str = ''#
nodes_file: Path | None = None#
elements_file: Path | None = None#
stratigraphy_file: Path | None = None#
subregions_file: Path | None = None#
streams_file: Path | None = None#
lakes_file: Path | None = None#
groundwater_file: Path | None = None#
rootzone_file: Path | None = None#
pumping_file: Path | None = None#
output_dir: Path | None = None#
budget_output_file: Path | None = None#
heads_output_file: Path | None = None#
n_layers: int = 1#
length_unit: str = 'FT'#
area_unit: str = 'ACRES'#
volume_unit: str = 'AF'#
metadata: dict[str, Any]#
__init__(base_dir, model_name='', nodes_file=None, elements_file=None, stratigraphy_file=None, subregions_file=None, streams_file=None, lakes_file=None, groundwater_file=None, rootzone_file=None, pumping_file=None, output_dir=None, budget_output_file=None, heads_output_file=None, n_layers=1, length_unit='FT', area_unit='ACRES', volume_unit='AF', metadata=<factory>)#
pyiwfm.io.preprocessor.read_preprocessor_main(filepath)[source]#

Read an IWFM PreProcessor main input file.

The main input file contains paths to all component input files and basic model configuration.

Expected format (simplified):

Model Name / Description nodes.dat / NODES_FILE elements.dat / ELEMENTS_FILE stratigraphy.dat / STRAT_FILE …

Parameters:

filepath (Path | str) – Path to the main PreProcessor input file

Returns:

PreProcessorConfig with parsed values

Return type:

PreProcessorConfig

pyiwfm.io.preprocessor.read_subregions_file(filepath)[source]#

Read subregion definitions from a subregions file.

Expected format:

NSUBREGION / Number of subregions ID NAME (one line per subregion)

Parameters:

filepath (Path | str) – Path to the subregions file

Returns:

Dictionary mapping subregion ID to Subregion object

Return type:

dict[int, Subregion]

pyiwfm.io.preprocessor.write_preprocessor_main(filepath, config, header=None)[source]#

Write an IWFM PreProcessor main input file.

Parameters:
  • filepath (Path | str) – Path to the output file

  • config (PreProcessorConfig) – PreProcessorConfig with file paths and settings

  • header (str | None) – Optional header comment

pyiwfm.io.preprocessor.save_model_to_preprocessor(model, output_dir, model_name=None)[source]#

Save an IWFMModel to PreProcessor input files.

Creates all necessary input files in the output directory.

Parameters:
  • model (IWFMModel) – IWFMModel to save

  • output_dir (Path | str) – Directory for output files

  • model_name (str | None) – Model name (defaults to model.name)

Returns:

PreProcessorConfig with paths to created files

Return type:

PreProcessorConfig

pyiwfm.io.preprocessor.save_complete_model(model, output_dir, timeseries_format='ascii', dss_file=None, file_paths=None)[source]#

Save a complete IWFM model to all input files.

This is the main function for exporting a complete model, including all component files (groundwater, streams, lakes, rootzone), time series, and the preprocessor/simulation control files.

Delegates to CompleteModelWriter for the actual writing.

Parameters:
  • model (IWFMModel) – IWFMModel to save

  • output_dir (Path | str) – Directory for output files

  • timeseries_format (str) – Format for time series data (“ascii” or “dss”)

  • dss_file (Path | str | None) – Path to DSS file for time series (if format is “dss”)

  • file_paths (dict[str, str] | None) – Optional dict of {file_key: relative_path} overrides. If None, uses default nested layout.

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

Example

>>> from pyiwfm.io import save_complete_model
>>> files = save_complete_model(model, Path("./model_output"))
>>> print(f"Wrote {len(files)} files")

PreProcessor Binary#

Reader for IWFM preprocessor binary output files.

PreProcessor Binary File Reader for IWFM.

This module reads the binary output file produced by the IWFM PreProcessor. The binary file uses ACCESS='STREAM' (raw bytes, no Fortran record markers) and contains pre-processed mesh, stratigraphy, and component data that enables faster simulation startup.

class pyiwfm.io.preprocessor_binary.AppNodeData(id, area, boundary_node, n_connected_node, n_face_id, surrounding_elements, connected_nodes, face_ids, elem_id_on_ccw_side, irrotational_coeff)[source]#

Bases: object

Pre-processed application node data.

id: int#
area: float#
boundary_node: bool#
n_connected_node: int#
n_face_id: int#
surrounding_elements: ndarray[tuple[Any, ...], dtype[int32]]#
connected_nodes: ndarray[tuple[Any, ...], dtype[int32]]#
face_ids: ndarray[tuple[Any, ...], dtype[int32]]#
elem_id_on_ccw_side: ndarray[tuple[Any, ...], dtype[int32]]#
irrotational_coeff: ndarray[tuple[Any, ...], dtype[float64]]#
__init__(id, area, boundary_node, n_connected_node, n_face_id, surrounding_elements, connected_nodes, face_ids, elem_id_on_ccw_side, irrotational_coeff)#
class pyiwfm.io.preprocessor_binary.AppElementData(id, subregion, area, face_ids, vertex_areas, vertex_area_fractions, integral_del_shp_i_del_shp_j, integral_rot_del_shp_i_del_shp_j)[source]#

Bases: object

Pre-processed application element data.

id: int#
subregion: int#
area: float#
face_ids: ndarray[tuple[Any, ...], dtype[int32]]#
vertex_areas: ndarray[tuple[Any, ...], dtype[float64]]#
vertex_area_fractions: ndarray[tuple[Any, ...], dtype[float64]]#
integral_del_shp_i_del_shp_j: ndarray[tuple[Any, ...], dtype[float64]]#
integral_rot_del_shp_i_del_shp_j: ndarray[tuple[Any, ...], dtype[float64]]#
__init__(id, subregion, area, face_ids, vertex_areas, vertex_area_fractions, integral_del_shp_i_del_shp_j, integral_rot_del_shp_i_del_shp_j)#
class pyiwfm.io.preprocessor_binary.AppFaceData(nodes, elements, boundary, lengths)[source]#

Bases: object

Pre-processed application face data.

nodes: ndarray[tuple[Any, ...], dtype[int32]]#
elements: ndarray[tuple[Any, ...], dtype[int32]]#
boundary: ndarray[tuple[Any, ...], dtype[bool]]#
lengths: ndarray[tuple[Any, ...], dtype[float64]]#
__init__(nodes, elements, boundary, lengths)#
class pyiwfm.io.preprocessor_binary.SubregionData(id, name, n_elements, n_neighbor_regions, area, region_elements, neighbor_region_ids, neighbor_n_boundary_faces, neighbor_boundary_faces)[source]#

Bases: object

Pre-processed subregion data.

id: int#
name: str#
n_elements: int#
n_neighbor_regions: int#
area: float#
region_elements: ndarray[tuple[Any, ...], dtype[int32]]#
neighbor_region_ids: ndarray[tuple[Any, ...], dtype[int32]]#
neighbor_n_boundary_faces: ndarray[tuple[Any, ...], dtype[int32]]#
neighbor_boundary_faces: list[ndarray[tuple[Any, ...], dtype[int32]]]#
__init__(id, name, n_elements, n_neighbor_regions, area, region_elements, neighbor_region_ids, neighbor_n_boundary_faces, neighbor_boundary_faces)#
class pyiwfm.io.preprocessor_binary.StratigraphyData(n_layers, ground_surface_elev, top_elev, bottom_elev, active_node, active_layer_above, active_layer_below, top_active_layer, bottom_active_layer)[source]#

Bases: object

Pre-processed stratigraphy data.

n_layers: int#
ground_surface_elev: ndarray[tuple[Any, ...], dtype[float64]]#
top_elev: ndarray[tuple[Any, ...], dtype[float64]]#
bottom_elev: ndarray[tuple[Any, ...], dtype[float64]]#
active_node: ndarray[tuple[Any, ...], dtype[bool]]#
active_layer_above: ndarray[tuple[Any, ...], dtype[int32]]#
active_layer_below: ndarray[tuple[Any, ...], dtype[int32]]#
top_active_layer: ndarray[tuple[Any, ...], dtype[int32]]#
bottom_active_layer: ndarray[tuple[Any, ...], dtype[int32]]#
__init__(n_layers, ground_surface_elev, top_elev, bottom_elev, active_node, active_layer_above, active_layer_below, top_active_layer, bottom_active_layer)#
class pyiwfm.io.preprocessor_binary.StreamGWConnectorData(n_stream_nodes, gw_nodes, layers)[source]#

Bases: object

Pre-processed stream-GW connector data.

n_stream_nodes: int#
gw_nodes: ndarray[tuple[Any, ...], dtype[int32]]#
layers: ndarray[tuple[Any, ...], dtype[int32]]#
__init__(n_stream_nodes, gw_nodes, layers)#
class pyiwfm.io.preprocessor_binary.LakeGWConnectorData(n_lakes, lake_elements, lake_nodes)[source]#

Bases: object

Pre-processed lake-GW connector data.

n_lakes: int#
lake_elements: list[ndarray[tuple[Any, ...], dtype[int32]]]#
lake_nodes: list[ndarray[tuple[Any, ...], dtype[int32]]]#
__init__(n_lakes, lake_elements, lake_nodes)#
class pyiwfm.io.preprocessor_binary.StreamLakeConnectorData(n_connections, stream_nodes, lake_ids)[source]#

Bases: object

Pre-processed stream-lake connector data.

n_connections: int#
stream_nodes: ndarray[tuple[Any, ...], dtype[int32]]#
lake_ids: ndarray[tuple[Any, ...], dtype[int32]]#
__init__(n_connections, stream_nodes, lake_ids)#
class pyiwfm.io.preprocessor_binary.StreamData(n_reaches, n_stream_nodes, reach_ids, reach_names, reach_upstream_nodes, reach_downstream_nodes, reach_outflow_dest)[source]#

Bases: object

Pre-processed stream data.

n_reaches: int#
n_stream_nodes: int#
reach_ids: ndarray[tuple[Any, ...], dtype[int32]]#
reach_names: list[str]#
reach_upstream_nodes: ndarray[tuple[Any, ...], dtype[int32]]#
reach_downstream_nodes: ndarray[tuple[Any, ...], dtype[int32]]#
reach_outflow_dest: ndarray[tuple[Any, ...], dtype[int32]]#
__init__(n_reaches, n_stream_nodes, reach_ids, reach_names, reach_upstream_nodes, reach_downstream_nodes, reach_outflow_dest)#
class pyiwfm.io.preprocessor_binary.LakeData(n_lakes, lake_ids, lake_names, lake_max_elevations, lake_elements)[source]#

Bases: object

Pre-processed lake data.

n_lakes: int#
lake_ids: ndarray[tuple[Any, ...], dtype[int32]]#
lake_names: list[str]#
lake_max_elevations: ndarray[tuple[Any, ...], dtype[float64]]#
lake_elements: list[ndarray[tuple[Any, ...], dtype[int32]]]#
__init__(n_lakes, lake_ids, lake_names, lake_max_elevations, lake_elements)#
class pyiwfm.io.preprocessor_binary.PreprocessorBinaryData(n_nodes=0, n_elements=0, n_faces=0, n_subregions=0, n_boundary_faces=0, x=<factory>, y=<factory>, n_vertex=<factory>, vertex=<factory>, app_nodes=<factory>, app_elements=<factory>, app_faces=None, boundary_face_list=<factory>, subregions=<factory>, stratigraphy=None, stream_lake_connector=None, stream_gw_connector=None, lake_gw_connector=None, lakes=None, streams=None, matrix_n_equations=0, matrix_connectivity=<factory>)[source]#

Bases: object

Complete preprocessor binary output structure.

n_nodes: int = 0#
n_elements: int = 0#
n_faces: int = 0#
n_subregions: int = 0#
n_boundary_faces: int = 0#
x: ndarray[tuple[Any, ...], dtype[float64]]#
y: ndarray[tuple[Any, ...], dtype[float64]]#
n_vertex: ndarray[tuple[Any, ...], dtype[int32]]#
vertex: ndarray[tuple[Any, ...], dtype[int32]]#
app_nodes: list[AppNodeData]#
app_elements: list[AppElementData]#
app_faces: AppFaceData | None = None#
boundary_face_list: ndarray[tuple[Any, ...], dtype[int32]]#
subregions: list[SubregionData]#
stratigraphy: StratigraphyData | None = None#
stream_lake_connector: StreamLakeConnectorData | None = None#
stream_gw_connector: StreamGWConnectorData | None = None#
lake_gw_connector: LakeGWConnectorData | None = None#
lakes: LakeData | None = None#
streams: StreamData | None = None#
matrix_n_equations: int = 0#
matrix_connectivity: ndarray[tuple[Any, ...], dtype[int32]]#
__init__(n_nodes=0, n_elements=0, n_faces=0, n_subregions=0, n_boundary_faces=0, x=<factory>, y=<factory>, n_vertex=<factory>, vertex=<factory>, app_nodes=<factory>, app_elements=<factory>, app_faces=None, boundary_face_list=<factory>, subregions=<factory>, stratigraphy=None, stream_lake_connector=None, stream_gw_connector=None, lake_gw_connector=None, lakes=None, streams=None, matrix_n_equations=0, matrix_connectivity=<factory>)#
class pyiwfm.io.preprocessor_binary.PreprocessorBinaryReader(endian='<')[source]#

Bases: object

Reader for IWFM PreProcessor binary output files.

The preprocessor binary uses ACCESS='STREAM' (raw bytes, no Fortran record markers). All arrays are written with explicit lengths that must be derived from dimension counts read earlier in the file.

Section read order (matching IWFM Fortran WritePreprocessedData):

  1. AppGrid — dimensions, coordinates, connectivity, per-node/element data, face data, boundary faces, subregions

  2. Stratigraphy — NLayers, TopActiveLayer, ActiveNode, GSElev, TopElev, BottomElev (2-D arrays in Fortran column-major order)

  3. StrmLakeConnector — 3 sub-connectors (stream→lake, lake→stream, lake→lake), each with count + source/dest arrays

  4. StrmGWConnector — version int, then gw_node/layer arrays

  5. LakeGWConnector — per-lake element/node arrays

  6. AppLake — version int, per-lake data with rating tables

  7. AppStream — version int, per-node rating tables, per-reach metadata

  8. Matrix — sparse structure (optional)

__init__(endian='<')[source]#
read(filepath)[source]#
pyiwfm.io.preprocessor_binary.read_preprocessor_binary(filepath, endian='<')[source]#

Convenience function to read preprocessor binary file.

Parameters:
  • filepath (Path | str) – Path to the binary file

  • endian (str) – Byte order

Returns:

PreprocessorBinaryData with all preprocessed data

Return type:

PreprocessorBinaryData

PreProcessor Writer#

Writer for IWFM preprocessor input files (nodes, elements, stratigraphy).

PreProcessor file writers for IWFM models.

This module provides writers for all preprocessor input files, enabling complete round-trip read/write of IWFM models.

class pyiwfm.io.preprocessor_writer.PreProcessorWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM PreProcessor input files.

Writes complete preprocessor file sets including: - Main control file - Node coordinates - Element configuration - Stratigraphy data - Stream configuration (optional) - Lake configuration (optional)

Example

>>> from pyiwfm.core.model import IWFMModel
>>> from pyiwfm.io.preprocessor_writer import PreProcessorWriter
>>> from pyiwfm.io.config import PreProcessorFileConfig
>>>
>>> # Load a model
>>> model = IWFMModel.from_preprocessor("model/Preprocessor.in")
>>>
>>> # Write to new location
>>> config = PreProcessorFileConfig(output_dir="output/Preprocessor")
>>> writer = PreProcessorWriter(model, config)
>>> writer.write_all()
__init__(model, config, template_engine=None)[source]#

Initialize the preprocessor writer.

Parameters:
  • model (IWFMModel) – Model to write

  • config (PreProcessorFileConfig) – Output file configuration

  • template_engine (TemplateEngine, optional) – Custom template engine

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all preprocessor files.

write_all()[source]#

Write all preprocessor files.

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

write_main()[source]#

Write the main preprocessor control file.

Returns:

Path to written file

Return type:

Path

write_nodes()[source]#

Write the node coordinates file.

Returns:

Path to written file

Return type:

Path

write_elements()[source]#

Write the element configuration file.

Returns:

Path to written file

Return type:

Path

write_stratigraphy()[source]#

Write the stratigraphy data file.

IWFM stratigraphy format uses THICKNESSES, not elevations: - ID, GSElev, W(1), W(2), W(3), W(4), … - W(2*i-1) = aquitard thickness above layer i (from previous bottom or GS to layer top) - W(2*i) = aquifer thickness of layer i (from layer top to layer bottom)

For NLayers layers, each row has 2 + 2*NLayers columns.

Returns:

Path to written file

Return type:

Path

write_stream_config()[source]#

Write the stream configuration file.

Returns:

Path to written file

Return type:

Path

write_lake_config()[source]#

Write the lake configuration file.

Returns:

Path to written file

Return type:

Path

pyiwfm.io.preprocessor_writer.write_preprocessor_files(model, output_dir, stream_version='5.0', lake_version='5.0')[source]#

Write all preprocessor files for a model.

Parameters:
  • model (IWFMModel) – Model to write

  • output_dir (Path or str) – Output directory

  • stream_version (str) – Stream component version

  • lake_version (str) – Lake component version

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

Example

>>> from pyiwfm.core.model import IWFMModel
>>> from pyiwfm.io.preprocessor_writer import write_preprocessor_files
>>>
>>> model = IWFMModel.from_preprocessor("model/Preprocessor.in")
>>> results = write_preprocessor_files(model, "output/Preprocessor")
>>> print(results["main"])
output/Preprocessor/Preprocessor.in
pyiwfm.io.preprocessor_writer.write_nodes_file(output_path, node_ids, x_coords, y_coords, coord_factor=1.0)[source]#

Write a standalone nodes file.

Parameters:
  • output_path (Path or str) – Output file path

  • node_ids (NDArray[np.intp]) – Node IDs

  • x_coords (NDArray[np.float64]) – X coordinates

  • y_coords (NDArray[np.float64]) – Y coordinates

  • coord_factor (float) – Coordinate conversion factor

Returns:

Path to written file

Return type:

Path

pyiwfm.io.preprocessor_writer.write_elements_file(output_path, element_ids, vertices, subregions, subregion_names=None)[source]#

Write a standalone elements file.

Parameters:
  • output_path (Path or str) – Output file path

  • element_ids (NDArray[np.int32]) – Element IDs

  • vertices (NDArray[np.int32]) – Vertex node IDs (n_elements, 4)

  • subregions (NDArray[np.int32]) – Subregion ID for each element

  • subregion_names (dict, optional) – Mapping of subregion ID to name

Returns:

Path to written file

Return type:

Path

pyiwfm.io.preprocessor_writer.write_stratigraphy_file(output_path, node_ids, ground_surface, layer_tops, layer_bottoms, elev_factor=1.0)[source]#

Write a standalone stratigraphy file in IWFM format.

IWFM stratigraphy uses THICKNESSES, not elevations: - ID, GSElev, W(1), W(2), W(3), W(4), … - W(2*i-1) = aquitard thickness above layer i - W(2*i) = aquifer thickness of layer i

Parameters:
  • output_path (Path or str) – Output file path

  • node_ids (NDArray[np.intp]) – Node IDs

  • ground_surface (NDArray[np.float64]) – Ground surface elevations (n_nodes,)

  • layer_tops (NDArray[np.float64]) – Layer top elevations (n_nodes, n_layers)

  • layer_bottoms (NDArray[np.float64]) – Layer bottom elevations (n_nodes, n_layers)

  • elev_factor (float) – Elevation conversion factor

Returns:

Path to written file

Return type:

Path

Simulation I/O#

Simulation Module#

The simulation module provides readers and writers for IWFM simulation control files.

Simulation control I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM simulation control files including the main simulation input file, time stepping, and output control settings.

class pyiwfm.io.simulation.SimulationConfig(model_name='IWFM_Model', title_lines=<factory>, start_date=<factory>, end_date=<factory>, time_step_length=1, time_step_unit=TimeUnit.DAY, restart_flag=0, output_interval=1, budget_output_interval=1, heads_output_interval=1, preprocessor_file=None, binary_preprocessor_file=None, groundwater_file=None, streams_file=None, lakes_file=None, rootzone_file=None, unsaturated_zone_file=None, small_watershed_file=None, irrigation_fractions_file=None, supply_adjust_file=None, precipitation_file=None, et_file=None, kc_file=None, output_dir=None, restart_output_flag=0, debug_flag=0, cache_size=500000, matrix_solver=2, relaxation=1.0, max_iterations=50, max_supply_iterations=50, convergence_tolerance=1e-06, convergence_volume=0.0, convergence_supply=0.001, supply_adjust_option=0, metadata=<factory>)[source]#

Bases: object

Configuration for an IWFM simulation.

Variables:
  • model_name (str) – Name of the model

  • title_lines (list[str]) – Project title lines (up to 3)

  • start_date (datetime.datetime) – Simulation start datetime

  • end_date (datetime.datetime) – Simulation end datetime

  • time_step_length (int) – Length of each time step

  • time_step_unit (pyiwfm.core.timeseries.TimeUnit) – Unit of time step (DAY, HOUR, etc.)

  • restart_flag (int) – Restart option (0=No, 1=Yes)

  • output_interval (int) – Output interval (multiple of time step)

  • preprocessor_file (pathlib.Path | None) – Path to preprocessor main file

  • binary_preprocessor_file (pathlib.Path | None) – Path to preprocessor binary output

  • groundwater_file (pathlib.Path | None) – Path to groundwater component file

  • streams_file (pathlib.Path | None) – Path to streams component file

  • lakes_file (pathlib.Path | None) – Path to lakes component file

  • rootzone_file (pathlib.Path | None) – Path to rootzone component file

  • unsaturated_zone_file (pathlib.Path | None) – Path to unsaturated zone component file

  • small_watershed_file (pathlib.Path | None) – Path to small watershed component file

  • irrigation_fractions_file (pathlib.Path | None) – Path to irrigation fractions data file

  • supply_adjust_file (pathlib.Path | None) – Path to supply adjustment specification file

  • precipitation_file (pathlib.Path | None) – Path to precipitation data file

  • et_file (pathlib.Path | None) – Path to evapotranspiration data file

  • kc_file (pathlib.Path | None) – Path to crop/habitat coefficient data file

  • output_dir (pathlib.Path | None) – Directory for output files

  • restart_output_flag (int) – Generate restart file (0=No, 1=Yes)

  • debug_flag (int) – Debug output level (-1, 0, or 1)

  • cache_size (int) – Cache size limit for time series entries

  • matrix_solver (int) – Matrix solver option (1=SOR, 2=Conjugate gradient)

  • relaxation (float) – Relaxation factor for iterative solver

  • max_iterations (int) – Maximum flow convergence iterations

  • max_supply_iterations (int) – Maximum supply adjustment iterations

  • convergence_tolerance (float) – Flow convergence tolerance (STOPC)

  • convergence_volume (float) – Volume convergence tolerance (STOPCVL)

  • convergence_supply (float) – Supply adjustment convergence tolerance (STOPCSP)

  • supply_adjust_option (int) – Water supply adjustment flag

model_name: str = 'IWFM_Model'#
title_lines: list[str]#
start_date: datetime#
end_date: datetime#
time_step_length: int = 1#
time_step_unit: TimeUnit = 'DAY'#
restart_flag: int = 0#
output_interval: int = 1#
budget_output_interval: int = 1#
heads_output_interval: int = 1#
preprocessor_file: Path | None = None#
binary_preprocessor_file: Path | None = None#
groundwater_file: Path | None = None#
streams_file: Path | None = None#
lakes_file: Path | None = None#
rootzone_file: Path | None = None#
unsaturated_zone_file: Path | None = None#
small_watershed_file: Path | None = None#
irrigation_fractions_file: Path | None = None#
supply_adjust_file: Path | None = None#
precipitation_file: Path | None = None#
et_file: Path | None = None#
kc_file: Path | None = None#
output_dir: Path | None = None#
restart_output_flag: int = 0#
debug_flag: int = 0#
cache_size: int = 500000#
matrix_solver: int = 2#
relaxation: float = 1.0#
max_iterations: int = 50#
max_supply_iterations: int = 50#
convergence_tolerance: float = 1e-06#
convergence_volume: float = 0.0#
convergence_supply: float = 0.001#
supply_adjust_option: int = 0#
metadata: dict[str, Any]#
property n_time_steps: int#

Calculate the number of time steps in the simulation.

to_simulation_period()[source]#

Convert to SimulationPeriod object.

__init__(model_name='IWFM_Model', title_lines=<factory>, start_date=<factory>, end_date=<factory>, time_step_length=1, time_step_unit=TimeUnit.DAY, restart_flag=0, output_interval=1, budget_output_interval=1, heads_output_interval=1, preprocessor_file=None, binary_preprocessor_file=None, groundwater_file=None, streams_file=None, lakes_file=None, rootzone_file=None, unsaturated_zone_file=None, small_watershed_file=None, irrigation_fractions_file=None, supply_adjust_file=None, precipitation_file=None, et_file=None, kc_file=None, output_dir=None, restart_output_flag=0, debug_flag=0, cache_size=500000, matrix_solver=2, relaxation=1.0, max_iterations=50, max_supply_iterations=50, convergence_tolerance=1e-06, convergence_volume=0.0, convergence_supply=0.001, supply_adjust_option=0, metadata=<factory>)#
class pyiwfm.io.simulation.SimulationFileConfig(output_dir, main_file='simulation.in', time_series_dir='timeseries')[source]#

Bases: object

Configuration for simulation file paths.

Variables:
  • output_dir (pathlib.Path) – Directory for output files

  • main_file (str) – Main simulation input file name

  • time_series_dir (str) – Directory for time series files

output_dir: Path#
main_file: str = 'simulation.in'#
time_series_dir: str = 'timeseries'#
get_main_file_path()[source]#
get_time_series_dir()[source]#
__init__(output_dir, main_file='simulation.in', time_series_dir='timeseries')#
class pyiwfm.io.simulation.SimulationWriter(file_config)[source]#

Bases: object

Writer for IWFM simulation control files.

Writes the main simulation input file and related control files.

Example

>>> file_config = SimulationFileConfig(output_dir=Path("./model"))
>>> writer = SimulationWriter(file_config)
>>> filepath = writer.write(sim_config)
__init__(file_config)[source]#

Initialize the simulation writer.

Parameters:

file_config (SimulationFileConfig) – File configuration

write(config, header=None)[source]#

Write the main simulation input file.

Parameters:
  • config (SimulationConfig) – Simulation configuration

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

class pyiwfm.io.simulation.SimulationReader[source]#

Bases: object

Reader for IWFM simulation control files.

read(filepath)[source]#

Read simulation configuration from main input file.

Parameters:

filepath (Path | str) – Path to simulation input file

Returns:

SimulationConfig object

Return type:

SimulationConfig

class pyiwfm.io.simulation.IWFMSimulationReader[source]#

Bases: object

Reader for IWFM simulation main files in positional sequential format.

Reads the actual IWFM Fortran format where values appear in fixed order (titles, file paths, time settings, solver parameters) as defined in SIM_ReadMainControlData in Package_Model.f90.

This reader handles both C2VSimFG-style files with / description comments and bare positional files without descriptions.

__init__()[source]#
read(filepath, base_dir=None)[source]#

Read IWFM simulation main file in positional format.

Parameters:
  • filepath (Path | str) – Path to the simulation main input file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

SimulationConfig with all configuration data

Return type:

SimulationConfig

pyiwfm.io.simulation.read_iwfm_simulation(filepath, base_dir=None)[source]#

Read IWFM simulation main file in positional format.

This reads files in the native IWFM Fortran format where values appear in a fixed order (titles, file paths, time settings, solver parameters).

Parameters:
  • filepath (Path | str) – Path to the simulation main input file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

SimulationConfig with all configuration data

Return type:

SimulationConfig

pyiwfm.io.simulation.write_simulation(config, output_dir, file_config=None)[source]#

Write simulation control file.

Parameters:
Returns:

Path to written file

Return type:

Path

pyiwfm.io.simulation.read_simulation(filepath)[source]#

Read simulation configuration from file.

Parameters:

filepath (Path | str) – Path to simulation input file

Returns:

SimulationConfig object

Return type:

SimulationConfig

Simulation Writer#

Writer for IWFM simulation main control files.

Simulation Main File Writer for IWFM models.

This module provides the main writer for IWFM simulation main input files, orchestrating the writing of the main control file that references all component input files.

class pyiwfm.io.simulation_writer.SimulationMainConfig(output_dir, main_file='Simulation_MAIN.IN', preprocessor_bin='PreProcessor.bin', gw_main='GW\\GW_MAIN.dat', stream_main='Stream\\Stream_MAIN.dat', lake_main='Lake\\Lake_MAIN.dat', rootzone_main='RootZone\\RootZone_MAIN.dat', swshed_main='', unsatzone_main='', irig_frac='IrigFrac.dat', supply_adjust='SupplyAdjust.dat', precip_file='Precip.dat', et_file='ET.dat', kc_file='', begin_date='09/30/1990_24:00', end_date='09/30/2000_24:00', time_step='1DAY', restart=0, istrt=0, kdeb=0, cache_size=500000, matrix_solver=2, relaxation=1.0, max_iterations=1500, max_supply_iter=50, convergence_head=0.0001, convergence_volume=0.001, convergence_supply=0.001, supply_adjust_option=11, title1='IWFM', title2='Generated by pyiwfm', title3='')[source]#

Bases: object

Configuration for simulation main file writing.

Variables:

output_dir (Path) – Base output directory for simulation files

output_dir: Path#
main_file: str = 'Simulation_MAIN.IN'#
preprocessor_bin: str = 'PreProcessor.bin'#
gw_main: str = 'GW\\GW_MAIN.dat'#
stream_main: str = 'Stream\\Stream_MAIN.dat'#
lake_main: str = 'Lake\\Lake_MAIN.dat'#
rootzone_main: str = 'RootZone\\RootZone_MAIN.dat'#
swshed_main: str = ''#
unsatzone_main: str = ''#
irig_frac: str = 'IrigFrac.dat'#
supply_adjust: str = 'SupplyAdjust.dat'#
precip_file: str = 'Precip.dat'#
et_file: str = 'ET.dat'#
kc_file: str = ''#
begin_date: str = '09/30/1990_24:00'#
end_date: str = '09/30/2000_24:00'#
time_step: str = '1DAY'#
restart: int = 0#
istrt: int = 0#
kdeb: int = 0#
cache_size: int = 500000#
matrix_solver: int = 2#
relaxation: float = 1.0#
max_iterations: int = 1500#
max_supply_iter: int = 50#
convergence_head: float = 0.0001#
convergence_volume: float = 0.001#
convergence_supply: float = 0.001#
supply_adjust_option: int = 11#
title1: str = 'IWFM'#
title2: str = 'Generated by pyiwfm'#
title3: str = ''#
property main_path: Path#

Get the main file path.

__init__(output_dir, main_file='Simulation_MAIN.IN', preprocessor_bin='PreProcessor.bin', gw_main='GW\\GW_MAIN.dat', stream_main='Stream\\Stream_MAIN.dat', lake_main='Lake\\Lake_MAIN.dat', rootzone_main='RootZone\\RootZone_MAIN.dat', swshed_main='', unsatzone_main='', irig_frac='IrigFrac.dat', supply_adjust='SupplyAdjust.dat', precip_file='Precip.dat', et_file='ET.dat', kc_file='', begin_date='09/30/1990_24:00', end_date='09/30/2000_24:00', time_step='1DAY', restart=0, istrt=0, kdeb=0, cache_size=500000, matrix_solver=2, relaxation=1.0, max_iterations=1500, max_supply_iter=50, convergence_head=0.0001, convergence_volume=0.001, convergence_supply=0.001, supply_adjust_option=11, title1='IWFM', title2='Generated by pyiwfm', title3='')#
class pyiwfm.io.simulation_writer.SimulationMainWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM Simulation Main Input File.

Writes the main simulation control file that references all component files.

Example

>>> from pyiwfm.io.simulation_writer import SimulationMainWriter, SimulationMainConfig
>>> config = SimulationMainConfig(output_dir=Path("model/Simulation"))
>>> writer = SimulationMainWriter(model, config)
>>> path = writer.write_main()
__init__(model, config, template_engine=None)[source]#

Initialize the simulation main writer.

Parameters:
  • model (IWFMModel) – Model to write

  • config (SimulationMainConfig) – Output file configuration

  • template_engine (TemplateEngine, optional) – Custom template engine

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all simulation files.

write_main()[source]#

Write the simulation main input file.

Returns:

Path to written file

Return type:

Path

pyiwfm.io.simulation_writer.write_simulation_main(model, output_dir, config=None)[source]#

Write simulation main file for a model.

Parameters:
  • model (IWFMModel) – Model to write

  • output_dir (Path or str) – Output directory

  • config (SimulationMainConfig, optional) – File configuration

Returns:

Path to written file

Return type:

Path

Groundwater I/O#

Groundwater Module#

The groundwater module provides readers and writers for IWFM groundwater component files.

Groundwater component I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM groundwater component files including wells, pumping, boundary conditions, aquifer parameters, tile drains, and subsidence data.

class pyiwfm.io.groundwater.KhAnomalyEntry(element_id, kh_per_layer)[source]#

Bases: object

Single Kh anomaly overwrite for one element.

Variables:
  • element_id (int) – 1-based element ID to overwrite.

  • kh_per_layer (list[float]) – Kh values per layer, already multiplied by FACT.

element_id: int#
kh_per_layer: list[float]#
__init__(element_id, kh_per_layer)#
class pyiwfm.io.groundwater.ParametricGridData(n_nodes, n_elements, elements, node_coords, node_values, node_range_str='', raw_node_lines=<factory>)[source]#

Bases: object

Raw parametric grid data parsed from the GW main file.

Variables:
  • n_nodes (int) – Number of parametric grid nodes.

  • n_elements (int) – Number of parametric grid elements.

  • elements (list[tuple[int, ...]]) – Element vertex index tuples (0-based into node arrays).

  • node_coords (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Parametric node coordinates, shape (n_nodes, 2).

  • node_values (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Parameter values per node, shape (n_nodes, n_layers, n_params). The 5 parameters are: Kh, Ss, Sy, AquitardKv, Kv.

  • node_range_str (str) – Raw node range string from file (e.g., “1-441”).

  • raw_node_lines (list[str]) – Raw text lines for each parametric node (before parsing).

n_nodes: int#
n_elements: int#
elements: list[tuple[int, ...]]#
node_coords: ndarray[tuple[Any, ...], dtype[float64]]#
node_values: ndarray[tuple[Any, ...], dtype[float64]]#
node_range_str: str = ''#
raw_node_lines: list[str]#
__init__(n_nodes, n_elements, elements, node_coords, node_values, node_range_str='', raw_node_lines=<factory>)#
class pyiwfm.io.groundwater.FaceFlowSpec(id, layer, node_a, node_b, name='')[source]#

Bases: object

Element face flow output specification.

Parsed from the inline face flow data in the GW main file. Format per line: ID IOUTFL IOUTFA IOUTFB NAME

Variables:
  • id (int) – Face flow output ID.

  • layer (int) – Aquifer layer for output.

  • node_a (int) – First node defining the element face.

  • node_b (int) – Second node defining the element face.

  • name (str) – Optional description.

id: int#
layer: int#
node_a: int#
node_b: int#
name: str = ''#
__init__(id, layer, node_a, node_b, name='')#
class pyiwfm.io.groundwater.GWFileConfig(output_dir, wells_file='wells.dat', pumping_file='pumping.dat', aquifer_params_file='aquifer_params.dat', boundary_conditions_file='boundary_conditions.dat', tile_drains_file='tile_drains.dat', subsidence_file='subsidence.dat', initial_heads_file='initial_heads.dat')[source]#

Bases: object

Configuration for groundwater component files.

Variables:
  • output_dir (pathlib.Path) – Directory for output files

  • wells_file (str) – Wells definition file name

  • pumping_file (str) – Pumping time series file name

  • aquifer_params_file (str) – Aquifer parameters file name

  • boundary_conditions_file (str) – Boundary conditions file name

  • tile_drains_file (str) – Tile drains file name

  • subsidence_file (str) – Subsidence parameters file name

  • initial_heads_file (str) – Initial heads file name

output_dir: Path#
wells_file: str = 'wells.dat'#
pumping_file: str = 'pumping.dat'#
aquifer_params_file: str = 'aquifer_params.dat'#
boundary_conditions_file: str = 'boundary_conditions.dat'#
tile_drains_file: str = 'tile_drains.dat'#
subsidence_file: str = 'subsidence.dat'#
initial_heads_file: str = 'initial_heads.dat'#
get_wells_path()[source]#
get_pumping_path()[source]#
get_aquifer_params_path()[source]#
get_boundary_conditions_path()[source]#
get_tile_drains_path()[source]#
get_subsidence_path()[source]#
get_initial_heads_path()[source]#
__init__(output_dir, wells_file='wells.dat', pumping_file='pumping.dat', aquifer_params_file='aquifer_params.dat', boundary_conditions_file='boundary_conditions.dat', tile_drains_file='tile_drains.dat', subsidence_file='subsidence.dat', initial_heads_file='initial_heads.dat')#
class pyiwfm.io.groundwater.GroundwaterWriter(config)[source]#

Bases: object

Writer for IWFM groundwater component files.

Writes all groundwater-related input files including wells, pumping time series, boundary conditions, aquifer parameters, etc.

Example

>>> config = GWFileConfig(output_dir=Path("./model"))
>>> writer = GroundwaterWriter(config)
>>> files = writer.write(gw_component)
__init__(config)[source]#

Initialize the groundwater writer.

Parameters:

config (GWFileConfig) – File configuration

write(gw)[source]#

Write all groundwater component files.

Parameters:

gw (AppGW) – AppGW component to write

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

write_wells(gw, header=None)[source]#

Write wells definition file.

Parameters:
  • gw (AppGW) – AppGW component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_pumping_timeseries(filepath, times, pumping_rates, well_ids=None, units='TAF', factor=1.0, header=None)[source]#

Write pumping time series file.

Parameters:
  • filepath (Path | str) – Output file path

  • times (Sequence[datetime]) – Sequence of datetime values

  • pumping_rates (dict[int, ndarray[tuple[Any, ...], dtype[float64]]]) – Dictionary mapping well ID to pumping rate array

  • well_ids (list[int] | None) – Order of well IDs (default: sorted)

  • units (str) – Units string

  • factor (float) – Conversion factor

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_aquifer_params(gw, header=None)[source]#

Write aquifer parameters file.

Parameters:
  • gw (AppGW) – AppGW component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_boundary_conditions(gw, header=None)[source]#

Write boundary conditions file.

Parameters:
  • gw (AppGW) – AppGW component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_tile_drains(gw, header=None)[source]#

Write tile drains file.

Parameters:
  • gw (AppGW) – AppGW component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_subsidence(gw, header=None)[source]#

Write subsidence parameters file.

Parameters:
  • gw (AppGW) – AppGW component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_initial_heads(gw, header=None)[source]#

Write initial heads file.

Parameters:
  • gw (AppGW) – AppGW component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

class pyiwfm.io.groundwater.GroundwaterReader[source]#

Bases: object

Reader for IWFM groundwater component files.

read_wells(filepath)[source]#

Read wells from a wells definition file.

Parameters:

filepath (Path | str) – Path to wells file

Returns:

Dictionary mapping well ID to Well object

Return type:

dict[int, Well]

read_initial_heads(filepath)[source]#

Read initial heads from file.

Parameters:

filepath (Path | str) – Path to initial heads file

Returns:

Tuple of (n_nodes, n_layers, heads array)

Return type:

tuple[int, int, ndarray[tuple[Any, …], dtype[float64]]]

read_subsidence(filepath)[source]#

Read subsidence parameters from file.

Parameters:

filepath (Path | str) – Path to subsidence parameters file

Returns:

List of Subsidence objects

Return type:

list[Subsidence]

class pyiwfm.io.groundwater.GWMainFileConfig(version='', bc_file=None, tile_drain_file=None, pumping_file=None, subsidence_file=None, overwrite_file=None, raw_paths=<factory>, head_output_factor=1.0, head_output_unit='FEET', volume_output_factor=1.0, volume_output_unit='TAF', velocity_output_factor=1.0, velocity_output_unit='', velocity_output_file=None, vertical_flow_output_file=None, head_all_output_file=None, head_tecplot_file=None, velocity_tecplot_file=None, budget_output_file=None, zbudget_output_file=None, final_heads_file=None, debug_flag=0, coord_factor=1.0, hydrograph_output_file=None, hydrograph_locations=<factory>, n_face_flow_outputs=0, face_flow_output_file=None, face_flow_specs=<factory>, aquifer_params=None, n_param_groups=0, aq_factors_line='', aq_time_unit_kh='', aq_time_unit_v='', aq_time_unit_l='', tecplot_print_flag=1, kh_anomalies=<factory>, kh_anomaly_factor=1.0, kh_anomaly_time_unit='', return_flow_flag=0, parametric_grids=<factory>, initial_heads=None)[source]#

Bases: object

Configuration parsed from GW component main file.

The groundwater component main file is a dispatcher that references sub-files for boundary conditions, tile drains, pumping, and subsidence. It also contains inline hydrograph output location data.

Variables:
  • version (str) – File format version (e.g., “4.0”)

  • bc_file (pathlib.Path | None) – Path to boundary conditions file

  • tile_drain_file (pathlib.Path | None) – Path to tile drains file

  • pumping_file (pathlib.Path | None) – Path to pumping file

  • subsidence_file (pathlib.Path | None) – Path to subsidence file

  • overwrite_file (pathlib.Path | None) – Path to parameter overwrite file (optional)

  • head_output_factor (float) – Conversion factor for head output

  • head_output_unit (str) – Unit string for head output

  • volume_output_factor (float) – Conversion factor for volume output

  • volume_output_unit (str) – Unit string for volume output

  • debug_flag (int) – Debug output flag

  • coord_factor (float) – Coordinate conversion factor for hydrographs

  • hydrograph_output_file (pathlib.Path | None) – Path to hydrograph output file

  • hydrograph_locations (list[pyiwfm.components.groundwater.HydrographLocation]) – List of GW observation point locations

version: str = ''#
bc_file: Path | None = None#
tile_drain_file: Path | None = None#
pumping_file: Path | None = None#
subsidence_file: Path | None = None#
overwrite_file: Path | None = None#
raw_paths: dict[str, str]#
head_output_factor: float = 1.0#
head_output_unit: str = 'FEET'#
volume_output_factor: float = 1.0#
volume_output_unit: str = 'TAF'#
velocity_output_factor: float = 1.0#
velocity_output_unit: str = ''#
velocity_output_file: Path | None = None#
vertical_flow_output_file: Path | None = None#
head_all_output_file: Path | None = None#
head_tecplot_file: Path | None = None#
velocity_tecplot_file: Path | None = None#
budget_output_file: Path | None = None#
zbudget_output_file: Path | None = None#
final_heads_file: Path | None = None#
debug_flag: int = 0#
coord_factor: float = 1.0#
hydrograph_output_file: Path | None = None#
hydrograph_locations: list[HydrographLocation]#
n_face_flow_outputs: int = 0#
face_flow_output_file: Path | None = None#
face_flow_specs: list[FaceFlowSpec]#
aquifer_params: AquiferParameters | None = None#
n_param_groups: int = 0#
aq_factors_line: str = ''#
aq_time_unit_kh: str = ''#
aq_time_unit_v: str = ''#
aq_time_unit_l: str = ''#
tecplot_print_flag: int = 1#
kh_anomalies: list[KhAnomalyEntry]#
kh_anomaly_factor: float = 1.0#
kh_anomaly_time_unit: str = ''#
return_flow_flag: int = 0#
parametric_grids: list[ParametricGridData]#
initial_heads: ndarray[tuple[Any, ...], dtype[float64]] | None = None#
__init__(version='', bc_file=None, tile_drain_file=None, pumping_file=None, subsidence_file=None, overwrite_file=None, raw_paths=<factory>, head_output_factor=1.0, head_output_unit='FEET', volume_output_factor=1.0, volume_output_unit='TAF', velocity_output_factor=1.0, velocity_output_unit='', velocity_output_file=None, vertical_flow_output_file=None, head_all_output_file=None, head_tecplot_file=None, velocity_tecplot_file=None, budget_output_file=None, zbudget_output_file=None, final_heads_file=None, debug_flag=0, coord_factor=1.0, hydrograph_output_file=None, hydrograph_locations=<factory>, n_face_flow_outputs=0, face_flow_output_file=None, face_flow_specs=<factory>, aquifer_params=None, n_param_groups=0, aq_factors_line='', aq_time_unit_kh='', aq_time_unit_v='', aq_time_unit_l='', tecplot_print_flag=1, kh_anomalies=<factory>, kh_anomaly_factor=1.0, kh_anomaly_time_unit='', return_flow_flag=0, parametric_grids=<factory>, initial_heads=None)#
class pyiwfm.io.groundwater.GWMainFileReader[source]#

Bases: object

Reader for IWFM groundwater component main file.

The GW main file is a hierarchical dispatcher that contains: 1. Version header (e.g., #4.0) 2. Paths to sub-files (BC, tile drains, pumping, subsidence) 3. Output conversion factors and units 4. Inline hydrograph location data

This reader parses the main file to extract configuration and hydrograph locations. It does NOT parse the sub-files - use the dedicated readers (e.g., GroundwaterReader.read_wells) for those.

__init__()[source]#
read(filepath, base_dir=None)[source]#

Parse GW main file, extracting config and hydrograph locations.

Parameters:
  • filepath (Path | str) – Path to the GW component main file

  • base_dir (Path | None) – Base directory for resolving relative paths. If None, uses the parent directory of filepath.

Returns:

GWMainFileConfig with parsed values

Return type:

GWMainFileConfig

pyiwfm.io.groundwater.write_groundwater(gw, output_dir, config=None)[source]#

Write groundwater component to files.

Parameters:
  • gw (AppGW) – AppGW component to write

  • output_dir (Path | str) – Output directory

  • config (GWFileConfig | None) – Optional file configuration

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

pyiwfm.io.groundwater.read_wells(filepath)[source]#

Read wells from a wells definition file.

Parameters:

filepath (Path | str) – Path to wells file

Returns:

Dictionary mapping well ID to Well object

Return type:

dict[int, Well]

pyiwfm.io.groundwater.read_initial_heads(filepath)[source]#

Read initial heads from file.

Parameters:

filepath (Path | str) – Path to initial heads file

Returns:

Tuple of (n_nodes, n_layers, heads array)

Return type:

tuple[int, int, ndarray[tuple[Any, …], dtype[float64]]]

pyiwfm.io.groundwater.read_subsidence(filepath)[source]#

Read subsidence parameters from file.

Parameters:

filepath (Path | str) – Path to subsidence parameters file

Returns:

List of Subsidence objects

Return type:

list[Subsidence]

pyiwfm.io.groundwater.read_gw_main_file(filepath, base_dir=None)[source]#

Read IWFM groundwater component main file.

The GW main file is a hierarchical dispatcher that contains paths to sub-files (boundary conditions, pumping, etc.) and inline hydrograph location data.

Parameters:
  • filepath (Path | str) – Path to the GW component main file

  • base_dir (Path | None) – Base directory for resolving relative paths. If None, uses the parent directory of filepath.

Returns:

GWMainFileConfig with parsed values including hydrograph locations

Return type:

GWMainFileConfig

Example

>>> config = read_gw_main_file("C2VSimFG_Groundwater.dat")
>>> print(f"Version: {config.version}")
>>> print(f"Hydrograph locations: {len(config.hydrograph_locations)}")
>>> if config.pumping_file:
...     wells = read_wells(config.pumping_file)

Groundwater Writer#

Writer for IWFM groundwater component files using Jinja2 templates.

Groundwater Component Writer for IWFM models.

This module provides the main writer for IWFM groundwater component files, orchestrating the writing of all groundwater-related input files including: - Main groundwater control file (GW_MAIN.dat) - Boundary conditions (BC_MAIN.dat, SpecHeadBC.dat, SpecFlowBC.dat) - Pumping files (Pump_MAIN.dat, ElemPump.dat, WellSpec.dat, TSPumping.dat) - Tile drains (TileDrain.dat) - Subsidence parameters (Subsidence.dat) - Aquifer parameters (included in GW_MAIN.dat or separate file) - Hydrograph specifications - Face flow output - Kh anomaly section - GW return flows

class pyiwfm.io.gw_writer.GWWriterConfig(output_dir, version='4.0', gw_subdir='GW', main_file='GW_MAIN.dat', bc_main_file='BC_MAIN.dat', pump_main_file='Pump_MAIN.dat', tile_drain_file='TileDrain.dat', subsidence_file='Subsidence.dat', elem_pump_file='ElemPump.dat', well_spec_file='WellSpec.dat', ts_pumping_file='TSPumping.dat', spec_head_bc_file='SpecHeadBC.dat', spec_flow_bc_file='SpecFlowBC.dat', bound_tsd_file='BoundTSD.dat', gw_budget_file='../Results/GW.hdf', gw_zbudget_file='../Results/GW_ZBud.hdf', gw_head_file='../Results/GWHeadAll.out', gw_hyd_file='../Results/GWHyd.out', gw_velocity_file='../Results/GWVelocities.out', vertical_flow_file='../Results/VerticalFlow.out', final_heads_file='../Results/FinalGWHeads.out', tecplot_head_file='../Results/TecPlotGW.out', pump_output_file='../Results/Pumping.out', face_flow_file='../Results/FaceFlows.out', td_output_file='../Results/TileDrainHyd.out', bc_output_file='../Results/BCOutput.out', length_factor=1.0, length_unit='ft.', volume_factor=2.29568e-05, volume_unit='ac.ft.', velocity_factor=1.0, velocity_unit='fpd')[source]#

Bases: BaseComponentWriterConfig

Configuration for groundwater component file writing.

Variables:
  • output_dir (Path) – Base output directory for groundwater files

  • gw_subdir (str) – Subdirectory name for groundwater files (default: “GW”)

  • version (str) – IWFM groundwater component version

gw_subdir: str = 'GW'#
main_file: str = 'GW_MAIN.dat'#
bc_main_file: str = 'BC_MAIN.dat'#
pump_main_file: str = 'Pump_MAIN.dat'#
tile_drain_file: str = 'TileDrain.dat'#
subsidence_file: str = 'Subsidence.dat'#
elem_pump_file: str = 'ElemPump.dat'#
well_spec_file: str = 'WellSpec.dat'#
ts_pumping_file: str = 'TSPumping.dat'#
spec_head_bc_file: str = 'SpecHeadBC.dat'#
spec_flow_bc_file: str = 'SpecFlowBC.dat'#
bound_tsd_file: str = 'BoundTSD.dat'#
gw_budget_file: str = '../Results/GW.hdf'#
gw_zbudget_file: str = '../Results/GW_ZBud.hdf'#
gw_head_file: str = '../Results/GWHeadAll.out'#
gw_hyd_file: str = '../Results/GWHyd.out'#
gw_velocity_file: str = '../Results/GWVelocities.out'#
vertical_flow_file: str = '../Results/VerticalFlow.out'#
final_heads_file: str = '../Results/FinalGWHeads.out'#
tecplot_head_file: str = '../Results/TecPlotGW.out'#
pump_output_file: str = '../Results/Pumping.out'#
face_flow_file: str = '../Results/FaceFlows.out'#
td_output_file: str = '../Results/TileDrainHyd.out'#
bc_output_file: str = '../Results/BCOutput.out'#
length_factor: float = 1.0#
length_unit: str = 'ft.'#
volume_factor: float = 2.29568e-05#
volume_unit: str = 'ac.ft.'#
velocity_factor: float = 1.0#
velocity_unit: str = 'fpd'#
property gw_dir: Path#

Get the groundwater subdirectory path.

property bc_main_path: Path#

Get the boundary conditions main file path.

property pump_main_path: Path#

Get the pumping main file path.

__init__(output_dir, version='4.0', gw_subdir='GW', main_file='GW_MAIN.dat', bc_main_file='BC_MAIN.dat', pump_main_file='Pump_MAIN.dat', tile_drain_file='TileDrain.dat', subsidence_file='Subsidence.dat', elem_pump_file='ElemPump.dat', well_spec_file='WellSpec.dat', ts_pumping_file='TSPumping.dat', spec_head_bc_file='SpecHeadBC.dat', spec_flow_bc_file='SpecFlowBC.dat', bound_tsd_file='BoundTSD.dat', gw_budget_file='../Results/GW.hdf', gw_zbudget_file='../Results/GW_ZBud.hdf', gw_head_file='../Results/GWHeadAll.out', gw_hyd_file='../Results/GWHyd.out', gw_velocity_file='../Results/GWVelocities.out', vertical_flow_file='../Results/VerticalFlow.out', final_heads_file='../Results/FinalGWHeads.out', tecplot_head_file='../Results/TecPlotGW.out', pump_output_file='../Results/Pumping.out', face_flow_file='../Results/FaceFlows.out', td_output_file='../Results/TileDrainHyd.out', bc_output_file='../Results/BCOutput.out', length_factor=1.0, length_unit='ft.', volume_factor=2.29568e-05, volume_unit='ac.ft.', velocity_factor=1.0, velocity_unit='fpd')#
class pyiwfm.io.gw_writer.GWComponentWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM Groundwater Component files.

Writes all groundwater-related input files for IWFM simulation.

Example

>>> from pyiwfm.io.gw_writer import GWComponentWriter, GWWriterConfig
>>> config = GWWriterConfig(output_dir=Path("model/Simulation"))
>>> writer = GWComponentWriter(model, config)
>>> files = writer.write_all()
__init__(model, config, template_engine=None)[source]#

Initialize the groundwater component writer.

Parameters:
  • model (IWFMModel) – Model to write

  • config (GWWriterConfig) – Output file configuration

  • template_engine (TemplateEngine, optional) – Custom template engine

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all groundwater files.

write_all(write_defaults=True)[source]#

Write all groundwater component files.

Parameters:

write_defaults (bool) – If True, write default files even when no groundwater component is loaded (useful for generating simulation skeleton)

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

write_main()[source]#

Write the main groundwater control file.

Returns:

Path to written file

Return type:

Path

write_bc_main()[source]#

Write the boundary conditions main file.

Returns:

Path to written file

Return type:

Path

write_pump_main()[source]#

Write the pumping main file.

Returns:

Path to written file

Return type:

Path

write_tile_drains()[source]#

Write the tile drains file.

Returns:

Path to written file

Return type:

Path

write_subsidence()[source]#

Write the subsidence parameters file.

Returns:

Path to written file

Return type:

Path

write_spec_head_bc()[source]#

Write the specified head boundary condition data file.

write_spec_flow_bc()[source]#

Write the specified flow boundary condition data file.

write_bc_ts_data()[source]#

Copy the BC time series data file from source if it exists.

write_well_specs()[source]#

Write the well specification file.

Returns:

Path to written file

Return type:

Path

write_elem_pump_specs()[source]#

Write the element-based pumping specification file.

Returns:

Path to written file

Return type:

Path

write_ts_pumping(dates=None, data=None)[source]#

Write the pumping time series data file using IWFMTimeSeriesDataWriter.

Parameters:
  • dates (list[str], optional) – IWFM timestamps

  • data (NDArray[np.float64], optional) – Pumping data array (n_times, n_cols)

Returns:

Path to written file

Return type:

Path

write_hydrograph_specs()[source]#

Write groundwater hydrograph output locations.

This is typically embedded in the main file, but can also be called standalone for verification.

Returns:

Path to main file (hydrograph specs are part of GW_MAIN)

Return type:

Path

write_face_flow_specs()[source]#

Write face flow output specifications.

This is typically embedded in the main file.

Returns:

Path to main file (face flow specs are part of GW_MAIN)

Return type:

Path

pyiwfm.io.gw_writer.write_gw_component(model, output_dir, config=None)[source]#

Write groundwater component files for a model.

Parameters:
  • model (IWFMModel) – Model to write

  • output_dir (Path or str) – Output directory

  • config (GWWriterConfig, optional) – File configuration

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

Groundwater Boundary Conditions#

Reader for groundwater boundary condition files (specified head, general head).

Groundwater Boundary Conditions Reader for IWFM.

This module reads the IWFM groundwater boundary conditions file, which contains four types of boundary conditions: 1. Specified Flow BCs (fixed flux at nodes) 2. Specified Head BCs (fixed head at nodes) 3. General Head BCs (head-dependent flow) 4. Constrained General Head BCs (head-dependent with constraints)

The main BC file is a dispatcher that references sub-files for each BC type, plus an optional time series data file for dynamic BCs.

class pyiwfm.io.gw_boundary.SpecifiedFlowBC(node_id, layer, ts_column=0, base_flow=0.0)[source]#

Bases: object

Specified flow boundary condition at a node.

Variables:
  • node_id (int) – GW node ID

  • layer (int) – Aquifer layer (1-based)

  • ts_column (int) – Time series column (0 = static)

  • base_flow (float) – Base flow value (positive = inflow)

node_id: int#
layer: int#
ts_column: int = 0#
base_flow: float = 0.0#
__init__(node_id, layer, ts_column=0, base_flow=0.0)#
class pyiwfm.io.gw_boundary.SpecifiedHeadBC(node_id, layer, ts_column=0, head_value=0.0)[source]#

Bases: object

Specified head boundary condition at a node.

Variables:
  • node_id (int) – GW node ID

  • layer (int) – Aquifer layer (1-based)

  • ts_column (int) – Time series column (0 = static)

  • head_value (float) – Head value

node_id: int#
layer: int#
ts_column: int = 0#
head_value: float = 0.0#
__init__(node_id, layer, ts_column=0, head_value=0.0)#
class pyiwfm.io.gw_boundary.GeneralHeadBC(node_id, layer, ts_column=0, external_head=0.0, conductance=0.0)[source]#

Bases: object

General head boundary condition at a node.

Flow is computed as: Q = conductance * (external_head - gw_head)

Variables:
  • node_id (int) – GW node ID

  • layer (int) – Aquifer layer (1-based)

  • ts_column (int) – Time series column for external head (0 = static)

  • external_head (float) – External head value

  • conductance (float) – BC conductance

node_id: int#
layer: int#
ts_column: int = 0#
external_head: float = 0.0#
conductance: float = 0.0#
__init__(node_id, layer, ts_column=0, external_head=0.0, conductance=0.0)#
class pyiwfm.io.gw_boundary.ConstrainedGeneralHeadBC(node_id, layer, ts_column=0, external_head=0.0, conductance=0.0, constraining_head=0.0, max_flow_ts_column=0, max_flow=0.0)[source]#

Bases: object

Constrained general head boundary condition at a node.

Like GeneralHeadBC but with a constraining head and maximum flow.

Variables:
  • node_id (int) – GW node ID

  • layer (int) – Aquifer layer (1-based)

  • ts_column (int) – Time series column for external head (0 = static)

  • external_head (float) – External head value

  • conductance (float) – BC conductance

  • constraining_head (float) – Head below which the constraining head is used

  • max_flow_ts_column (int) – Time series column for max flow (0 = static)

  • max_flow (float) – Maximum BC flow

node_id: int#
layer: int#
ts_column: int = 0#
external_head: float = 0.0#
conductance: float = 0.0#
constraining_head: float = 0.0#
max_flow_ts_column: int = 0#
max_flow: float = 0.0#
__init__(node_id, layer, ts_column=0, external_head=0.0, conductance=0.0, constraining_head=0.0, max_flow_ts_column=0, max_flow=0.0)#
class pyiwfm.io.gw_boundary.GWBoundaryConfig(sp_flow_file=None, sp_head_file=None, gh_file=None, cgh_file=None, ts_data_file=None, specified_flow_bcs=<factory>, sp_flow_factor=1.0, sp_flow_time_unit='', specified_head_bcs=<factory>, sp_head_factor=1.0, general_head_bcs=<factory>, gh_head_factor=1.0, gh_conductance_factor=1.0, gh_time_unit='', constrained_gh_bcs=<factory>, cgh_head_factor=1.0, cgh_max_flow_factor=1.0, cgh_head_time_unit='', cgh_conductance_factor=1.0, cgh_conductance_time_unit='', n_bc_output_nodes=0, bc_output_file=None, bc_output_file_raw='', bc_output_specs=<factory>)[source]#

Bases: object

Complete GW boundary conditions configuration.

Variables:
  • sp_flow_file (pathlib.Path | None) – Path to specified flow BC sub-file

  • sp_head_file (pathlib.Path | None) – Path to specified head BC sub-file

  • gh_file (pathlib.Path | None) – Path to general head BC sub-file

  • cgh_file (pathlib.Path | None) – Path to constrained general head BC sub-file

  • ts_data_file (pathlib.Path | None) – Path to time series BC data file

  • specified_flow_bcs (list[pyiwfm.io.gw_boundary.SpecifiedFlowBC]) – List of specified flow BCs

  • sp_flow_factor (float) – Conversion factor for specified flow

  • sp_flow_time_unit (str) – Time unit for specified flow

  • specified_head_bcs (list[pyiwfm.io.gw_boundary.SpecifiedHeadBC]) – List of specified head BCs

  • sp_head_factor (float) – Conversion factor for specified head

  • general_head_bcs (list[pyiwfm.io.gw_boundary.GeneralHeadBC]) – List of general head BCs

  • gh_head_factor (float) – Head conversion factor for general head

  • gh_conductance_factor (float) – Conductance conversion factor

  • gh_time_unit (str) – Time unit for general head

  • constrained_gh_bcs (list[pyiwfm.io.gw_boundary.ConstrainedGeneralHeadBC]) – List of constrained general head BCs

  • cgh_head_factor (float) – Head conversion factor

  • cgh_max_flow_factor (float) – Max flow conversion factor

  • cgh_head_time_unit (str) – Time unit for head

  • cgh_conductance_factor (float) – Conductance conversion factor

  • cgh_conductance_time_unit (str) – Time unit for conductance

sp_flow_file: Path | None = None#
sp_head_file: Path | None = None#
gh_file: Path | None = None#
cgh_file: Path | None = None#
ts_data_file: Path | None = None#
specified_flow_bcs: list[SpecifiedFlowBC]#
sp_flow_factor: float = 1.0#
sp_flow_time_unit: str = ''#
specified_head_bcs: list[SpecifiedHeadBC]#
sp_head_factor: float = 1.0#
general_head_bcs: list[GeneralHeadBC]#
gh_head_factor: float = 1.0#
gh_conductance_factor: float = 1.0#
gh_time_unit: str = ''#
constrained_gh_bcs: list[ConstrainedGeneralHeadBC]#
cgh_head_factor: float = 1.0#
cgh_max_flow_factor: float = 1.0#
cgh_head_time_unit: str = ''#
cgh_conductance_factor: float = 1.0#
cgh_conductance_time_unit: str = ''#
n_bc_output_nodes: int = 0#
bc_output_file: Path | None = None#
bc_output_file_raw: str = ''#
bc_output_specs: list[dict[str, Any]]#
property n_specified_flow: int#
property n_specified_head: int#
property n_general_head: int#
property n_constrained_gh: int#
property total_bcs: int#
__init__(sp_flow_file=None, sp_head_file=None, gh_file=None, cgh_file=None, ts_data_file=None, specified_flow_bcs=<factory>, sp_flow_factor=1.0, sp_flow_time_unit='', specified_head_bcs=<factory>, sp_head_factor=1.0, general_head_bcs=<factory>, gh_head_factor=1.0, gh_conductance_factor=1.0, gh_time_unit='', constrained_gh_bcs=<factory>, cgh_head_factor=1.0, cgh_max_flow_factor=1.0, cgh_head_time_unit='', cgh_conductance_factor=1.0, cgh_conductance_time_unit='', n_bc_output_nodes=0, bc_output_file=None, bc_output_file_raw='', bc_output_specs=<factory>)#
class pyiwfm.io.gw_boundary.GWBoundaryReader[source]#

Bases: ReaderMixin

Reader for IWFM groundwater boundary conditions files.

The main BC file contains 5 lines of sub-file paths: 1. Specified flow BC file 2. Specified head BC file 3. General head BC file 4. Constrained general head BC file 5. Time series BC data file

Each sub-file has its own header with counts and conversion factors, followed by per-BC data rows.

__init__()[source]#
read(filepath, base_dir=None)[source]#

Read BC main file and all referenced sub-files.

Parameters:
  • filepath (Path | str) – Path to main BC file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

GWBoundaryConfig with all BC data

Return type:

GWBoundaryConfig

pyiwfm.io.gw_boundary.read_gw_boundary(filepath, base_dir=None)[source]#

Read IWFM GW boundary conditions file.

Parameters:
  • filepath (Path | str) – Path to the BC main file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

GWBoundaryConfig with all boundary condition data

Return type:

GWBoundaryConfig

Groundwater Pumping#

Reader for groundwater pumping specification files.

Groundwater Pumping Reader for IWFM.

This module reads the IWFM groundwater pumping files, which define: 1. Well specifications (location, perforation intervals, radius) 2. Well pumping specifications (rates, distribution, destinations) 3. Element-based pumping specifications 4. Element groups for pumping destinations 5. Time series pumping data references

The pumping main file references sub-files for wells, element pumping, and a time series data file containing the actual pumping rates.

class pyiwfm.io.gw_pumping.WellSpec(id, x, y, radius=0.0, perf_top=0.0, perf_bottom=0.0, name='')[source]#

Bases: object

Well physical specification.

Variables:
  • id (int) – Well ID

  • x (float) – X coordinate

  • y (float) – Y coordinate

  • radius (float) – Well radius (half of diameter)

  • perf_top (float) – Perforation top elevation

  • perf_bottom (float) – Perforation bottom elevation

  • name (str) – Well name/description (from / delimiter in spec file)

id: int#
x: float#
y: float#
radius: float = 0.0#
perf_top: float = 0.0#
perf_bottom: float = 0.0#
name: str = ''#
__init__(id, x, y, radius=0.0, perf_top=0.0, perf_bottom=0.0, name='')#
class pyiwfm.io.gw_pumping.WellPumpingSpec(well_id, pump_column=0, pump_fraction=1.0, dist_method=0, dest_type=-1, dest_id=0, irig_frac_column=0, adjust_column=0, pump_max_column=0, pump_max_fraction=0.0)[source]#

Bases: object

Well pumping specification.

Variables:
  • well_id (int) – Well ID (must match a WellSpec)

  • pump_column (int) – Column in time series pumping file

  • pump_fraction (float) – Fraction of pumping at this column

  • dist_method (int) – Distribution method (0-4)

  • dest_type (int) – Destination type (-1, 0, 1, 2, 3)

  • dest_id (int) – Destination ID (element, subregion, or group)

  • irig_frac_column (int) – Column for irrigation fraction

  • adjust_column (int) – Column for supply adjustment

  • pump_max_column (int) – Column for maximum pumping

  • pump_max_fraction (float) – Fraction of maximum pumping

well_id: int#
pump_column: int = 0#
pump_fraction: float = 1.0#
dist_method: int = 0#
dest_type: int = -1#
dest_id: int = 0#
irig_frac_column: int = 0#
adjust_column: int = 0#
pump_max_column: int = 0#
pump_max_fraction: float = 0.0#
__init__(well_id, pump_column=0, pump_fraction=1.0, dist_method=0, dest_type=-1, dest_id=0, irig_frac_column=0, adjust_column=0, pump_max_column=0, pump_max_fraction=0.0)#
class pyiwfm.io.gw_pumping.ElementPumpingSpec(element_id, pump_column=0, pump_fraction=1.0, dist_method=0, layer_factors=<factory>, dest_type=-1, dest_id=0, irig_frac_column=0, adjust_column=0, pump_max_column=0, pump_max_fraction=0.0)[source]#

Bases: object

Element-based pumping specification.

Variables:
  • element_id (int) – Element ID

  • pump_column (int) – Column in time series pumping file

  • pump_fraction (float) – Fraction of pumping at this column

  • dist_method (int) – Distribution method (0-4)

  • layer_factors (list[float]) – Layer distribution factors

  • dest_type (int) – Destination type (-1, 0, 1, 2, 3)

  • dest_id (int) – Destination ID

  • irig_frac_column (int) – Column for irrigation fraction

  • adjust_column (int) – Column for supply adjustment

  • pump_max_column (int) – Column for maximum pumping

  • pump_max_fraction (float) – Fraction of maximum pumping

element_id: int#
pump_column: int = 0#
pump_fraction: float = 1.0#
dist_method: int = 0#
layer_factors: list[float]#
dest_type: int = -1#
dest_id: int = 0#
irig_frac_column: int = 0#
adjust_column: int = 0#
pump_max_column: int = 0#
pump_max_fraction: float = 0.0#
__init__(element_id, pump_column=0, pump_fraction=1.0, dist_method=0, layer_factors=<factory>, dest_type=-1, dest_id=0, irig_frac_column=0, adjust_column=0, pump_max_column=0, pump_max_fraction=0.0)#
class pyiwfm.io.gw_pumping.ElementGroup(id, elements=<factory>)[source]#

Bases: object

Group of elements for pumping destination.

Variables:
  • id (int) – Group ID

  • elements (list[int]) – List of element IDs in this group

id: int#
elements: list[int]#
__init__(id, elements=<factory>)#
class pyiwfm.io.gw_pumping.PumpingConfig(version='', well_file=None, elem_pump_file=None, ts_data_file=None, output_file=None, well_specs=<factory>, well_pumping_specs=<factory>, well_groups=<factory>, factor_xy=1.0, factor_radius=1.0, factor_length=1.0, elem_pumping_specs=<factory>, elem_groups=<factory>, pump_factor=1.0)[source]#

Bases: object

Complete pumping configuration.

Variables:
version: str = ''#
well_file: Path | None = None#
elem_pump_file: Path | None = None#
ts_data_file: Path | None = None#
output_file: Path | None = None#
well_specs: list[WellSpec]#
well_pumping_specs: list[WellPumpingSpec]#
well_groups: list[ElementGroup]#
factor_xy: float = 1.0#
factor_radius: float = 1.0#
factor_length: float = 1.0#
elem_pumping_specs: list[ElementPumpingSpec]#
elem_groups: list[ElementGroup]#
pump_factor: float = 1.0#
property n_wells: int#
property n_elem_pumping: int#
__init__(version='', well_file=None, elem_pump_file=None, ts_data_file=None, output_file=None, well_specs=<factory>, well_pumping_specs=<factory>, well_groups=<factory>, factor_xy=1.0, factor_radius=1.0, factor_length=1.0, elem_pumping_specs=<factory>, elem_groups=<factory>, pump_factor=1.0)#
class pyiwfm.io.gw_pumping.PumpingReader[source]#

Bases: ReaderMixin

Reader for IWFM pumping files.

The pumping system reads from a main file that references: - Well specification file - Element pumping specification file - Time series pumping data file

__init__()[source]#
read(filepath, base_dir=None, n_layers=1)[source]#

Read pumping main file and referenced sub-files.

Parameters:
  • filepath (Path | str) – Path to main pumping file

  • base_dir (Path | None) – Base directory for resolving relative paths

  • n_layers (int) – Number of aquifer layers (needed for element pumping)

Returns:

PumpingConfig with all pumping data

Return type:

PumpingConfig

pyiwfm.io.gw_pumping.read_gw_pumping(filepath, base_dir=None, n_layers=1)[source]#

Read IWFM GW pumping file.

Parameters:
  • filepath (Path | str) – Path to the pumping main file

  • base_dir (Path | None) – Base directory for resolving relative paths

  • n_layers (int) – Number of aquifer layers

Returns:

PumpingConfig with all pumping data

Return type:

PumpingConfig

Groundwater Tile Drains#

Reader for groundwater tile drain specification files.

Groundwater Tile Drain and Sub-Irrigation Reader for IWFM.

This module reads the IWFM tile drain/sub-irrigation file, which contains: 1. Tile drain specifications (node, elevation, conductance, destination) 2. Sub-irrigation specifications (node, elevation, conductance)

Both are in the same input file: tile drains first, then sub-irrigation.

class pyiwfm.io.gw_tiledrain.TileDrainSpec(id, gw_node, elevation, conductance, dest_type=1, dest_id=0)[source]#

Bases: object

Tile drain specification.

Variables:
  • id (int) – Tile drain ID

  • gw_node (int) – GW node ID

  • elevation (float) – Drain elevation

  • conductance (float) – Drain conductance

  • dest_type (int) – Destination type (1=outside, 2=stream node)

  • dest_id (int) – Destination ID (stream node number if dest_type=2)

id: int#
gw_node: int#
elevation: float#
conductance: float#
dest_type: int = 1#
dest_id: int = 0#
__init__(id, gw_node, elevation, conductance, dest_type=1, dest_id=0)#
class pyiwfm.io.gw_tiledrain.SubIrrigationSpec(id, gw_node, elevation, conductance)[source]#

Bases: object

Sub-irrigation (subsurface irrigation) specification.

Variables:
  • id (int) – Sub-irrigation ID

  • gw_node (int) – GW node ID

  • elevation (float) – Sub-irrigation elevation

  • conductance (float) – Conductance

id: int#
gw_node: int#
elevation: float#
conductance: float#
__init__(id, gw_node, elevation, conductance)#
class pyiwfm.io.gw_tiledrain.TileDrainHydroSpec(id, id_type=1, name='')[source]#

Bases: object

Tile drain / sub-irrigation hydrograph output specification.

Variables:
  • id (int) – Tile drain or sub-irrigation ID

  • id_type (int) – 1 = tile drain, 2 = sub-irrigation

  • name (str) – Hydrograph name

id: int#
id_type: int = 1#
name: str = ''#
__init__(id, id_type=1, name='')#
class pyiwfm.io.gw_tiledrain.TileDrainConfig(version='', n_drains=0, drain_height_factor=1.0, drain_conductance_factor=1.0, drain_time_unit='', tile_drains=<factory>, n_sub_irrigation=0, subirig_height_factor=1.0, subirig_conductance_factor=1.0, subirig_time_unit='', sub_irrigations=<factory>, n_td_hydro=0, td_hydro_volume_factor=1.0, td_hydro_volume_unit='', td_output_file='', td_hydro_specs=<factory>)[source]#

Bases: object

Complete tile drain and sub-irrigation configuration.

Variables:
  • version (str) – File format version (e.g., “4.0”)

  • n_drains (int) – Number of tile drains

  • drain_height_factor (float) – Height conversion factor for drains

  • drain_conductance_factor (float) – Conductance conversion factor for drains

  • drain_time_unit (str) – Time unit for drain conductance

  • tile_drains (list[pyiwfm.io.gw_tiledrain.TileDrainSpec]) – List of tile drain specifications

  • n_sub_irrigation (int) – Number of sub-irrigation locations

  • subirig_height_factor (float) – Height conversion factor

  • subirig_conductance_factor (float) – Conductance conversion factor

  • subirig_time_unit (str) – Time unit for sub-irrigation conductance

  • sub_irrigations (list[pyiwfm.io.gw_tiledrain.SubIrrigationSpec]) – List of sub-irrigation specifications

  • n_td_hydro (int) – Number of hydrograph outputs

  • td_hydro_volume_factor (float) – Volume conversion factor for output

  • td_hydro_volume_unit (str) – Volume output unit

  • td_output_file (str) – Output file path (raw string from file)

  • td_hydro_specs (list[pyiwfm.io.gw_tiledrain.TileDrainHydroSpec]) – List of hydrograph output specifications

version: str = ''#
n_drains: int = 0#
drain_height_factor: float = 1.0#
drain_conductance_factor: float = 1.0#
drain_time_unit: str = ''#
tile_drains: list[TileDrainSpec]#
n_sub_irrigation: int = 0#
subirig_height_factor: float = 1.0#
subirig_conductance_factor: float = 1.0#
subirig_time_unit: str = ''#
sub_irrigations: list[SubIrrigationSpec]#
n_td_hydro: int = 0#
td_hydro_volume_factor: float = 1.0#
td_hydro_volume_unit: str = ''#
td_output_file: str = ''#
td_hydro_specs: list[TileDrainHydroSpec]#
__init__(version='', n_drains=0, drain_height_factor=1.0, drain_conductance_factor=1.0, drain_time_unit='', tile_drains=<factory>, n_sub_irrigation=0, subirig_height_factor=1.0, subirig_conductance_factor=1.0, subirig_time_unit='', sub_irrigations=<factory>, n_td_hydro=0, td_hydro_volume_factor=1.0, td_hydro_volume_unit='', td_output_file='', td_hydro_specs=<factory>)#
class pyiwfm.io.gw_tiledrain.TileDrainReader[source]#

Bases: ReaderMixin

Reader for IWFM tile drain/sub-irrigation file.

The file contains tile drains first, then sub-irrigation data.

__init__()[source]#
read(filepath)[source]#

Read tile drain and sub-irrigation file.

Parameters:

filepath (Path | str) – Path to the tile drain file

Returns:

TileDrainConfig with all data

Return type:

TileDrainConfig

pyiwfm.io.gw_tiledrain.read_gw_tiledrain(filepath)[source]#

Read IWFM tile drain/sub-irrigation file.

Parameters:

filepath (Path | str) – Path to the tile drain file

Returns:

TileDrainConfig with all data

Return type:

TileDrainConfig

Groundwater Subsidence#

Reader for groundwater subsidence parameter files.

Groundwater Subsidence Reader for IWFM.

This module reads the IWFM subsidence parameter files, which define compaction-related parameters for each aquifer node-layer. Four versions are supported:

  • Version 4.0: Basic subsidence with elastic/inelastic storage, interbed thickness, and pre-compaction head.

  • Version 4.1: Same as 4.0 but adds AllSubsidenceAtAllNodes HDF5 output file path (read before IC filename).

  • Version 5.0: Enhanced with vertical interbed conductivity, number of equivalent delay interbeds, and preferred discretization thickness.

  • Version 5.1: Same as 5.0 but adds AllSubsidenceAtAllNodes HDF5 output file path (read before IC filename).

class pyiwfm.io.gw_subsidence.SubsidenceHydrographSpec(id=0, hydtyp=0, layer=1, x=0.0, y=0.0, name='')[source]#

Bases: object

Subsidence hydrograph output location.

Variables:
  • id (int) – Hydrograph output ID (1-based)

  • hydtyp (int) – Hydrograph type (0=x-y coord, 1=node number)

  • layer (int) – Output layer

  • x (float) – X coordinate (if hydtyp=0)

  • y (float) – Y coordinate (if hydtyp=0)

  • name (str) – Optional name/description

id: int = 0#
hydtyp: int = 0#
layer: int = 1#
x: float = 0.0#
y: float = 0.0#
name: str = ''#
__init__(id=0, hydtyp=0, layer=1, x=0.0, y=0.0, name='')#
class pyiwfm.io.gw_subsidence.SubsidenceNodeParams(node_id=0, elastic_sc=<factory>, inelastic_sc=<factory>, interbed_thick=<factory>, interbed_thick_min=<factory>, precompact_head=<factory>, kv_sub=<factory>, n_eq=<factory>)[source]#

Bases: object

Subsidence parameters for one node across all layers.

Variables:
  • node_id (int) – GW node ID

  • elastic_sc (list[float]) – Elastic storage coefficient per layer

  • inelastic_sc (list[float]) – Inelastic storage coefficient per layer

  • interbed_thick (list[float]) – Interbed thickness per layer

  • interbed_thick_min (list[float]) – Minimum interbed thickness per layer

  • precompact_head (list[float]) – Pre-compaction head per layer

  • kv_sub (list[float]) – Vertical conductivity of interbeds per layer (v5.0 only)

  • n_eq (list[float]) – Number of equivalent delay interbeds per layer (v5.0 only)

node_id: int = 0#
elastic_sc: list[float]#
inelastic_sc: list[float]#
interbed_thick: list[float]#
interbed_thick_min: list[float]#
precompact_head: list[float]#
kv_sub: list[float]#
n_eq: list[float]#
__init__(node_id=0, elastic_sc=<factory>, inelastic_sc=<factory>, interbed_thick=<factory>, interbed_thick_min=<factory>, precompact_head=<factory>, kv_sub=<factory>, n_eq=<factory>)#
class pyiwfm.io.gw_subsidence.ParametricSubsidenceData(node_range_str, n_nodes, n_elements, elements, node_coords, node_values)[source]#

Bases: object

Raw parametric grid data for subsidence parameters.

Variables:
  • node_range_str (str) – Original node range string (e.g. "1-441").

  • n_nodes (int) – Number of parametric grid nodes.

  • n_elements (int) – Number of parametric grid elements.

  • elements (list[tuple[int, ...]]) – Element vertex index tuples (0-based into node arrays).

  • node_coords (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Parametric node coordinates, shape (n_nodes, 2).

  • node_values (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Parameter values per node, shape (n_nodes, n_layers, n_params). The 5 params are: ElasticSC, InelasticSC, InterbedThick, InterbedThickMin, PreCompactHead.

node_range_str: str#
n_nodes: int#
n_elements: int#
elements: list[tuple[int, ...]]#
node_coords: ndarray[tuple[Any, ...], dtype[float64]]#
node_values: ndarray[tuple[Any, ...], dtype[float64]]#
__init__(node_range_str, n_nodes, n_elements, elements, node_coords, node_values)#
class pyiwfm.io.gw_subsidence.SubsidenceConfig(version='', all_subs_out_file=None, ic_file=None, tecplot_file=None, final_subs_file=None, output_factor=1.0, output_unit='FEET', interbed_dz=0.0, n_parametric_grids=0, conversion_factors=<factory>, n_hydrograph_outputs=0, hydrograph_coord_factor=1.0, hydrograph_output_file=None, hydrograph_specs=<factory>, _raw_all_subs_out_file='', _raw_ic_file='', _raw_tecplot_file='', _raw_final_subs_file='', _raw_hydrograph_output_file='', parametric_grids=<factory>, node_params=<factory>, n_nodes=0, n_layers=0, ic_factor=1.0, ic_interbed_thick=None, ic_precompact_head=None)[source]#

Bases: object

Complete subsidence configuration.

Variables:
  • version (str) – File format version (4.0, 4.1, 5.0, or 5.1)

  • all_subs_out_file (pathlib.Path | None) – Path to AllSubsidenceAtAllNodes HDF5 output (v4.1/v5.1 only)

  • ic_file (pathlib.Path | None) – Path to initial conditions file

  • tecplot_file (pathlib.Path | None) – Path to Tecplot output file

  • final_subs_file (pathlib.Path | None) – Path to end-of-simulation output file

  • output_factor (float) – Output unit conversion factor

  • output_unit (str) – Output unit string

  • interbed_dz (float) – Preferred interbed discretization thickness (v5.0 only)

  • n_parametric_grids (int) – Number of parametric grids (0 = direct input)

  • conversion_factors (list[float]) – Conversion factor array (6 for v4.0, 7 for v5.0)

  • node_params (list[pyiwfm.io.gw_subsidence.SubsidenceNodeParams]) – List of SubsidenceNodeParams (one per node)

  • n_nodes (int) – Number of nodes with subsidence data

  • n_layers (int) – Number of aquifer layers

  • ic_factor (float) – IC file conversion factor

  • ic_interbed_thick (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]] | None) – IC interbed thickness array (n_nodes, n_layers)

  • ic_precompact_head (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]] | None) – IC pre-compaction head array (n_nodes, n_layers)

version: str = ''#
all_subs_out_file: Path | None = None#
ic_file: Path | None = None#
tecplot_file: Path | None = None#
final_subs_file: Path | None = None#
output_factor: float = 1.0#
output_unit: str = 'FEET'#
interbed_dz: float = 0.0#
n_parametric_grids: int = 0#
conversion_factors: list[float]#
n_hydrograph_outputs: int = 0#
hydrograph_coord_factor: float = 1.0#
hydrograph_output_file: Path | None = None#
hydrograph_specs: list[SubsidenceHydrographSpec]#
parametric_grids: list[ParametricSubsidenceData]#
node_params: list[SubsidenceNodeParams]#
n_nodes: int = 0#
n_layers: int = 0#
ic_factor: float = 1.0#
ic_interbed_thick: ndarray[tuple[Any, ...], dtype[float64]] | None = None#
ic_precompact_head: ndarray[tuple[Any, ...], dtype[float64]] | None = None#
__init__(version='', all_subs_out_file=None, ic_file=None, tecplot_file=None, final_subs_file=None, output_factor=1.0, output_unit='FEET', interbed_dz=0.0, n_parametric_grids=0, conversion_factors=<factory>, n_hydrograph_outputs=0, hydrograph_coord_factor=1.0, hydrograph_output_file=None, hydrograph_specs=<factory>, _raw_all_subs_out_file='', _raw_ic_file='', _raw_tecplot_file='', _raw_final_subs_file='', _raw_hydrograph_output_file='', parametric_grids=<factory>, node_params=<factory>, n_nodes=0, n_layers=0, ic_factor=1.0, ic_interbed_thick=None, ic_precompact_head=None)#
class pyiwfm.io.gw_subsidence.SubsidenceReader[source]#

Bases: ReaderMixin

Reader for IWFM subsidence parameter files.

Supports versions 4.0, 4.1, 5.0, and 5.1. Version is auto-detected from the file header. Versions 4.1 and 5.1 add an AllSubsidenceAtAllNodes HDF5 output file path before the IC filename.

__init__()[source]#
read(filepath, base_dir=None, n_nodes=0, n_layers=0)[source]#

Read subsidence parameter file.

Parameters:
  • filepath (Path | str) – Path to the subsidence file

  • base_dir (Path | None) – Base directory for resolving relative paths

  • n_nodes (int) – Number of GW nodes (needed for reading parameters)

  • n_layers (int) – Number of aquifer layers

Returns:

SubsidenceConfig with all subsidence data

Return type:

SubsidenceConfig

pyiwfm.io.gw_subsidence.read_gw_subsidence(filepath, base_dir=None, n_nodes=0, n_layers=0)[source]#

Read IWFM GW subsidence parameter file.

Parameters:
  • filepath (Path | str) – Path to the subsidence file

  • base_dir (Path | None) – Base directory for resolving relative paths

  • n_nodes (int) – Number of GW nodes

  • n_layers (int) – Number of aquifer layers

Returns:

SubsidenceConfig with all subsidence data

Return type:

SubsidenceConfig

Stream I/O#

Streams Module#

The streams module provides readers and writers for IWFM stream network component files.

Stream network I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM stream network component files including stream nodes, reaches, diversions, bypasses, and rating curves.

class pyiwfm.io.streams.StreamBedParamRow(node_id, conductivity=0.0, bed_thickness=0.0, wetted_perimeter=None, gw_node=0)[source]#

Bases: object

Per-node stream bed parameters from the main file.

v4.2 column order: IR, WETPR, IRGW, CSTRM, DSTRM (5 columns) v4.0 column order: IR, CSTRM, DSTRM, WETPR (4 columns) v4.1/v5.0: IR, CSTRM, DSTRM (3 columns)

node_id: int#
conductivity: float = 0.0#
bed_thickness: float = 0.0#
wetted_perimeter: float | None = None#
gw_node: int = 0#
__init__(node_id, conductivity=0.0, bed_thickness=0.0, wetted_perimeter=None, gw_node=0)#
class pyiwfm.io.streams.CrossSectionRow(node_id, bottom_elev=0.0, B0=0.0, s=0.0, n=0.04, max_flow_depth=10.0)[source]#

Bases: object

Per-node v5.0 cross-section data from the main file.

node_id: int#
bottom_elev: float = 0.0#
B0: float = 0.0#
s: float = 0.0#
n: float = 0.04#
max_flow_depth: float = 10.0#
__init__(node_id, bottom_elev=0.0, B0=0.0, s=0.0, n=0.04, max_flow_depth=10.0)#
class pyiwfm.io.streams.StreamInitialConditionRow(node_id, value=0.0)[source]#

Bases: object

Per-node v5.0 initial condition.

node_id: int#
value: float = 0.0#
__init__(node_id, value=0.0)#
class pyiwfm.io.streams.StreamFileConfig(output_dir, stream_nodes_file='stream_nodes.dat', reaches_file='reaches.dat', diversions_file='diversions.dat', bypasses_file='bypasses.dat', rating_curves_file='rating_curves.dat', inflows_file='stream_inflows.dat')[source]#

Bases: object

Configuration for stream component files.

Variables:
  • output_dir (pathlib.Path) – Directory for output files

  • stream_nodes_file (str) – Stream nodes file name

  • reaches_file (str) – Reaches definition file name

  • diversions_file (str) – Diversions file name

  • bypasses_file (str) – Bypasses file name

  • rating_curves_file (str) – Rating curves file name

  • inflows_file (str) – Inflow time series file name

output_dir: Path#
stream_nodes_file: str = 'stream_nodes.dat'#
reaches_file: str = 'reaches.dat'#
diversions_file: str = 'diversions.dat'#
bypasses_file: str = 'bypasses.dat'#
rating_curves_file: str = 'rating_curves.dat'#
inflows_file: str = 'stream_inflows.dat'#
get_stream_nodes_path()[source]#
get_reaches_path()[source]#
get_diversions_path()[source]#
get_bypasses_path()[source]#
get_rating_curves_path()[source]#
get_inflows_path()[source]#
__init__(output_dir, stream_nodes_file='stream_nodes.dat', reaches_file='reaches.dat', diversions_file='diversions.dat', bypasses_file='bypasses.dat', rating_curves_file='rating_curves.dat', inflows_file='stream_inflows.dat')#
class pyiwfm.io.streams.StreamWriter(config)[source]#

Bases: object

Writer for IWFM stream network component files.

Writes all stream-related input files including nodes, reaches, diversions, bypasses, and rating curves.

Example

>>> config = StreamFileConfig(output_dir=Path("./model"))
>>> writer = StreamWriter(config)
>>> files = writer.write(stream_component)
__init__(config)[source]#

Initialize the stream writer.

Parameters:

config (StreamFileConfig) – File configuration

write(stream)[source]#

Write all stream component files.

Parameters:

stream (AppStream) – AppStream component to write

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

write_stream_nodes(stream, header=None)[source]#

Write stream nodes file.

Parameters:
  • stream (AppStream) – AppStream component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_reaches(stream, header=None)[source]#

Write reaches definition file.

Parameters:
  • stream (AppStream) – AppStream component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_diversions(stream, header=None)[source]#

Write diversions file.

Parameters:
  • stream (AppStream) – AppStream component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_bypasses(stream, header=None)[source]#

Write bypasses file.

Parameters:
  • stream (AppStream) – AppStream component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_rating_curves(stream, header=None)[source]#

Write rating curves file.

Parameters:
  • stream (AppStream) – AppStream component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_inflows_timeseries(filepath, times, inflows, node_ids=None, units='CFS', factor=1.0, header=None)[source]#

Write stream inflows time series file.

Parameters:
  • filepath (Path | str) – Output file path

  • times (Sequence[datetime]) – Sequence of datetime values

  • inflows (dict[int, ndarray[tuple[Any, ...], dtype[float64]]]) – Dictionary mapping node ID to inflow array

  • node_ids (list[int] | None) – Order of node IDs (default: sorted)

  • units (str) – Units string

  • factor (float) – Conversion factor

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

class pyiwfm.io.streams.StreamReader[source]#

Bases: object

Reader for IWFM stream network component files.

read_stream_nodes(filepath)[source]#

Read stream nodes from file.

Parameters:

filepath (Path | str) – Path to stream nodes file

Returns:

Dictionary mapping node ID to StrmNode object

Return type:

dict[int, StrmNode]

read_diversions(filepath)[source]#

Read diversions from file.

Parameters:

filepath (Path | str) – Path to diversions file

Returns:

Dictionary mapping diversion ID to Diversion object

Return type:

dict[int, Diversion]

class pyiwfm.io.streams.StreamMainFileConfig(version='', inflow_file=None, diversion_spec_file=None, bypass_spec_file=None, diversion_file=None, budget_output_file=None, diversion_budget_file=None, hydrograph_count=0, hydrograph_output_type=0, hydrograph_output_file=None, hydrograph_specs=<factory>, final_flow_file=None, hydrograph_flow_factor=1.0, hydrograph_flow_unit='', hydrograph_elev_factor=1.0, hydrograph_elev_unit='', node_budget_count=0, node_budget_output_file=None, node_budget_ids=<factory>, conductivity_factor=1.0, conductivity_time_unit='', length_factor=1.0, bed_params=<factory>, interaction_type=None, evap_area_file=None, evap_node_specs=<factory>, roughness_factor=1.0, cross_section_length_factor=1.0, cross_section_data=<factory>, ic_type=0, ic_time_unit='', ic_factor=1.0, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from Stream component main file.

The stream component main file is a dispatcher that references sub-files for inflows, diversions, and bypasses. It also contains inline hydrograph output specifications.

Variables:
  • version (str) – File format version (e.g., “4.2”)

  • inflow_file (pathlib.Path | None) – Path to stream inflow time series file

  • diversion_spec_file (pathlib.Path | None) – Path to diversion specifications file

  • bypass_spec_file (pathlib.Path | None) – Path to bypass specifications file

  • diversion_file (pathlib.Path | None) – Path to diversion time series file

  • budget_output_file (pathlib.Path | None) – Path to stream reach budget output

  • diversion_budget_file (pathlib.Path | None) – Path to diversion detail budget output

  • hydrograph_count (int) – Number of hydrograph output locations

  • hydrograph_output_type (int) – 0=flow, 1=stage, 2=both

  • hydrograph_output_file (pathlib.Path | None) – Path to hydrograph output file

  • hydrograph_specs (list[tuple[int, str]]) – List of (node_id, name) tuples for output locations

version: str = ''#
inflow_file: Path | None = None#
diversion_spec_file: Path | None = None#
bypass_spec_file: Path | None = None#
diversion_file: Path | None = None#
budget_output_file: Path | None = None#
diversion_budget_file: Path | None = None#
hydrograph_count: int = 0#
hydrograph_output_type: int = 0#
hydrograph_output_file: Path | None = None#
hydrograph_specs: list[tuple[int, str]]#
final_flow_file: Path | None = None#
hydrograph_flow_factor: float = 1.0#
hydrograph_flow_unit: str = ''#
hydrograph_elev_factor: float = 1.0#
hydrograph_elev_unit: str = ''#
node_budget_count: int = 0#
node_budget_output_file: Path | None = None#
node_budget_ids: list[int]#
conductivity_factor: float = 1.0#
conductivity_time_unit: str = ''#
length_factor: float = 1.0#
bed_params: list[StreamBedParamRow]#
interaction_type: int | None = None#
evap_area_file: Path | None = None#
evap_node_specs: list[tuple[int, int, int]]#
roughness_factor: float = 1.0#
cross_section_length_factor: float = 1.0#
cross_section_data: list[CrossSectionRow]#
ic_type: int = 0#
ic_time_unit: str = ''#
ic_factor: float = 1.0#
initial_conditions: list[StreamInitialConditionRow]#
__init__(version='', inflow_file=None, diversion_spec_file=None, bypass_spec_file=None, diversion_file=None, budget_output_file=None, diversion_budget_file=None, hydrograph_count=0, hydrograph_output_type=0, hydrograph_output_file=None, hydrograph_specs=<factory>, final_flow_file=None, hydrograph_flow_factor=1.0, hydrograph_flow_unit='', hydrograph_elev_factor=1.0, hydrograph_elev_unit='', node_budget_count=0, node_budget_output_file=None, node_budget_ids=<factory>, conductivity_factor=1.0, conductivity_time_unit='', length_factor=1.0, bed_params=<factory>, interaction_type=None, evap_area_file=None, evap_node_specs=<factory>, roughness_factor=1.0, cross_section_length_factor=1.0, cross_section_data=<factory>, ic_type=0, ic_time_unit='', ic_factor=1.0, initial_conditions=<factory>)#
class pyiwfm.io.streams.StreamMainFileReader[source]#

Bases: object

Reader for IWFM stream component main file.

The Stream main file is a hierarchical dispatcher that contains: 1. Version header (e.g., #4.2) 2. Paths to sub-files (inflows, diversions, bypasses) 3. Output file paths 4. Inline hydrograph output specifications

__init__()[source]#
read(filepath, base_dir=None)[source]#

Parse Stream main file, extracting config and hydrograph specs.

Parameters:
  • filepath (Path | str) – Path to the Stream component main file

  • base_dir (Path | None) – Base directory for resolving relative paths. If None, uses the parent directory of filepath.

Returns:

StreamMainFileConfig with parsed values

Return type:

StreamMainFileConfig

class pyiwfm.io.streams.StreamReachSpec(id, n_nodes, outflow_node=0, name='', node_ids=<factory>, node_to_gw_node=<factory>, node_rating_tables=<factory>, node_bottom_elevations=<factory>)[source]#

Bases: object

Stream reach specification from preprocessor StreamsSpec file.

Contains the reach definition including node-to-GW-node mappings for stream-aquifer interaction.

Variables:
  • id (int) – Reach ID

  • n_nodes (int) – Number of stream nodes in this reach

  • outflow_node (int) – Outflow destination (0=boundary, -n=lake n, +n=reach n)

  • name (str) – Reach name/description

  • node_ids (list[int]) – List of stream node IDs in this reach

  • node_to_gw_node (dict[int, int]) – Mapping of stream_node_id -> gw_node_id

  • node_rating_tables (dict[int, tuple[list[float], list[float]]]) – Maps stream_node_id -> (stages, flows)

  • node_bottom_elevations (dict[int, float]) – Maps stream_node_id -> bottom elevation

id: int#
n_nodes: int#
outflow_node: int = 0#
name: str = ''#
node_ids: list[int]#
node_to_gw_node: dict[int, int]#
node_rating_tables: dict[int, tuple[list[float], list[float]]]#
node_bottom_elevations: dict[int, float]#
__init__(id, n_nodes, outflow_node=0, name='', node_ids=<factory>, node_to_gw_node=<factory>, node_rating_tables=<factory>, node_bottom_elevations=<factory>)#
class pyiwfm.io.streams.StreamSpecReader[source]#

Bases: object

Reader for IWFM preprocessor StreamsSpec file.

The StreamsSpec file defines the stream network geometry including: - Number of reaches and rating table points - Reach definitions with node lists - Stream-GW node mappings for each stream node

__init__()[source]#
read(filepath)[source]#

Parse StreamsSpec file.

Parameters:

filepath (Path | str) – Path to the StreamsSpec file

Returns:

Tuple of (n_reaches, n_rating_points, list of reach specs)

Return type:

tuple[int, int, list[StreamReachSpec]]

pyiwfm.io.streams.write_stream(stream, output_dir, config=None)[source]#

Write stream component to files.

Parameters:
  • stream (AppStream) – AppStream component to write

  • output_dir (Path | str) – Output directory

  • config (StreamFileConfig | None) – Optional file configuration

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

pyiwfm.io.streams.read_stream_nodes(filepath)[source]#

Read stream nodes from file.

Parameters:

filepath (Path | str) – Path to stream nodes file

Returns:

Dictionary mapping node ID to StrmNode object

Return type:

dict[int, StrmNode]

pyiwfm.io.streams.read_diversions(filepath)[source]#

Read diversions from file.

Parameters:

filepath (Path | str) – Path to diversions file

Returns:

Dictionary mapping diversion ID to Diversion object

Return type:

dict[int, Diversion]

pyiwfm.io.streams.read_stream_main_file(filepath, base_dir=None)[source]#

Read IWFM stream component main file.

The Stream main file is a hierarchical dispatcher that contains paths to sub-files (inflows, diversions, bypasses) and inline hydrograph output specifications.

Parameters:
  • filepath (Path | str) – Path to the Stream component main file

  • base_dir (Path | None) – Base directory for resolving relative paths. If None, uses the parent directory of filepath.

Returns:

StreamMainFileConfig with parsed values

Return type:

StreamMainFileConfig

Example

>>> config = read_stream_main_file("C2VSimFG_Streams.dat")
>>> print(f"Version: {config.version}")
>>> print(f"Hydrograph outputs: {config.hydrograph_count}")
pyiwfm.io.streams.read_stream_spec(filepath)[source]#

Read IWFM preprocessor StreamsSpec file.

The StreamsSpec file defines the stream network geometry including reach definitions and stream-GW node mappings.

Parameters:

filepath (Path | str) – Path to the StreamsSpec file

Returns:

Tuple of (n_reaches, n_rating_points, list of StreamReachSpec)

Return type:

tuple[int, int, list[StreamReachSpec]]

Example

>>> n_reaches, n_rtb, reaches = read_stream_spec("StreamsSpec.dat")
>>> print(f"Loaded {n_reaches} reaches")
>>> for reach in reaches:
...     print(f"  Reach {reach.id}: {reach.n_nodes} nodes")

Stream Writer#

Writer for IWFM stream component files using Jinja2 templates.

Stream Component Writer for IWFM models.

This module provides the main writer for IWFM stream component files, orchestrating the writing of all stream-related input files including: - Main stream control file (Stream_MAIN.dat) - Stream inflows time series - Diversion specifications - Bypass specifications - Stream bed parameters

class pyiwfm.io.stream_writer.StreamWriterConfig(output_dir, version='4.0', stream_subdir='Stream', main_file='Stream_MAIN.dat', inflow_file='StreamInflow.dat', diver_specs_file='DiverSpecs.dat', bypass_specs_file='BypassSpecs.dat', diversions_file='Diversions.dat', strm_budget_file='../Results/StrmBud.hdf', strm_node_budget_file='../Results/StrmNodeBud.hdf', strm_hyd_file='../Results/StrmHyd.out', diver_detail_file='../Results/DiverDetail.hdf', flow_factor=2.2957e-05, flow_unit='ac.ft./day', length_factor=1.0, length_unit='ft', conductivity=10.0, bed_thickness=1.0, wetted_perimeter=150.0, conductivity_factor=1.0, conductivity_time_unit='1day', bed_length_factor=1.0, interaction_type=1, final_flow_file='', roughness_factor=1.0, cross_section_length_factor=1.0, ic_type=0, ic_time_unit='', ic_factor=1.0, evap_area_file='')[source]#

Bases: BaseComponentWriterConfig

Configuration for stream component file writing.

Variables:
  • output_dir (Path) – Base output directory for stream files

  • stream_subdir (str) – Subdirectory name for stream files (default: “Stream”)

  • version (str) – IWFM stream component version

stream_subdir: str = 'Stream'#
main_file: str = 'Stream_MAIN.dat'#
inflow_file: str = 'StreamInflow.dat'#
diver_specs_file: str = 'DiverSpecs.dat'#
bypass_specs_file: str = 'BypassSpecs.dat'#
diversions_file: str = 'Diversions.dat'#
strm_budget_file: str = '../Results/StrmBud.hdf'#
strm_node_budget_file: str = '../Results/StrmNodeBud.hdf'#
strm_hyd_file: str = '../Results/StrmHyd.out'#
diver_detail_file: str = '../Results/DiverDetail.hdf'#
flow_factor: float = 2.2957e-05#
flow_unit: str = 'ac.ft./day'#
length_factor: float = 1.0#
length_unit: str = 'ft'#
conductivity: float = 10.0#
bed_thickness: float = 1.0#
wetted_perimeter: float = 150.0#
conductivity_factor: float = 1.0#
conductivity_time_unit: str = '1day'#
bed_length_factor: float = 1.0#
interaction_type: int = 1#
final_flow_file: str = ''#
roughness_factor: float = 1.0#
cross_section_length_factor: float = 1.0#
ic_type: int = 0#
ic_time_unit: str = ''#
ic_factor: float = 1.0#
evap_area_file: str = ''#
property stream_dir: Path#

Get the stream subdirectory path.

__init__(output_dir, version='4.0', stream_subdir='Stream', main_file='Stream_MAIN.dat', inflow_file='StreamInflow.dat', diver_specs_file='DiverSpecs.dat', bypass_specs_file='BypassSpecs.dat', diversions_file='Diversions.dat', strm_budget_file='../Results/StrmBud.hdf', strm_node_budget_file='../Results/StrmNodeBud.hdf', strm_hyd_file='../Results/StrmHyd.out', diver_detail_file='../Results/DiverDetail.hdf', flow_factor=2.2957e-05, flow_unit='ac.ft./day', length_factor=1.0, length_unit='ft', conductivity=10.0, bed_thickness=1.0, wetted_perimeter=150.0, conductivity_factor=1.0, conductivity_time_unit='1day', bed_length_factor=1.0, interaction_type=1, final_flow_file='', roughness_factor=1.0, cross_section_length_factor=1.0, ic_type=0, ic_time_unit='', ic_factor=1.0, evap_area_file='')#
class pyiwfm.io.stream_writer.StreamComponentWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM Stream Component files.

Writes all stream-related input files for IWFM simulation.

Example

>>> from pyiwfm.io.stream_writer import StreamComponentWriter, StreamWriterConfig
>>> config = StreamWriterConfig(output_dir=Path("model/Simulation"))
>>> writer = StreamComponentWriter(model, config)
>>> files = writer.write_all()
__init__(model, config, template_engine=None)[source]#

Initialize the stream component writer.

Parameters:
  • model (IWFMModel) – Model to write

  • config (StreamWriterConfig) – Output file configuration

  • template_engine (TemplateEngine, optional) – Custom template engine

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all stream files.

write_all(write_defaults=True)[source]#

Write all stream component files.

Parameters:

write_defaults (bool) – If True, write default files even when no stream component is loaded (useful for generating simulation skeleton)

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

write_main()[source]#

Write the main stream control file.

Returns:

Path to written file

Return type:

Path

write_diver_specs()[source]#

Write the diversion specifications file.

Writes IWFM 14-column (no spills) diversion specification format including NDIVER, per-diversion parameters, element groups, and recharge zones.

Returns:

Path to written file

Return type:

Path

write_bypass_specs()[source]#

Write the bypass specifications file.

IWFM format: - NBYPASS, FACTX, TUNITX, FACTY, TUNITY (header) - Per bypass: ID, IA, TYPEDEST, DEST, IDIVC, DIVRL, DIVNL, NAME - Rating table rows (DIVX, DIVY) if IDIVC < 0 - Seepage/recharge zone data for each bypass

Returns:

Path to written file

Return type:

Path

write_stream_inflow_ts(dates=None, data=None, column_mapping=None)[source]#

Write the stream inflow time series data file.

Parameters:
  • dates (list[str], optional) – IWFM timestamps

  • data (NDArray[np.float64], optional) – Inflow data array (n_times, n_cols)

  • column_mapping (list[str], optional) – Column mapping rows (ID, IRST)

Returns:

Path to written file

Return type:

Path

write_diversion_data_ts(dates=None, data=None)[source]#

Write the diversion data time series file.

Parameters:
  • dates (list[str], optional) – IWFM timestamps

  • data (NDArray[np.float64], optional) – Diversion data array (n_times, n_cols)

Returns:

Path to written file

Return type:

Path

write_surface_area_ts(dates=None, data=None)[source]#

Write the stream surface area time series file.

Parameters:
  • dates (list[str], optional) – IWFM timestamps

  • data (NDArray[np.float64], optional) – Surface area data array (n_times, n_cols)

Returns:

Path to written file

Return type:

Path

pyiwfm.io.stream_writer.write_stream_component(model, output_dir, config=None)[source]#

Write stream component files for a model.

Parameters:
  • model (IWFMModel) – Model to write

  • output_dir (Path or str) – Output directory

  • config (StreamWriterConfig, optional) – File configuration

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

Stream Diversion#

Reader for stream diversion specification files.

Stream Diversion Specification Reader for IWFM.

This module reads the IWFM diversion specification file, which defines diversion definitions with source nodes, delivery destinations, element groups, and recharge zone destinations for recoverable losses.

The file format supports two variants: 14-column (legacy, without spill fields) and 16-column (with spill column and fraction).

Reference: Class_Diversion.f90 - Diversion_New()

class pyiwfm.io.stream_diversion.DiversionSpec(id=0, stream_node=0, max_diver_col=0, frac_max_diver=1.0, recv_loss_col=0, frac_recv_loss=0.0, non_recv_loss_col=0, frac_non_recv_loss=0.0, spill_col=0, frac_spill=0.0, dest_type=3, dest_id=0, delivery_col=0, frac_delivery=1.0, irrig_frac_col=0, adjustment_col=0, name='')[source]#

Bases: object

Specification for a single stream diversion.

Variables:
  • id (int) – Diversion ID

  • stream_node (int) – Stream node where diversion originates (0=outside model)

  • max_diver_col (int) – Column in diversions file for max diversion rate

  • frac_max_diver (float) – Fraction of max diversion to apply

  • recv_loss_col (int) – Column for recoverable loss (0=not used)

  • frac_recv_loss (float) – Fraction of recoverable loss

  • non_recv_loss_col (int) – Column for non-recoverable loss (0=not used)

  • frac_non_recv_loss (float) – Fraction of non-recoverable loss

  • spill_col (int) – Column for spill rates (0=not used, only in 16-col format)

  • frac_spill (float) – Fraction of spill (only in 16-col format)

  • dest_type (int) – Delivery destination type (1=element, 2=subregion, 3=outside, 4=elementset)

  • dest_id (int) – Destination element/subregion/set ID

  • delivery_col (int) – Column for delivery amounts (0=not used)

  • frac_delivery (float) – Fraction of delivery

  • irrig_frac_col (int) – Column for irrigation fraction

  • adjustment_col (int) – Column for adjustment data

  • name (str) – Diversion name (up to 20 chars)

id: int = 0#
stream_node: int = 0#
max_diver_col: int = 0#
frac_max_diver: float = 1.0#
recv_loss_col: int = 0#
frac_recv_loss: float = 0.0#
non_recv_loss_col: int = 0#
frac_non_recv_loss: float = 0.0#
spill_col: int = 0#
frac_spill: float = 0.0#
dest_type: int = 3#
dest_id: int = 0#
delivery_col: int = 0#
frac_delivery: float = 1.0#
irrig_frac_col: int = 0#
adjustment_col: int = 0#
name: str = ''#
__init__(id=0, stream_node=0, max_diver_col=0, frac_max_diver=1.0, recv_loss_col=0, frac_recv_loss=0.0, non_recv_loss_col=0, frac_non_recv_loss=0.0, spill_col=0, frac_spill=0.0, dest_type=3, dest_id=0, delivery_col=0, frac_delivery=1.0, irrig_frac_col=0, adjustment_col=0, name='')#
class pyiwfm.io.stream_diversion.ElementGroup(id=0, elements=<factory>)[source]#

Bases: object

A group of elements used as a diversion delivery destination.

Variables:
  • id (int) – Group ID

  • elements (list[int]) – List of element IDs in this group

id: int = 0#
elements: list[int]#
__init__(id=0, elements=<factory>)#
class pyiwfm.io.stream_diversion.RechargeZoneDest(diversion_id=0, n_zones=0, zone_ids=<factory>, zone_fractions=<factory>)[source]#

Bases: object

Recharge zone destination for recoverable loss or spill.

Variables:
  • diversion_id (int) – ID of the associated diversion

  • n_zones (int) – Number of recharge zone destinations

  • zone_ids (list[int]) – List of zone IDs

  • zone_fractions (list[float]) – List of fractions for each zone

diversion_id: int = 0#
n_zones: int = 0#
zone_ids: list[int]#
zone_fractions: list[float]#
__init__(diversion_id=0, n_zones=0, zone_ids=<factory>, zone_fractions=<factory>)#
class pyiwfm.io.stream_diversion.DiversionSpecConfig(n_diversions=0, diversions=<factory>, n_element_groups=0, element_groups=<factory>, recharge_zones=<factory>, spill_zones=<factory>, has_spills=False)[source]#

Bases: object

Complete diversion specification configuration.

Variables:
n_diversions: int = 0#
diversions: list[DiversionSpec]#
n_element_groups: int = 0#
element_groups: list[ElementGroup]#
recharge_zones: list[RechargeZoneDest]#
spill_zones: list[RechargeZoneDest]#
has_spills: bool = False#
__init__(n_diversions=0, diversions=<factory>, n_element_groups=0, element_groups=<factory>, recharge_zones=<factory>, spill_zones=<factory>, has_spills=False)#
class pyiwfm.io.stream_diversion.DiversionSpecReader[source]#

Bases: ReaderMixin

Reader for IWFM diversion specification files.

Supports both 14-column (legacy) and 16-column (with spills) formats. Auto-detects the format based on the number of columns in the first diversion data line.

__init__()[source]#
read(filepath)[source]#

Read diversion specification file.

Parameters:

filepath (Path | str) – Path to the diversion spec file

Returns:

DiversionSpecConfig with all diversion data

Return type:

DiversionSpecConfig

pyiwfm.io.stream_diversion.read_diversion_spec(filepath)[source]#

Read IWFM diversion specification file.

Parameters:

filepath (Path | str) – Path to the diversion spec file

Returns:

DiversionSpecConfig with all diversion data

Return type:

DiversionSpecConfig

Stream Bypass#

Reader for stream bypass specification files.

Stream Bypass Specification Reader for IWFM.

This module reads the IWFM bypass specification file, which defines conversion factors for flow and bypass values, bypass definitions with source/destination nodes, rating tables, and recoverable/non-recoverable loss fractions. Bypass destination types are 1 (stream node) or 2 (lake).

Reference: Class_Bypass.f90 - Bypass_New()

class pyiwfm.io.stream_bypass.BypassRatingTable(flows=<factory>, fractions=<factory>)[source]#

Bases: object

Rating table for a bypass (inline definition).

Variables:
flows: ndarray[tuple[Any, ...], dtype[float64]]#
fractions: ndarray[tuple[Any, ...], dtype[float64]]#
__init__(flows=<factory>, fractions=<factory>)#
class pyiwfm.io.stream_bypass.BypassSpec(id=0, export_stream_node=0, dest_type=1, dest_id=0, rating_table_col=0, frac_recoverable=0.0, frac_non_recoverable=0.0, name='', inline_rating=None)[source]#

Bases: object

Specification for a single bypass.

Variables:
  • id (int) – Bypass ID

  • export_stream_node (int) – Stream node where bypass originates (0=outside model)

  • dest_type (int) – Destination type (1=stream node, 2=lake)

  • dest_id (int) – Destination stream node ID or lake ID

  • rating_table_col (int) – Column in diversions file for rating. Positive means pre-defined rating from diversions file, negative means abs(col) inline rating table points, zero means no rating table.

  • frac_recoverable (float) – Fraction of bypass that is recoverable loss

  • frac_non_recoverable (float) – Fraction of bypass that is non-recoverable loss

  • name (str) – Bypass name (up to 20 chars)

  • inline_rating (pyiwfm.io.stream_bypass.BypassRatingTable | None) – Inline rating table (if rating_table_col < 0)

id: int = 0#
export_stream_node: int = 0#
dest_type: int = 1#
dest_id: int = 0#
rating_table_col: int = 0#
frac_recoverable: float = 0.0#
frac_non_recoverable: float = 0.0#
name: str = ''#
inline_rating: BypassRatingTable | None = None#
__init__(id=0, export_stream_node=0, dest_type=1, dest_id=0, rating_table_col=0, frac_recoverable=0.0, frac_non_recoverable=0.0, name='', inline_rating=None)#
class pyiwfm.io.stream_bypass.BypassSeepageZone(bypass_id=0, n_elements=0, element_ids=<factory>, element_fractions=<factory>)[source]#

Bases: object

Seepage/recharge zone for a bypass.

Variables:
  • bypass_id (int) – ID of the associated bypass

  • n_elements (int) – Number of elements receiving recharge

  • element_ids (list[int]) – List of element IDs

  • element_fractions (list[float]) – List of fractions for each element

bypass_id: int = 0#
n_elements: int = 0#
element_ids: list[int]#
element_fractions: list[float]#
__init__(bypass_id=0, n_elements=0, element_ids=<factory>, element_fractions=<factory>)#
class pyiwfm.io.stream_bypass.BypassSpecConfig(n_bypasses=0, flow_factor=1.0, flow_time_unit='', bypass_factor=1.0, bypass_time_unit='', bypasses=<factory>, seepage_zones=<factory>)[source]#

Bases: object

Complete bypass specification configuration.

Variables:
n_bypasses: int = 0#
flow_factor: float = 1.0#
flow_time_unit: str = ''#
bypass_factor: float = 1.0#
bypass_time_unit: str = ''#
bypasses: list[BypassSpec]#
seepage_zones: list[BypassSeepageZone]#
__init__(n_bypasses=0, flow_factor=1.0, flow_time_unit='', bypass_factor=1.0, bypass_time_unit='', bypasses=<factory>, seepage_zones=<factory>)#
class pyiwfm.io.stream_bypass.BypassSpecReader[source]#

Bases: ReaderMixin

Reader for IWFM bypass specification files.

The bypass file defines flow routing that bypasses portions of the stream network, with optional inline rating tables.

__init__()[source]#
read(filepath)[source]#

Read bypass specification file.

Parameters:

filepath (Path | str) – Path to the bypass spec file

Returns:

BypassSpecConfig with all bypass data

Return type:

BypassSpecConfig

pyiwfm.io.stream_bypass.read_bypass_spec(filepath)[source]#

Read IWFM bypass specification file.

Parameters:

filepath (Path | str) – Path to the bypass spec file

Returns:

BypassSpecConfig with all bypass data

Return type:

BypassSpecConfig

Stream Inflow#

Reader for stream inflow data files.

Stream Inflow Reader for IWFM.

This module reads the IWFM stream inflow file header, which maps inflow time-series columns to stream nodes. The actual time-series data follows in standard IWFM time-series format.

The header contains: 1. Conversion factor for all inflow values 2. Time unit 3. Number of inflow series 4. Per-series: [InflowID] StreamNodeID mapping

Reference: Class_StrmInflow.f90 - New()

class pyiwfm.io.stream_inflow.InflowSpec(inflow_id=0, stream_node=0)[source]#

Bases: object

Specification for a single inflow point.

Variables:
  • inflow_id (int) – Inflow identifier (may be auto-numbered)

  • stream_node (int) – Stream node receiving inflow (0=no inflow)

inflow_id: int = 0#
stream_node: int = 0#
__init__(inflow_id=0, stream_node=0)#
class pyiwfm.io.stream_inflow.InflowConfig(conversion_factor=1.0, time_unit='', n_inflows=0, inflow_specs=<factory>)[source]#

Bases: object

Configuration from stream inflow file header.

Variables:
  • conversion_factor (float) – Multiplication factor for all inflow values

  • time_unit (str) – Time unit string (e.g., “MONTH”, “DAY”)

  • n_inflows (int) – Number of inflow time series

  • inflow_specs (list[pyiwfm.io.stream_inflow.InflowSpec]) – List of inflow specifications (column-to-node mapping)

conversion_factor: float = 1.0#
time_unit: str = ''#
n_inflows: int = 0#
inflow_specs: list[InflowSpec]#
property inflow_nodes: list[int]#

Return list of stream nodes receiving inflows (non-zero only).

__init__(conversion_factor=1.0, time_unit='', n_inflows=0, inflow_specs=<factory>)#
class pyiwfm.io.stream_inflow.InflowReader[source]#

Bases: ReaderMixin

Reader for IWFM stream inflow file header.

Reads only the header (conversion factor, time unit, number of series, and per-series node mapping). Does not parse the time-series data.

__init__()[source]#
read(filepath)[source]#

Read stream inflow file header.

Parameters:

filepath (Path | str) – Path to the stream inflow file

Returns:

InflowConfig with header data

Return type:

InflowConfig

pyiwfm.io.stream_inflow.read_stream_inflow(filepath)[source]#

Read IWFM stream inflow file header.

Parameters:

filepath (Path | str) – Path to the stream inflow file

Returns:

InflowConfig with header data

Return type:

InflowConfig

Lake I/O#

Lakes Module#

The lakes module provides readers and writers for IWFM lake component files.

Lake component I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM lake component files including lake definitions, lake elements, rating curves, and outflows.

class pyiwfm.io.lakes.LakeFileConfig(output_dir, lakes_file='lakes.dat', lake_elements_file='lake_elements.dat', rating_curves_file='lake_rating_curves.dat', outflows_file='lake_outflows.dat')[source]#

Bases: object

Configuration for lake component files.

Variables:
  • output_dir (pathlib.Path) – Directory for output files

  • lakes_file (str) – Lake definitions file name

  • lake_elements_file (str) – Lake elements file name

  • rating_curves_file (str) – Lake rating curves file name

  • outflows_file (str) – Lake outflows file name

output_dir: Path#
lakes_file: str = 'lakes.dat'#
lake_elements_file: str = 'lake_elements.dat'#
rating_curves_file: str = 'lake_rating_curves.dat'#
outflows_file: str = 'lake_outflows.dat'#
get_lakes_path()[source]#
get_lake_elements_path()[source]#
get_rating_curves_path()[source]#
get_outflows_path()[source]#
__init__(output_dir, lakes_file='lakes.dat', lake_elements_file='lake_elements.dat', rating_curves_file='lake_rating_curves.dat', outflows_file='lake_outflows.dat')#
class pyiwfm.io.lakes.LakeWriter(config)[source]#

Bases: object

Writer for IWFM lake component files.

Writes all lake-related input files including definitions, elements, rating curves, and outflows.

Example

>>> config = LakeFileConfig(output_dir=Path("./model"))
>>> writer = LakeWriter(config)
>>> files = writer.write(lake_component)
__init__(config)[source]#

Initialize the lake writer.

Parameters:

config (LakeFileConfig) – File configuration

write(lakes)[source]#

Write all lake component files.

Parameters:

lakes (AppLake) – AppLake component to write

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

write_lake_definitions(lakes, header=None)[source]#

Write lake definitions file.

Parameters:
  • lakes (AppLake) – AppLake component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_lake_elements(lakes, header=None)[source]#

Write lake elements file.

Parameters:
  • lakes (AppLake) – AppLake component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_rating_curves(lakes, header=None)[source]#

Write lake rating curves file.

Parameters:
  • lakes (AppLake) – AppLake component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_outflows(lakes, header=None)[source]#

Write lake outflows file.

Parameters:
  • lakes (AppLake) – AppLake component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

class pyiwfm.io.lakes.LakeReader[source]#

Bases: object

Reader for IWFM lake component files.

read_lake_definitions(filepath)[source]#

Read lake definitions from file.

Parameters:

filepath (Path | str) – Path to lakes file

Returns:

Dictionary mapping lake ID to Lake object

Return type:

dict[int, Lake]

read_lake_elements(filepath)[source]#

Read lake elements from file.

Parameters:

filepath (Path | str) – Path to lake elements file

Returns:

List of LakeElement objects

Return type:

list[LakeElement]

read_rating_curves(filepath)[source]#

Read lake rating curves from file.

Parameters:

filepath (Path | str) – Path to rating curves file

Returns:

Dictionary mapping lake ID to LakeRating object

Return type:

dict[int, LakeRating]

class pyiwfm.io.lakes.LakeParamSpec(lake_id=0, conductance_coeff=0.0, depth_denom=1.0, max_elev_col=0, et_col=0, precip_col=0, name='')[source]#

Bases: object

Per-lake parameters from the simulation lake main file.

Variables:
  • lake_id (int) – Lake ID

  • conductance_coeff (float) – Dimensionless conductance coefficient (CLAKE)

  • depth_denom (float) – Lake depth denominator (DLAKE)

  • max_elev_col (int) – Column index in max elevation file

  • et_col (int) – Column index in ET time series file

  • precip_col (int) – Column index in precipitation file

  • name (str) – Lake identification name

lake_id: int = 0#
conductance_coeff: float = 0.0#
depth_denom: float = 1.0#
max_elev_col: int = 0#
et_col: int = 0#
precip_col: int = 0#
name: str = ''#
__init__(lake_id=0, conductance_coeff=0.0, depth_denom=1.0, max_elev_col=0, et_col=0, precip_col=0, name='')#
class pyiwfm.io.lakes.OutflowRatingPoint(elevation=0.0, outflow=0.0)[source]#

Bases: object

A single point in a lake outflow rating table (v5.0).

Variables:
  • elevation (float) – Water surface elevation

  • outflow (float) – Outflow rate at this elevation

elevation: float = 0.0#
outflow: float = 0.0#
__init__(elevation=0.0, outflow=0.0)#
class pyiwfm.io.lakes.LakeOutflowRating(lake_id=0, points=<factory>)[source]#

Bases: object

Outflow rating table for a lake (v5.0 only).

Variables:
lake_id: int = 0#
points: list[OutflowRatingPoint]#
__init__(lake_id=0, points=<factory>)#
class pyiwfm.io.lakes.LakeMainFileConfig(version='', max_elev_file=None, budget_output_file=None, final_elev_file=None, conductance_factor=1.0, conductance_time_unit='', depth_factor=1.0, lake_params=<factory>, elev_factor=1.0, outflow_factor=1.0, outflow_time_unit='', outflow_ratings=<factory>)[source]#

Bases: object

Configuration parsed from Lake component main simulation file.

The lake main file configures lake hydrological parameters and data source file paths for the simulation.

Variables:
  • version (str) – File format version (e.g., “4.0” or “5.0”)

  • max_elev_file (pathlib.Path | None) – Path to max lake elevation time series file

  • budget_output_file (pathlib.Path | None) – Path to lake budget output file

  • final_elev_file (pathlib.Path | None) – Path to end-of-simulation elevation output file

  • conductance_factor (float) – Conductance factor K (FactK)

  • conductance_time_unit (str) – Time unit for conductance

  • depth_factor (float) – Lake depth parameter L (FactL)

  • lake_params (list[pyiwfm.io.lakes.LakeParamSpec]) – Per-lake parameter specifications

  • elev_factor (float) – Elevation conversion factor (v5.0 only)

  • outflow_factor (float) – Outflow rate conversion factor (v5.0 only)

  • outflow_time_unit (str) – Time unit for outflow rating tables (v5.0 only)

  • outflow_ratings (list[pyiwfm.io.lakes.LakeOutflowRating]) – Outflow rating tables per lake (v5.0 only)

version: str = ''#
max_elev_file: Path | None = None#
budget_output_file: Path | None = None#
final_elev_file: Path | None = None#
conductance_factor: float = 1.0#
conductance_time_unit: str = ''#
depth_factor: float = 1.0#
lake_params: list[LakeParamSpec]#
elev_factor: float = 1.0#
outflow_factor: float = 1.0#
outflow_time_unit: str = ''#
outflow_ratings: list[LakeOutflowRating]#
__init__(version='', max_elev_file=None, budget_output_file=None, final_elev_file=None, conductance_factor=1.0, conductance_time_unit='', depth_factor=1.0, lake_params=<factory>, elev_factor=1.0, outflow_factor=1.0, outflow_time_unit='', outflow_ratings=<factory>)#
class pyiwfm.io.lakes.LakeMainFileReader[source]#

Bases: object

Reader for IWFM lake component main simulation file.

The Lake main file is a hierarchical dispatcher that contains: 1. Version header (e.g., #4.0) 2. Paths to sub-files (max elevation TS, budget output, final elevation) 3. Conductance and depth parameters 4. Per-lake parameter lines 5. (v5.0) Outflow rating tables

Supports both version 4.0 and 5.0 formats.

__init__()[source]#
read(filepath, base_dir=None)[source]#

Parse Lake main file.

Parameters:
  • filepath (Path | str) – Path to the Lake component main file

  • base_dir (Path | None) – Base directory for resolving relative paths. If None, uses the parent directory of filepath.

Returns:

LakeMainFileConfig with parsed values

Return type:

LakeMainFileConfig

pyiwfm.io.lakes.read_lake_main_file(filepath, base_dir=None)[source]#

Read IWFM lake component main simulation file.

Parameters:
  • filepath (Path | str) – Path to the Lake component main file

  • base_dir (Path | None) – Base directory for resolving relative paths.

Returns:

LakeMainFileConfig with parsed values

Return type:

LakeMainFileConfig

pyiwfm.io.lakes.write_lakes(lakes, output_dir, config=None)[source]#

Write lake component to files.

Parameters:
  • lakes (AppLake) – AppLake component to write

  • output_dir (Path | str) – Output directory

  • config (LakeFileConfig | None) – Optional file configuration

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

pyiwfm.io.lakes.read_lake_definitions(filepath)[source]#

Read lake definitions from file.

Parameters:

filepath (Path | str) – Path to lakes file

Returns:

Dictionary mapping lake ID to Lake object

Return type:

dict[int, Lake]

pyiwfm.io.lakes.read_lake_elements(filepath)[source]#

Read lake elements from file.

Parameters:

filepath (Path | str) – Path to lake elements file

Returns:

List of LakeElement objects

Return type:

list[LakeElement]

Lake Writer#

Writer for IWFM lake component files using Jinja2 templates.

Lake Component Writer for IWFM models.

This module provides the main writer for IWFM lake component files, orchestrating the writing of all lake-related input files including: - Main lake control file (Lake_MAIN.dat) for v4.0 and v5.0 - Maximum lake elevation time series (v4.0) - Lake budget output configuration - v5.0 outflow rating tables

class pyiwfm.io.lake_writer.LakeWriterConfig(output_dir, version='4.0', lake_subdir='Lake', main_file='Lake_MAIN.dat', max_elev_file='MaxLakeElev.dat', lake_budget_file='../Results/LakeBud.hdf', final_elev_file='../Results/FinalLakeElev.out', conductivity_factor=1.0, conductivity_time_unit='1day', length_factor=1.0, rating_elev_factor=1.0, rating_flow_factor=1.0, rating_flow_time_unit='1DAY', bed_conductivity=2.0, bed_thickness=1.0)[source]#

Bases: BaseComponentWriterConfig

Configuration for lake component file writing.

Variables:
  • output_dir (Path) – Base output directory for lake files

  • lake_subdir (str) – Subdirectory name for lake files (default: “Lake”)

  • version (str) – IWFM lake component version

lake_subdir: str = 'Lake'#
main_file: str = 'Lake_MAIN.dat'#
max_elev_file: str = 'MaxLakeElev.dat'#
lake_budget_file: str = '../Results/LakeBud.hdf'#
final_elev_file: str = '../Results/FinalLakeElev.out'#
conductivity_factor: float = 1.0#
conductivity_time_unit: str = '1day'#
length_factor: float = 1.0#
rating_elev_factor: float = 1.0#
rating_flow_factor: float = 1.0#
rating_flow_time_unit: str = '1DAY'#
bed_conductivity: float = 2.0#
bed_thickness: float = 1.0#
property lake_dir: Path#

Get the lake subdirectory path.

__init__(output_dir, version='4.0', lake_subdir='Lake', main_file='Lake_MAIN.dat', max_elev_file='MaxLakeElev.dat', lake_budget_file='../Results/LakeBud.hdf', final_elev_file='../Results/FinalLakeElev.out', conductivity_factor=1.0, conductivity_time_unit='1day', length_factor=1.0, rating_elev_factor=1.0, rating_flow_factor=1.0, rating_flow_time_unit='1DAY', bed_conductivity=2.0, bed_thickness=1.0)#
class pyiwfm.io.lake_writer.LakeComponentWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM Lake Component files.

Writes all lake-related input files for IWFM simulation.

Example

>>> from pyiwfm.io.lake_writer import LakeComponentWriter, LakeWriterConfig
>>> config = LakeWriterConfig(output_dir=Path("model/Simulation"))
>>> writer = LakeComponentWriter(model, config)
>>> files = writer.write_all()
__init__(model, config, template_engine=None)[source]#

Initialize the lake component writer.

Parameters:
  • model (IWFMModel) – Model to write

  • config (LakeWriterConfig) – Output file configuration

  • template_engine (TemplateEngine, optional) – Custom template engine

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all lake files.

write_all(write_defaults=True)[source]#

Write all lake component files.

Parameters:

write_defaults (bool) – If True, write default files even when no lake component is loaded (useful for generating simulation skeleton)

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

write_main()[source]#

Write the main lake control file.

Returns:

Path to written file

Return type:

Path

write_max_lake_elev_ts(dates=None, data=None)[source]#

Write the maximum lake elevation time series file (v4.0).

Parameters:
  • dates (list[str], optional) – IWFM timestamps

  • data (NDArray[np.float64], optional) – Max elevation data array (n_times, n_lakes)

Returns:

Path to written file

Return type:

Path

pyiwfm.io.lake_writer.write_lake_component(model, output_dir, config=None)[source]#

Write lake component files for a model.

Parameters:
  • model (IWFMModel) – Model to write

  • output_dir (Path or str) – Output directory

  • config (LakeWriterConfig, optional) – File configuration

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

Root Zone I/O#

Root Zone Module#

The root zone module provides readers and writers for IWFM root zone component files.

Root zone component I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM root zone component files including crop types, soil parameters, land use assignments, and soil moisture data.

class pyiwfm.io.rootzone.ElementSoilParamRow(element_id, wilting_point, field_capacity, total_porosity, lambda_param, hydraulic_conductivity, kunsat_method, precip_column, precip_factor, generic_moisture_column, surface_flow_dest_type=0, surface_flow_dest_id=0, k_ponded=-1.0, capillary_rise=0.0, dest_ag=0, dest_urban_in=0, dest_urban_out=0, dest_nvrv=0)[source]#

Bases: object

Per-element soil parameters from the root zone main file.

Column layout varies by version — see RootZoneMainFileReader._read_element_soil_params().

element_id: int#
wilting_point: float#
field_capacity: float#
total_porosity: float#
lambda_param: float#
hydraulic_conductivity: float#
kunsat_method: int#
precip_column: int#
precip_factor: float#
generic_moisture_column: int#
surface_flow_dest_type: int = 0#
surface_flow_dest_id: int = 0#
k_ponded: float = -1.0#
capillary_rise: float = 0.0#
dest_ag: int = 0#
dest_urban_in: int = 0#
dest_urban_out: int = 0#
dest_nvrv: int = 0#
__init__(element_id, wilting_point, field_capacity, total_porosity, lambda_param, hydraulic_conductivity, kunsat_method, precip_column, precip_factor, generic_moisture_column, surface_flow_dest_type=0, surface_flow_dest_id=0, k_ponded=-1.0, capillary_rise=0.0, dest_ag=0, dest_urban_in=0, dest_urban_out=0, dest_nvrv=0)#
class pyiwfm.io.rootzone.RootZoneFileConfig(output_dir, crop_types_file='crop_types.dat', soil_params_file='soil_params.dat', landuse_file='landuse.dat', ag_landuse_file='ag_landuse.dat', urban_landuse_file='urban_landuse.dat', native_landuse_file='native_landuse.dat', soil_moisture_file='initial_soil_moisture.dat')[source]#

Bases: object

Configuration for root zone component files.

Variables:
  • output_dir (pathlib.Path) – Directory for output files

  • crop_types_file (str) – Crop types file name

  • soil_params_file (str) – Soil parameters file name

  • landuse_file (str) – Land use assignments file name

  • ag_landuse_file (str) – Agricultural land use file name

  • urban_landuse_file (str) – Urban land use file name

  • native_landuse_file (str) – Native/riparian land use file name

  • soil_moisture_file (str) – Initial soil moisture file name

output_dir: Path#
crop_types_file: str = 'crop_types.dat'#
soil_params_file: str = 'soil_params.dat'#
landuse_file: str = 'landuse.dat'#
ag_landuse_file: str = 'ag_landuse.dat'#
urban_landuse_file: str = 'urban_landuse.dat'#
native_landuse_file: str = 'native_landuse.dat'#
soil_moisture_file: str = 'initial_soil_moisture.dat'#
get_crop_types_path()[source]#
get_soil_params_path()[source]#
get_landuse_path()[source]#
get_ag_landuse_path()[source]#
get_urban_landuse_path()[source]#
get_native_landuse_path()[source]#
get_soil_moisture_path()[source]#
__init__(output_dir, crop_types_file='crop_types.dat', soil_params_file='soil_params.dat', landuse_file='landuse.dat', ag_landuse_file='ag_landuse.dat', urban_landuse_file='urban_landuse.dat', native_landuse_file='native_landuse.dat', soil_moisture_file='initial_soil_moisture.dat')#
class pyiwfm.io.rootzone.RootZoneWriter(config)[source]#

Bases: object

Writer for IWFM root zone component files.

Writes all root zone-related input files including crop types, soil parameters, and land use assignments.

Example

>>> config = RootZoneFileConfig(output_dir=Path("./model"))
>>> writer = RootZoneWriter(config)
>>> files = writer.write(rootzone_component)
__init__(config)[source]#

Initialize the root zone writer.

Parameters:

config (RootZoneFileConfig) – File configuration

write(rootzone)[source]#

Write all root zone component files.

Parameters:

rootzone (RootZone) – RootZone component to write

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

write_crop_types(rootzone, header=None)[source]#

Write crop types file.

Parameters:
  • rootzone (RootZone) – RootZone component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_soil_params(rootzone, header=None)[source]#

Write soil parameters file.

Parameters:
  • rootzone (RootZone) – RootZone component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_landuse(rootzone, header=None)[source]#

Write land use assignments file.

Parameters:
  • rootzone (RootZone) – RootZone component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

write_soil_moisture(rootzone, header=None)[source]#

Write initial soil moisture file.

Parameters:
  • rootzone (RootZone) – RootZone component

  • header (str | None) – Optional header comment

Returns:

Path to written file

Return type:

Path

class pyiwfm.io.rootzone.RootZoneReader[source]#

Bases: object

Reader for IWFM root zone component files.

read_crop_types(filepath)[source]#

Read crop types from file.

Parameters:

filepath (Path | str) – Path to crop types file

Returns:

Dictionary mapping crop ID to CropType object

Return type:

dict[int, CropType]

read_soil_params(filepath)[source]#

Read soil parameters from file.

Parameters:

filepath (Path | str) – Path to soil parameters file

Returns:

Dictionary mapping element ID to SoilParameters object

Return type:

dict[int, SoilParameters]

class pyiwfm.io.rootzone.RootZoneMainFileConfig(version='', convergence_tolerance=1e-08, max_iterations=2000, length_conversion=1.0, gw_uptake_enabled=False, nonponded_crop_file=None, ponded_crop_file=None, urban_file=None, native_veg_file=None, return_flow_file=None, reuse_file=None, irrigation_period_file=None, generic_moisture_file=None, ag_water_demand_file=None, lwu_budget_file=None, rz_budget_file=None, lwu_zone_budget_file=None, rz_zone_budget_file=None, lu_area_scale_file=None, final_moisture_file=None, k_factor=1.0, k_exdth_factor=1.0, k_time_unit='', surface_flow_dest_file=None, element_soil_params=<factory>)[source]#

Bases: object

Configuration parsed from RootZone component main file.

The root zone component main file is a dispatcher that contains solver parameters and paths to sub-files for crop, urban, and native vegetation data.

Variables:
  • version (str) – File format version (e.g., “4.0”, “4.11”)

  • convergence_tolerance (float) – Root zone solver convergence tolerance

  • max_iterations (int) – Maximum solver iterations

  • length_conversion (float) – Length unit conversion factor (FACTCN)

  • gw_uptake_enabled (bool) – Whether ET from GW is enabled (v4.11+ only)

  • nonponded_crop_file (pathlib.Path | None) – Path to non-ponded agricultural crop file

  • ponded_crop_file (pathlib.Path | None) – Path to ponded agricultural crop file

  • urban_file (pathlib.Path | None) – Path to urban land use file

  • native_veg_file (pathlib.Path | None) – Path to native/riparian vegetation file

  • return_flow_file (pathlib.Path | None) – Path to return flow fractions file

  • reuse_file (pathlib.Path | None) – Path to water reuse specifications file

  • irrigation_period_file (pathlib.Path | None) – Path to irrigation period file

  • generic_moisture_file (pathlib.Path | None) – Path to generic moisture data file

  • ag_water_demand_file (pathlib.Path | None) – Path to agricultural water supply requirement file

  • lwu_budget_file (pathlib.Path | None) – Path to land and water use budget output

  • rz_budget_file (pathlib.Path | None) – Path to root zone budget output

  • lwu_zone_budget_file (pathlib.Path | None) – Path to LWU zone budget output (v4.11+ only)

  • rz_zone_budget_file (pathlib.Path | None) – Path to RZ zone budget output (v4.11+ only)

  • lu_area_scale_file (pathlib.Path | None) – Path to land use area scaling output (v4.11+ only)

  • final_moisture_file (pathlib.Path | None) – Path to end-of-simulation soil moisture output

version: str = ''#
convergence_tolerance: float = 1e-08#
max_iterations: int = 2000#
length_conversion: float = 1.0#
gw_uptake_enabled: bool = False#
nonponded_crop_file: Path | None = None#
ponded_crop_file: Path | None = None#
urban_file: Path | None = None#
native_veg_file: Path | None = None#
return_flow_file: Path | None = None#
reuse_file: Path | None = None#
irrigation_period_file: Path | None = None#
generic_moisture_file: Path | None = None#
ag_water_demand_file: Path | None = None#
lwu_budget_file: Path | None = None#
rz_budget_file: Path | None = None#
lwu_zone_budget_file: Path | None = None#
rz_zone_budget_file: Path | None = None#
lu_area_scale_file: Path | None = None#
final_moisture_file: Path | None = None#
k_factor: float = 1.0#
k_exdth_factor: float = 1.0#
k_time_unit: str = ''#
surface_flow_dest_file: Path | None = None#
element_soil_params: list[ElementSoilParamRow]#
__init__(version='', convergence_tolerance=1e-08, max_iterations=2000, length_conversion=1.0, gw_uptake_enabled=False, nonponded_crop_file=None, ponded_crop_file=None, urban_file=None, native_veg_file=None, return_flow_file=None, reuse_file=None, irrigation_period_file=None, generic_moisture_file=None, ag_water_demand_file=None, lwu_budget_file=None, rz_budget_file=None, lwu_zone_budget_file=None, rz_zone_budget_file=None, lu_area_scale_file=None, final_moisture_file=None, k_factor=1.0, k_exdth_factor=1.0, k_time_unit='', surface_flow_dest_file=None, element_soil_params=<factory>)#
class pyiwfm.io.rootzone.RootZoneMainFileReader[source]#

Bases: object

Reader for IWFM rootzone component main file.

The RootZone main file is a hierarchical dispatcher that contains: 1. Version header (e.g., #4.11) 2. Solver parameters (convergence, iterations) 3. Paths to sub-files for different land use types 4. Output file paths

__init__()[source]#
read(filepath, base_dir=None, n_elements=0)[source]#

Parse RootZone main file.

Supports v4.0 through v4.13 formats. v4.1+ adds capillary rise; v4.11+ adds ET-from-GW flag and zone budget files; v4.12+ adds per-landuse surface flow destinations.

Parameters:
  • filepath (Path | str) – Path to the RootZone component main file

  • base_dir (Path | None) – Base directory for resolving relative paths. If None, uses the parent directory of filepath.

  • n_elements (int) – Expected number of elements (from mesh). If > 0, the soil parameter parser will read exactly this many rows instead of relying on column-count heuristics.

Returns:

RootZoneMainFileConfig with parsed values

Return type:

RootZoneMainFileConfig

pyiwfm.io.rootzone.write_rootzone(rootzone, output_dir, config=None)[source]#

Write root zone component to files.

Parameters:
  • rootzone (RootZone) – RootZone component to write

  • output_dir (Path | str) – Output directory

  • config (RootZoneFileConfig | None) – Optional file configuration

Returns:

Dictionary mapping file type to output path

Return type:

dict[str, Path]

pyiwfm.io.rootzone.read_crop_types(filepath)[source]#

Read crop types from file.

Parameters:

filepath (Path | str) – Path to crop types file

Returns:

Dictionary mapping crop ID to CropType object

Return type:

dict[int, CropType]

pyiwfm.io.rootzone.read_soil_params(filepath)[source]#

Read soil parameters from file.

Parameters:

filepath (Path | str) – Path to soil parameters file

Returns:

Dictionary mapping element ID to SoilParameters object

Return type:

dict[int, SoilParameters]

pyiwfm.io.rootzone.read_rootzone_main_file(filepath, base_dir=None)[source]#

Read IWFM rootzone component main file.

The RootZone main file is a hierarchical dispatcher that contains solver parameters and paths to sub-files for different land use types.

Parameters:
  • filepath (Path | str) – Path to the RootZone component main file

  • base_dir (Path | None) – Base directory for resolving relative paths. If None, uses the parent directory of filepath.

Returns:

RootZoneMainFileConfig with parsed values

Return type:

RootZoneMainFileConfig

Example

>>> config = read_rootzone_main_file("C2VSimFG_RootZone.dat")
>>> print(f"Version: {config.version}")
>>> print(f"GW uptake: {config.gw_uptake_enabled}")
>>> if config.nonponded_crop_file:
...     print(f"Crop file: {config.nonponded_crop_file}")

Root Zone Writer#

Writer for IWFM root zone component files using Jinja2 templates.

Root Zone Component Writer for IWFM models.

This module provides the main writer for IWFM root zone component files, orchestrating the writing of all root zone-related input files including: - Main root zone control file (RootZone_MAIN.dat) - Soil parameters for each element - Return flow fractions - Irrigation periods

class pyiwfm.io.rootzone_writer.RootZoneWriterConfig(output_dir, version='4.12', rootzone_subdir='RootZone', main_file='RootZone_MAIN.dat', return_flow_file='ReturnFlowFrac.dat', reuse_file='ReuseFrac.dat', irig_period_file='IrigPeriod.dat', surface_flow_dest_file='SurfaceFlowDest.dat', lwu_budget_file='../Results/LWU.hdf', rz_budget_file='../Results/RootZone.hdf', lwu_zbudget_file='../Results/LWU_ZBud.hdf', rz_zbudget_file='../Results/RootZone_ZBud.hdf', convergence=0.001, max_iterations=150, inch_to_length_factor=0.0833333, gw_uptake=0, wilting_point=0.0, field_capacity=0.2, total_porosity=0.45, pore_size_index=0.62, hydraulic_conductivity=2.6, k_ponded=-1.0, rhc_method=2, capillary_rise=0.0, k_factor=0.03281, cprise_factor=1.0, k_time_unit='1hour', final_moisture_file='')[source]#

Bases: BaseComponentWriterConfig

Configuration for root zone component file writing.

Variables:
  • output_dir (Path) – Base output directory for root zone files

  • rootzone_subdir (str) – Subdirectory name for root zone files (default: “RootZone”)

  • version (str) – IWFM root zone component version

rootzone_subdir: str = 'RootZone'#
version: str = '4.12'#
main_file: str = 'RootZone_MAIN.dat'#
return_flow_file: str = 'ReturnFlowFrac.dat'#
reuse_file: str = 'ReuseFrac.dat'#
irig_period_file: str = 'IrigPeriod.dat'#
surface_flow_dest_file: str = 'SurfaceFlowDest.dat'#
lwu_budget_file: str = '../Results/LWU.hdf'#
rz_budget_file: str = '../Results/RootZone.hdf'#
lwu_zbudget_file: str = '../Results/LWU_ZBud.hdf'#
rz_zbudget_file: str = '../Results/RootZone_ZBud.hdf'#
convergence: float = 0.001#
max_iterations: int = 150#
inch_to_length_factor: float = 0.0833333#
gw_uptake: int = 0#
wilting_point: float = 0.0#
field_capacity: float = 0.2#
total_porosity: float = 0.45#
pore_size_index: float = 0.62#
hydraulic_conductivity: float = 2.6#
k_ponded: float = -1.0#
rhc_method: int = 2#
capillary_rise: float = 0.0#
k_factor: float = 0.03281#
cprise_factor: float = 1.0#
k_time_unit: str = '1hour'#
final_moisture_file: str = ''#
property rootzone_dir: Path#

Get the root zone subdirectory path.

__init__(output_dir, version='4.12', rootzone_subdir='RootZone', main_file='RootZone_MAIN.dat', return_flow_file='ReturnFlowFrac.dat', reuse_file='ReuseFrac.dat', irig_period_file='IrigPeriod.dat', surface_flow_dest_file='SurfaceFlowDest.dat', lwu_budget_file='../Results/LWU.hdf', rz_budget_file='../Results/RootZone.hdf', lwu_zbudget_file='../Results/LWU_ZBud.hdf', rz_zbudget_file='../Results/RootZone_ZBud.hdf', convergence=0.001, max_iterations=150, inch_to_length_factor=0.0833333, gw_uptake=0, wilting_point=0.0, field_capacity=0.2, total_porosity=0.45, pore_size_index=0.62, hydraulic_conductivity=2.6, k_ponded=-1.0, rhc_method=2, capillary_rise=0.0, k_factor=0.03281, cprise_factor=1.0, k_time_unit='1hour', final_moisture_file='')#
class pyiwfm.io.rootzone_writer.RootZoneComponentWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM Root Zone Component files.

Writes all root zone-related input files for IWFM simulation.

Example

>>> from pyiwfm.io.rootzone_writer import RootZoneComponentWriter, RootZoneWriterConfig
>>> config = RootZoneWriterConfig(output_dir=Path("model/Simulation"))
>>> writer = RootZoneComponentWriter(model, config)
>>> files = writer.write_all()
__init__(model, config, template_engine=None)[source]#

Initialize the root zone component writer.

Parameters:
  • model (IWFMModel) – Model to write

  • config (RootZoneWriterConfig) – Output file configuration

  • template_engine (TemplateEngine, optional) – Custom template engine

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all root zone files.

write_all(write_defaults=True)[source]#

Write all root zone component files.

Parameters:

write_defaults (bool) – If True, write default files even when no root zone component is loaded (useful for generating simulation skeleton)

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

write_main()[source]#

Write the main root zone control file.

Returns:

Path to written file

Return type:

Path

write_precip_ts(dates=None, data=None)[source]#

Write precipitation time series data file.

write_et_ts(dates=None, data=None)[source]#

Write evapotranspiration time series data file.

write_crop_coeff_ts(dates=None, data=None)[source]#

Write crop coefficient time series data file.

write_return_flow_ts(dates=None, data=None)[source]#

Write return flow fraction time series data file.

write_reuse_ts(dates=None, data=None)[source]#

Write reuse fraction time series data file.

write_irig_period_ts(dates=None, data=None)[source]#

Write irrigation period time series data file.

write_ag_water_demand_ts(dates=None, data=None)[source]#

Write agricultural water demand time series data file.

pyiwfm.io.rootzone_writer.write_rootzone_component(model, output_dir, config=None)[source]#

Write root zone component files for a model.

Parameters:
  • model (IWFMModel) – Model to write

  • output_dir (Path or str) – Output directory

  • config (RootZoneWriterConfig, optional) – File configuration

Returns:

Mapping of file type to output path

Return type:

dict[str, Path]

Root Zone v4.x#

Readers and writers specific to IWFM v4.x root zone format.

v4.x sub-file readers and writers for IWFM RootZone component.

This module handles the per-element, v4.0-v4.13 Fortran file formats for the four land-use sub-files (non-ponded ag, ponded ag, urban, native/riparian). These formats differ from the v5.0/subregion-based readers in rootzone_nonponded.py etc.

The existing v5.0 readers and their 1100+ passing tests are left untouched; this module provides parallel v4.x-specific I/O.

class pyiwfm.io.rootzone_v4x.RootDepthRow(crop_index, max_root_depth, fractions_column)[source]#

Bases: object

Root depth entry for one crop.

crop_index: int#
max_root_depth: float#
fractions_column: int#
__init__(crop_index, max_root_depth, fractions_column)#
class pyiwfm.io.rootzone_v4x.ElementCropRow(element_id, values=<factory>)[source]#

Bases: object

Per-element row with one value per crop (or per category).

element_id: int#
values: list[float]#
__init__(element_id, values=<factory>)#
class pyiwfm.io.rootzone_v4x.AgInitialConditionRow(element_id, precip_fraction, moisture_contents=<factory>)[source]#

Bases: object

Initial condition row: element_id, precip_fraction, MC per crop.

element_id: int#
precip_fraction: float#
moisture_contents: list[float]#
__init__(element_id, precip_fraction, moisture_contents=<factory>)#
class pyiwfm.io.rootzone_v4x.NonPondedCropConfigV4x(n_crops=0, demand_from_moisture_flag=1, crop_codes=<factory>, area_data_file=None, n_budget_crops=0, budget_crop_codes=<factory>, lwu_budget_file=None, rz_budget_file=None, root_depth_fractions_file=None, root_depth_factor=1.0, root_depth_data=<factory>, curve_numbers=<factory>, etc_pointers=<factory>, supply_req_pointers=<factory>, irrigation_pointers=<factory>, min_soil_moisture_file=None, min_moisture_pointers=<factory>, target_soil_moisture_file=None, target_moisture_pointers=<factory>, return_flow_pointers=<factory>, reuse_pointers=<factory>, leaching_factors_file=None, leaching_pointers=<factory>, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from a v4.x non-ponded agricultural sub-file.

n_crops: int = 0#
demand_from_moisture_flag: int = 1#
crop_codes: list[str]#
area_data_file: Path | None = None#
n_budget_crops: int = 0#
budget_crop_codes: list[str]#
lwu_budget_file: Path | None = None#
rz_budget_file: Path | None = None#
root_depth_fractions_file: Path | None = None#
root_depth_factor: float = 1.0#
root_depth_data: list[RootDepthRow]#
curve_numbers: list[ElementCropRow]#
etc_pointers: list[ElementCropRow]#
supply_req_pointers: list[ElementCropRow]#
irrigation_pointers: list[ElementCropRow]#
min_soil_moisture_file: Path | None = None#
min_moisture_pointers: list[ElementCropRow]#
target_soil_moisture_file: Path | None = None#
target_moisture_pointers: list[ElementCropRow]#
return_flow_pointers: list[ElementCropRow]#
reuse_pointers: list[ElementCropRow]#
leaching_factors_file: Path | None = None#
leaching_pointers: list[ElementCropRow]#
initial_conditions: list[AgInitialConditionRow]#
__init__(n_crops=0, demand_from_moisture_flag=1, crop_codes=<factory>, area_data_file=None, n_budget_crops=0, budget_crop_codes=<factory>, lwu_budget_file=None, rz_budget_file=None, root_depth_fractions_file=None, root_depth_factor=1.0, root_depth_data=<factory>, curve_numbers=<factory>, etc_pointers=<factory>, supply_req_pointers=<factory>, irrigation_pointers=<factory>, min_soil_moisture_file=None, min_moisture_pointers=<factory>, target_soil_moisture_file=None, target_moisture_pointers=<factory>, return_flow_pointers=<factory>, reuse_pointers=<factory>, leaching_factors_file=None, leaching_pointers=<factory>, initial_conditions=<factory>)#
class pyiwfm.io.rootzone_v4x.PondedCropConfigV4x(area_data_file=None, n_budget_crops=0, budget_crop_codes=<factory>, lwu_budget_file=None, rz_budget_file=None, root_depth_factor=1.0, root_depths=<factory>, curve_numbers=<factory>, etc_pointers=<factory>, supply_req_pointers=<factory>, irrigation_pointers=<factory>, ponding_depth_file=None, operations_flow_file=None, ponding_depth_pointers=<factory>, decomp_water_pointers=<factory>, return_flow_pointers=<factory>, reuse_pointers=<factory>, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from a v4.x ponded agricultural sub-file.

Fixed 5 crop types: rice (3 decomp variants) + refuge (2 types).

area_data_file: Path | None = None#
n_budget_crops: int = 0#
budget_crop_codes: list[str]#
lwu_budget_file: Path | None = None#
rz_budget_file: Path | None = None#
root_depth_factor: float = 1.0#
root_depths: list[float]#
curve_numbers: list[ElementCropRow]#
etc_pointers: list[ElementCropRow]#
supply_req_pointers: list[ElementCropRow]#
irrigation_pointers: list[ElementCropRow]#
ponding_depth_file: Path | None = None#
operations_flow_file: Path | None = None#
ponding_depth_pointers: list[ElementCropRow]#
decomp_water_pointers: list[ElementCropRow]#
return_flow_pointers: list[ElementCropRow]#
reuse_pointers: list[ElementCropRow]#
initial_conditions: list[AgInitialConditionRow]#
__init__(area_data_file=None, n_budget_crops=0, budget_crop_codes=<factory>, lwu_budget_file=None, rz_budget_file=None, root_depth_factor=1.0, root_depths=<factory>, curve_numbers=<factory>, etc_pointers=<factory>, supply_req_pointers=<factory>, irrigation_pointers=<factory>, ponding_depth_file=None, operations_flow_file=None, ponding_depth_pointers=<factory>, decomp_water_pointers=<factory>, return_flow_pointers=<factory>, reuse_pointers=<factory>, initial_conditions=<factory>)#
class pyiwfm.io.rootzone_v4x.UrbanElementRowV4x(element_id, pervious_fraction, curve_number, pop_column, per_cap_column, demand_fraction, etc_column, return_flow_column, reuse_column, water_use_column)[source]#

Bases: object

Per-element urban data (10-column row).

element_id: int#
pervious_fraction: float#
curve_number: float#
pop_column: int#
per_cap_column: int#
demand_fraction: float#
etc_column: int#
return_flow_column: int#
reuse_column: int#
water_use_column: int#
__init__(element_id, pervious_fraction, curve_number, pop_column, per_cap_column, demand_fraction, etc_column, return_flow_column, reuse_column, water_use_column)#
class pyiwfm.io.rootzone_v4x.UrbanInitialRowV4x(element_id, precip_fraction, moisture_content)[source]#

Bases: object

Urban initial conditions: element_id, precip_frac, moisture.

element_id: int#
precip_fraction: float#
moisture_content: float#
__init__(element_id, precip_fraction, moisture_content)#
class pyiwfm.io.rootzone_v4x.UrbanConfigV4x(area_data_file=None, root_depth_factor=1.0, root_depth=0.0, population_file=None, per_capita_water_use_file=None, water_use_specs_file=None, element_data=<factory>, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from a v4.x urban land-use sub-file.

area_data_file: Path | None = None#
root_depth_factor: float = 1.0#
root_depth: float = 0.0#
population_file: Path | None = None#
per_capita_water_use_file: Path | None = None#
water_use_specs_file: Path | None = None#
element_data: list[UrbanElementRowV4x]#
initial_conditions: list[UrbanInitialRowV4x]#
__init__(area_data_file=None, root_depth_factor=1.0, root_depth=0.0, population_file=None, per_capita_water_use_file=None, water_use_specs_file=None, element_data=<factory>, initial_conditions=<factory>)#
class pyiwfm.io.rootzone_v4x.NativeRiparianElementRowV4x(element_id, native_cn, riparian_cn, native_etc_column, riparian_etc_column, riparian_stream_node=0)[source]#

Bases: object

Per-element native/riparian data (5 or 6-column row).

The 6th column (riparian_stream_node) is the stream node index for riparian ET from stream (ISTRMRV), present in v4.1+ files.

element_id: int#
native_cn: float#
riparian_cn: float#
native_etc_column: int#
riparian_etc_column: int#
riparian_stream_node: int = 0#
__init__(element_id, native_cn, riparian_cn, native_etc_column, riparian_etc_column, riparian_stream_node=0)#
class pyiwfm.io.rootzone_v4x.NativeRiparianInitialRowV4x(element_id, native_moisture, riparian_moisture)[source]#

Bases: object

Native/riparian initial conditions (3-column row).

element_id: int#
native_moisture: float#
riparian_moisture: float#
__init__(element_id, native_moisture, riparian_moisture)#
class pyiwfm.io.rootzone_v4x.NativeRiparianConfigV4x(area_data_file=None, root_depth_factor=1.0, native_root_depth=0.0, riparian_root_depth=0.0, element_data=<factory>, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from a v4.x native/riparian sub-file.

area_data_file: Path | None = None#
root_depth_factor: float = 1.0#
native_root_depth: float = 0.0#
riparian_root_depth: float = 0.0#
element_data: list[NativeRiparianElementRowV4x]#
initial_conditions: list[NativeRiparianInitialRowV4x]#
__init__(area_data_file=None, root_depth_factor=1.0, native_root_depth=0.0, riparian_root_depth=0.0, element_data=<factory>, initial_conditions=<factory>)#
class pyiwfm.io.rootzone_v4x.NonPondedCropReaderV4x(n_elements=0)[source]#

Bases: _V4xReaderBase

Reader for IWFM v4.x non-ponded agricultural crop sub-file.

Reads the 24-section file produced by Class_NonPondedAgLandUse_v40::New().

read(filepath, base_dir=None)[source]#
class pyiwfm.io.rootzone_v4x.PondedCropReaderV4x(n_elements=0)[source]#

Bases: _V4xReaderBase

Reader for IWFM v4.x ponded agricultural crop sub-file.

Fixed 5 crop types (3 rice + 2 refuge).

N_PONDED_CROPS = 5#
read(filepath, base_dir=None)[source]#
class pyiwfm.io.rootzone_v4x.UrbanReaderV4x(n_elements=0)[source]#

Bases: _V4xReaderBase

Reader for IWFM v4.x urban land-use sub-file.

read(filepath, base_dir=None)[source]#
class pyiwfm.io.rootzone_v4x.NativeRiparianReaderV4x(n_elements=0)[source]#

Bases: _V4xReaderBase

Reader for IWFM v4.x native/riparian vegetation sub-file.

read(filepath, base_dir=None)[source]#
class pyiwfm.io.rootzone_v4x.NonPondedCropWriterV4x[source]#

Bases: _V4xWriterBase

Writer for IWFM v4.x non-ponded agricultural crop sub-file.

write(cfg, filepath)[source]#
class pyiwfm.io.rootzone_v4x.PondedCropWriterV4x[source]#

Bases: _V4xWriterBase

Writer for IWFM v4.x ponded agricultural crop sub-file.

write(cfg, filepath)[source]#
class pyiwfm.io.rootzone_v4x.UrbanWriterV4x[source]#

Bases: _V4xWriterBase

Writer for IWFM v4.x urban land-use sub-file.

write(cfg, filepath)[source]#
class pyiwfm.io.rootzone_v4x.NativeRiparianWriterV4x[source]#

Bases: _V4xWriterBase

Writer for IWFM v4.x native/riparian vegetation sub-file.

write(cfg, filepath)[source]#
pyiwfm.io.rootzone_v4x.read_nonponded_v4x(filepath, base_dir=None, n_elements=0)[source]#

Read a v4.x non-ponded agricultural crop sub-file.

pyiwfm.io.rootzone_v4x.read_ponded_v4x(filepath, base_dir=None, n_elements=0)[source]#

Read a v4.x ponded agricultural crop sub-file.

pyiwfm.io.rootzone_v4x.read_urban_v4x(filepath, base_dir=None, n_elements=0)[source]#

Read a v4.x urban land-use sub-file.

pyiwfm.io.rootzone_v4x.read_native_riparian_v4x(filepath, base_dir=None, n_elements=0)[source]#

Read a v4.x native/riparian vegetation sub-file.

Root Zone Non-Ponded Crops#

Reader for non-ponded (dry) crop land use parameters.

Non-ponded agricultural crop sub-file reader for IWFM RootZone component.

This module reads the IWFM non-ponded agricultural crop file (AGNPFL), which is referenced by the RootZone component main file. The file contains crop definitions, curve numbers, ET-column pointers, irrigation period references, soil moisture targets, return-flow / reuse fractions, and initial conditions.

The same binary format (Class_AgLandUse_v50) is used by both the non-ponded and ponded crop files in IWFM v5.0.

class pyiwfm.io.rootzone_nonponded.CurveNumberRow(subregion_id, cn_values=<factory>)[source]#

Bases: object

Curve-number data for one subregion.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • cn_values (list[float]) – Curve-number values, one per soil type.

subregion_id: int#
cn_values: list[float]#
__init__(subregion_id, cn_values=<factory>)#
class pyiwfm.io.rootzone_nonponded.EtcPointerRow(subregion_id, etc_columns=<factory>)[source]#

Bases: object

ET-column pointers for one subregion.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • etc_columns (list[int]) – Column indices into the ET data file, one per crop.

subregion_id: int#
etc_columns: list[int]#
__init__(subregion_id, etc_columns=<factory>)#
class pyiwfm.io.rootzone_nonponded.IrrigationPointerRow(subregion_id, irrig_columns=<factory>)[source]#

Bases: object

Irrigation-period column pointers for one subregion.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • irrig_columns (list[int]) – Column indices, one per crop.

subregion_id: int#
irrig_columns: list[int]#
__init__(subregion_id, irrig_columns=<factory>)#
class pyiwfm.io.rootzone_nonponded.SoilMoisturePointerRow(subregion_id, columns=<factory>)[source]#

Bases: object

Soil-moisture column pointers for one subregion.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • columns (list[int]) – Column indices, one per crop.

subregion_id: int#
columns: list[int]#
__init__(subregion_id, columns=<factory>)#
class pyiwfm.io.rootzone_nonponded.SupplyReturnReuseRow(subregion_id, supply_column=0, return_flow_column=0, reuse_column=0)[source]#

Bases: object

Water-supply, return-flow and reuse column pointers for one subregion.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • supply_column (int) – Water-supply column pointer.

  • return_flow_column (int) – Return-flow fraction column pointer.

  • reuse_column (int) – Reuse fraction column pointer.

subregion_id: int#
supply_column: int = 0#
return_flow_column: int = 0#
reuse_column: int = 0#
__init__(subregion_id, supply_column=0, return_flow_column=0, reuse_column=0)#
class pyiwfm.io.rootzone_nonponded.InitialConditionRow(subregion_id, precip_fractions=<factory>, moisture_contents=<factory>)[source]#

Bases: object

Initial soil-moisture conditions for one subregion.

For each soil type there are two values: - fraction of initial soil moisture due to precipitation (0–1) - initial soil-moisture content (0–1)

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • precip_fractions (list[float]) – Precipitation fractions, one per soil type.

  • moisture_contents (list[float]) – Soil-moisture contents, one per soil type.

subregion_id: int#
precip_fractions: list[float]#
moisture_contents: list[float]#
__init__(subregion_id, precip_fractions=<factory>, moisture_contents=<factory>)#
class pyiwfm.io.rootzone_nonponded.NonPondedCropConfig(n_crops=0, subregional_area_file=None, elemental_area_file=None, area_output_factor=1.0, area_output_unit='', area_output_file=None, root_depth_factor=1.0, root_depths=<factory>, curve_numbers=<factory>, etc_pointers=<factory>, irrigation_period_file=None, irrigation_pointers=<factory>, min_soil_moisture_file=None, min_moisture_pointers=<factory>, target_soil_moisture_file=None, target_moisture_pointers=<factory>, water_demand_file=None, demand_from_moisture_flag=1, supply_return_reuse=<factory>, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from a non-ponded agricultural crop sub-file.

Variables:
n_crops: int = 0#
subregional_area_file: Path | None = None#
elemental_area_file: Path | None = None#
area_output_factor: float = 1.0#
area_output_unit: str = ''#
area_output_file: Path | None = None#
root_depth_factor: float = 1.0#
root_depths: list[float]#
curve_numbers: list[CurveNumberRow]#
etc_pointers: list[EtcPointerRow]#
irrigation_period_file: Path | None = None#
irrigation_pointers: list[IrrigationPointerRow]#
min_soil_moisture_file: Path | None = None#
min_moisture_pointers: list[SoilMoisturePointerRow]#
target_soil_moisture_file: Path | None = None#
target_moisture_pointers: list[SoilMoisturePointerRow]#
water_demand_file: Path | None = None#
demand_from_moisture_flag: int = 1#
supply_return_reuse: list[SupplyReturnReuseRow]#
initial_conditions: list[InitialConditionRow]#
__init__(n_crops=0, subregional_area_file=None, elemental_area_file=None, area_output_factor=1.0, area_output_unit='', area_output_file=None, root_depth_factor=1.0, root_depths=<factory>, curve_numbers=<factory>, etc_pointers=<factory>, irrigation_period_file=None, irrigation_pointers=<factory>, min_soil_moisture_file=None, min_moisture_pointers=<factory>, target_soil_moisture_file=None, target_moisture_pointers=<factory>, water_demand_file=None, demand_from_moisture_flag=1, supply_return_reuse=<factory>, initial_conditions=<factory>)#
class pyiwfm.io.rootzone_nonponded.NonPondedCropReader(n_subregions=None)[source]#

Bases: object

Reader for IWFM non-ponded agricultural crop sub-file.

This parses the positional-sequential file referenced as AGNPFL in the RootZone component main file. The format matches Class_AgLandUse_v50::New() in the Fortran source.

Parameters:

n_subregions (int | None) – Number of subregions in the model. If provided, tabular sections read exactly this many rows (skipping comments within). If None, sections are delimited by comment lines (i.e. a comment line after data rows ends the section).

__init__(n_subregions=None)[source]#
read(filepath, base_dir=None)[source]#

Parse the non-ponded crop file.

Parameters:
  • filepath (Path | str) – Path to the non-ponded crop file.

  • base_dir (Path | None) – Base directory for resolving relative sub-file paths. Defaults to the parent of filepath.

Returns:

Populated NonPondedCropConfig.

Return type:

NonPondedCropConfig

pyiwfm.io.rootzone_nonponded.read_nonponded_crop(filepath, base_dir=None, n_subregions=None)[source]#

Read an IWFM non-ponded agricultural crop sub-file.

Parameters:
  • filepath (Path | str) – Path to the non-ponded crop file.

  • base_dir (Path | None) – Base directory for resolving relative paths.

  • n_subregions (int | None) – Number of subregions (for exact row counts).

Returns:

NonPondedCropConfig with parsed values.

Return type:

NonPondedCropConfig

Root Zone Ponded Crops#

Reader for ponded (rice/wetland) crop land use parameters.

Ponded agricultural crop sub-file reader for IWFM RootZone component.

This module reads the IWFM ponded agricultural crop file (PFL), which is referenced by the RootZone component main file. In IWFM v5.0 the ponded crop file uses the same Class_AgLandUse_v50 format as the non-ponded crop file, so this module re-exports the non-ponded reader with ponded-specific type aliases.

Typical ponded crops in C2VSimFG:
  • Rice (flood-decomposed, non-flood-decomposed, no-decomposition)

  • Refuge (seasonal, permanent)

pyiwfm.io.rootzone_ponded.PondedCropConfig#

Alias for NonPondedCropConfig — same file format.

class pyiwfm.io.rootzone_ponded.PondedCropReader(n_subregions=None)[source]#

Bases: NonPondedCropReader

Reader for IWFM ponded agricultural crop sub-file.

Inherits from NonPondedCropReader since IWFM v5.0 uses the same Class_AgLandUse_v50 format for both ponded and non-ponded crop files.

pyiwfm.io.rootzone_ponded.read_ponded_crop(filepath, base_dir=None, n_subregions=None)[source]#

Read an IWFM ponded agricultural crop sub-file.

Parameters:
  • filepath (Path | str) – Path to the ponded crop file.

  • base_dir (Path | None) – Base directory for resolving relative paths.

  • n_subregions (int | None) – Number of subregions (for exact row counts).

Returns:

PondedCropConfig with parsed values.

Return type:

NonPondedCropConfig

Root Zone Urban#

Reader for urban land use parameters.

Urban land-use sub-file reader for IWFM RootZone component.

This module reads the IWFM urban land-use file (URBFL), which is referenced by the RootZone component main file. The file contains urban area data references, root depth, curve numbers, water demand and water-use specification references, per-subregion management data, per-element surface-flow destinations, and initial soil-moisture conditions.

Format matches Class_UrbanLandUse_v50::New() in the Fortran source.

class pyiwfm.io.rootzone_urban.UrbanCurveNumberRow(subregion_id, cn_values=<factory>)[source]#

Bases: object

Curve-number data for one subregion (urban).

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • cn_values (list[float]) – Curve-number values, one per soil type.

subregion_id: int#
cn_values: list[float]#
__init__(subregion_id, cn_values=<factory>)#
class pyiwfm.io.rootzone_urban.UrbanManagementRow(subregion_id, pervious_fraction=1.0, demand_column=0, water_use_column=0, etc_column=0, return_flow_column=0, reuse_column=0)[source]#

Bases: object

Per-subregion urban water management data.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • pervious_fraction (float) – Fraction of pervious area (0–1).

  • demand_column (int) – Column index into water-demand data file.

  • water_use_column (int) – Column index into water-use specs file.

  • etc_column (int) – Column index into ET data file.

  • return_flow_column (int) – Column index into return-flow fractions.

  • reuse_column (int) – Column index into reuse fractions.

subregion_id: int#
pervious_fraction: float = 1.0#
demand_column: int = 0#
water_use_column: int = 0#
etc_column: int = 0#
return_flow_column: int = 0#
reuse_column: int = 0#
__init__(subregion_id, pervious_fraction=1.0, demand_column=0, water_use_column=0, etc_column=0, return_flow_column=0, reuse_column=0)#
class pyiwfm.io.rootzone_urban.SurfaceFlowDestRow(element_id, dest_type=1, dest_id=0)[source]#

Bases: object

Per-element surface-flow destination.

Variables:
  • element_id (int) – Element identifier (1-based).

  • dest_type (int) – Destination type code: 1 = outside model domain, 2 = stream node, 3 = lake, 4 = subregion, 5 = groundwater element.

  • dest_id (int) – Destination identifier (node / lake / subregion / element ID).

element_id: int#
dest_type: int = 1#
dest_id: int = 0#
__init__(element_id, dest_type=1, dest_id=0)#
class pyiwfm.io.rootzone_urban.UrbanInitialConditionRow(subregion_id, precip_fractions=<factory>, moisture_contents=<factory>)[source]#

Bases: object

Initial soil-moisture conditions for one subregion.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • precip_fractions (list[float]) – Precipitation fractions, one per soil type.

  • moisture_contents (list[float]) – Soil-moisture contents, one per soil type.

subregion_id: int#
precip_fractions: list[float]#
moisture_contents: list[float]#
__init__(subregion_id, precip_fractions=<factory>, moisture_contents=<factory>)#
class pyiwfm.io.rootzone_urban.UrbanLandUseConfig(area_data_file=None, root_depth_factor=1.0, root_depth=0.0, curve_numbers=<factory>, demand_file=None, water_use_specs_file=None, management=<factory>, surface_flow_destinations=<factory>, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from an urban land-use sub-file.

Variables:
area_data_file: Path | None = None#
root_depth_factor: float = 1.0#
root_depth: float = 0.0#
curve_numbers: list[UrbanCurveNumberRow]#
demand_file: Path | None = None#
water_use_specs_file: Path | None = None#
management: list[UrbanManagementRow]#
surface_flow_destinations: list[SurfaceFlowDestRow]#
initial_conditions: list[UrbanInitialConditionRow]#
__init__(area_data_file=None, root_depth_factor=1.0, root_depth=0.0, curve_numbers=<factory>, demand_file=None, water_use_specs_file=None, management=<factory>, surface_flow_destinations=<factory>, initial_conditions=<factory>)#
class pyiwfm.io.rootzone_urban.UrbanLandUseReader(n_subregions=None, n_elements=None)[source]#

Bases: object

Reader for IWFM urban land-use sub-file.

Parses the positional-sequential file referenced as URBFL in the RootZone component main file.

Parameters:
  • n_subregions (int | None) – Number of subregions (for exact row counts).

  • n_elements (int | None) – Number of elements (for destination row counts).

__init__(n_subregions=None, n_elements=None)[source]#
read(filepath, base_dir=None)[source]#

Parse the urban land-use file.

Parameters:
  • filepath (Path | str) – Path to the urban land-use file.

  • base_dir (Path | None) – Base directory for resolving relative sub-file paths. Defaults to the parent of filepath.

Returns:

Populated UrbanLandUseConfig.

Return type:

UrbanLandUseConfig

pyiwfm.io.rootzone_urban.read_urban_landuse(filepath, base_dir=None, n_subregions=None, n_elements=None)[source]#

Read an IWFM urban land-use sub-file.

Parameters:
  • filepath (Path | str) – Path to the urban land-use file.

  • base_dir (Path | None) – Base directory for resolving relative paths.

  • n_subregions (int | None) – Number of subregions (for exact row counts).

  • n_elements (int | None) – Number of elements (for destination row counts).

Returns:

UrbanLandUseConfig with parsed values.

Return type:

UrbanLandUseConfig

Root Zone Native/Riparian#

Reader for native and riparian vegetation parameters.

Native / riparian vegetation sub-file reader for IWFM RootZone component.

This module reads the IWFM native and riparian vegetation file (NVRVFL), which is referenced by the RootZone component main file. The file contains land-use area references, root depths for native and riparian vegetation, curve numbers for both vegetation types, ET-column pointers, and initial soil-moisture conditions.

Format matches Class_NativeRiparianLandUse_v50::New() in the Fortran source.

class pyiwfm.io.rootzone_native.NativeRiparianCNRow(subregion_id, native_cn=<factory>, riparian_cn=<factory>)[source]#

Bases: object

Curve-number data for one subregion (native + riparian).

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • native_cn (list[float]) – Curve-number values for native vegetation, one per soil.

  • riparian_cn (list[float]) – Curve-number values for riparian vegetation, one per soil.

subregion_id: int#
native_cn: list[float]#
riparian_cn: list[float]#
__init__(subregion_id, native_cn=<factory>, riparian_cn=<factory>)#
class pyiwfm.io.rootzone_native.NativeRiparianEtcRow(subregion_id, native_etc_column=0, riparian_etc_column=0)[source]#

Bases: object

ET-column pointers for one subregion.

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • native_etc_column (int) – Column index for native vegetation ETc.

  • riparian_etc_column (int) – Column index for riparian vegetation ETc.

subregion_id: int#
native_etc_column: int = 0#
riparian_etc_column: int = 0#
__init__(subregion_id, native_etc_column=0, riparian_etc_column=0)#
class pyiwfm.io.rootzone_native.NativeRiparianInitialRow(subregion_id, native_moisture=<factory>, riparian_moisture=<factory>)[source]#

Bases: object

Initial soil-moisture conditions for one subregion.

Values alternate between native and riparian per soil type: native_soil1  riparian_soil1  native_soil2  riparian_soil2

Variables:
  • subregion_id (int) – Subregion identifier (1-based).

  • native_moisture (list[float]) – Initial moisture content for native, one per soil.

  • riparian_moisture (list[float]) – Initial moisture content for riparian, one per soil.

subregion_id: int#
native_moisture: list[float]#
riparian_moisture: list[float]#
__init__(subregion_id, native_moisture=<factory>, riparian_moisture=<factory>)#
class pyiwfm.io.rootzone_native.NativeRiparianConfig(area_data_file=None, root_depth_factor=1.0, native_root_depth=0.0, riparian_root_depth=0.0, curve_numbers=<factory>, etc_pointers=<factory>, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from a native/riparian vegetation sub-file.

Variables:
area_data_file: Path | None = None#
root_depth_factor: float = 1.0#
native_root_depth: float = 0.0#
riparian_root_depth: float = 0.0#
curve_numbers: list[NativeRiparianCNRow]#
etc_pointers: list[NativeRiparianEtcRow]#
initial_conditions: list[NativeRiparianInitialRow]#
__init__(area_data_file=None, root_depth_factor=1.0, native_root_depth=0.0, riparian_root_depth=0.0, curve_numbers=<factory>, etc_pointers=<factory>, initial_conditions=<factory>)#
class pyiwfm.io.rootzone_native.NativeRiparianReader(n_subregions=None)[source]#

Bases: object

Reader for IWFM native/riparian vegetation sub-file.

Parses the positional-sequential file referenced as NVRVFL in the RootZone component main file.

Parameters:

n_subregions (int | None) – Number of subregions (for exact row counts).

__init__(n_subregions=None)[source]#
read(filepath, base_dir=None)[source]#

Parse the native/riparian vegetation file.

Parameters:
  • filepath (Path | str) – Path to the native/riparian vegetation file.

  • base_dir (Path | None) – Base directory for resolving relative sub-file paths. Defaults to the parent of filepath.

Returns:

Populated NativeRiparianConfig.

Return type:

NativeRiparianConfig

pyiwfm.io.rootzone_native.read_native_riparian(filepath, base_dir=None, n_subregions=None)[source]#

Read an IWFM native/riparian vegetation sub-file.

Parameters:
  • filepath (Path | str) – Path to the native/riparian vegetation file.

  • base_dir (Path | None) – Base directory for resolving relative paths.

  • n_subregions (int | None) – Number of subregions (for exact row counts).

Returns:

NativeRiparianConfig with parsed values.

Return type:

NativeRiparianConfig

Supplemental Package I/O#

Small Watershed Module#

Reader for IWFM small watershed component files.

Small Watershed Main File Reader for IWFM.

This module reads the IWFM small watershed component main file, which defines watershed-level modeling parameters including: 1. Output file paths (budget, final results) 2. Number of small watersheds and their geospatial data 3. Root zone parameters (soil properties, curve numbers) 4. Aquifer parameters (storage, recession coefficients) 5. Initial conditions

Reference: Class_AppSmallWatershed_v40.f90 - Class_AppSmallWatershed_v40_New()

class pyiwfm.io.small_watershed.WatershedGWNode(gw_node_id=0, max_perc_rate=0.0, is_baseflow=False, layer=0)[source]#

Bases: object

Groundwater node connection for a small watershed.

Variables:
  • gw_node_id (int) – Groundwater node ID

  • max_perc_rate (float) – Max percolation rate (positive) or baseflow layer (negative)

  • is_baseflow (bool) – Whether this is a baseflow node

  • layer (int) – Baseflow layer (if is_baseflow)

gw_node_id: int = 0#
max_perc_rate: float = 0.0#
is_baseflow: bool = False#
layer: int = 0#
__init__(gw_node_id=0, max_perc_rate=0.0, is_baseflow=False, layer=0)#
class pyiwfm.io.small_watershed.WatershedSpec(id=0, area=0.0, dest_stream_node=0, gw_nodes=<factory>)[source]#

Bases: object

Geospatial specification for a single small watershed.

Variables:
id: int = 0#
area: float = 0.0#
dest_stream_node: int = 0#
gw_nodes: list[WatershedGWNode]#
__init__(id=0, area=0.0, dest_stream_node=0, gw_nodes=<factory>)#
class pyiwfm.io.small_watershed.WatershedRootZoneParams(id=0, precip_col=0, precip_factor=1.0, et_col=0, crop_coeff_col=0, wilting_point=0.0, field_capacity=0.0, total_porosity=0.0, lambda_param=0.0, root_depth=0.0, hydraulic_cond=0.0, kunsat_method=0, curve_number=0.0)[source]#

Bases: object

Root zone parameters for a single small watershed.

Variables:
  • id (int) – Watershed ID

  • precip_col (int) – Precipitation time-series column index

  • precip_factor (float) – Precipitation conversion factor

  • et_col (int) – ET time-series column index

  • wilting_point (float) – Soil wilting point

  • field_capacity (float) – Soil field capacity

  • total_porosity (float) – Total porosity

  • lambda_param (float) – Pore size distribution parameter

  • root_depth (float) – Root zone depth

  • hydraulic_cond (float) – Hydraulic conductivity

  • kunsat_method (int) – Unsaturated K method code

  • curve_number (float) – SCS curve number

id: int = 0#
precip_col: int = 0#
precip_factor: float = 1.0#
et_col: int = 0#
crop_coeff_col: int = 0#
wilting_point: float = 0.0#
field_capacity: float = 0.0#
total_porosity: float = 0.0#
lambda_param: float = 0.0#
root_depth: float = 0.0#
hydraulic_cond: float = 0.0#
kunsat_method: int = 0#
curve_number: float = 0.0#
__init__(id=0, precip_col=0, precip_factor=1.0, et_col=0, crop_coeff_col=0, wilting_point=0.0, field_capacity=0.0, total_porosity=0.0, lambda_param=0.0, root_depth=0.0, hydraulic_cond=0.0, kunsat_method=0, curve_number=0.0)#
class pyiwfm.io.small_watershed.WatershedInitialCondition(id=0, soil_moisture=0.0, gw_storage=0.0)[source]#

Bases: object

Initial conditions for a single small watershed.

Variables:
  • id (int) – Watershed ID

  • soil_moisture (float) – Initial soil moisture fraction (0-1)

  • gw_storage (float) – Initial groundwater storage (multiplied by ic_factor)

id: int = 0#
soil_moisture: float = 0.0#
gw_storage: float = 0.0#
__init__(id=0, soil_moisture=0.0, gw_storage=0.0)#
class pyiwfm.io.small_watershed.WatershedAquiferParams(id=0, gw_threshold=0.0, max_gw_storage=0.0, surface_flow_coeff=0.0, baseflow_coeff=0.0)[source]#

Bases: object

Aquifer parameters for a single small watershed.

Variables:
  • id (int) – Watershed ID

  • gw_threshold (float) – Groundwater storage threshold

  • max_gw_storage (float) – Maximum groundwater storage

  • surface_flow_coeff (float) – Surface flow recession coefficient

  • baseflow_coeff (float) – Baseflow recession coefficient

id: int = 0#
gw_threshold: float = 0.0#
max_gw_storage: float = 0.0#
surface_flow_coeff: float = 0.0#
baseflow_coeff: float = 0.0#
__init__(id=0, gw_threshold=0.0, max_gw_storage=0.0, surface_flow_coeff=0.0, baseflow_coeff=0.0)#
class pyiwfm.io.small_watershed.SmallWatershedMainConfig(version='', budget_output_file=None, final_results_file=None, n_watersheds=0, area_factor=1.0, flow_factor=1.0, flow_time_unit='', watershed_specs=<factory>, rz_solver_tolerance=1e-08, rz_max_iterations=2000, rz_length_factor=1.0, rz_cn_factor=1.0, rz_k_factor=1.0, rz_k_time_unit='', rootzone_params=<factory>, aq_gw_factor=1.0, aq_time_factor=1.0, aq_time_unit='', aquifer_params=<factory>, ic_factor=1.0, initial_conditions=<factory>)[source]#

Bases: object

Configuration parsed from Small Watershed component main file.

Variables:
  • version (str) – File format version

  • budget_output_file (pathlib.Path | None) – Path to budget output file

  • final_results_file (pathlib.Path | None) – Path to final simulation results file

  • n_watersheds (int) – Number of small watersheds

  • area_factor (float) – Area conversion factor

  • flow_factor (float) – Flow rate conversion factor

  • flow_time_unit (str) – Time unit for flow rates

  • watershed_specs (list[pyiwfm.io.small_watershed.WatershedSpec]) – Geospatial specs per watershed

  • rz_solver_tolerance (float) – Root zone solver tolerance

  • rz_max_iterations (int) – Root zone solver max iterations

  • rz_length_factor (float) – Root zone length conversion factor

  • rz_cn_factor (float) – Curve number conversion factor

  • rz_k_factor (float) – Hydraulic conductivity conversion factor

  • rz_k_time_unit (str) – Time unit for hydraulic conductivity

  • rootzone_params (list[pyiwfm.io.small_watershed.WatershedRootZoneParams]) – Root zone parameters per watershed

  • aq_gw_factor (float) – GW conversion factor

  • aq_time_factor (float) – Time conversion factor

  • aq_time_unit (str) – Time unit for recession coefficients

  • aquifer_params (list[pyiwfm.io.small_watershed.WatershedAquiferParams]) – Aquifer parameters per watershed

version: str = ''#
budget_output_file: Path | None = None#
final_results_file: Path | None = None#
n_watersheds: int = 0#
area_factor: float = 1.0#
flow_factor: float = 1.0#
flow_time_unit: str = ''#
watershed_specs: list[WatershedSpec]#
rz_solver_tolerance: float = 1e-08#
rz_max_iterations: int = 2000#
rz_length_factor: float = 1.0#
rz_cn_factor: float = 1.0#
rz_k_factor: float = 1.0#
rz_k_time_unit: str = ''#
rootzone_params: list[WatershedRootZoneParams]#
aq_gw_factor: float = 1.0#
aq_time_factor: float = 1.0#
aq_time_unit: str = ''#
aquifer_params: list[WatershedAquiferParams]#
ic_factor: float = 1.0#
initial_conditions: list[WatershedInitialCondition]#
__init__(version='', budget_output_file=None, final_results_file=None, n_watersheds=0, area_factor=1.0, flow_factor=1.0, flow_time_unit='', watershed_specs=<factory>, rz_solver_tolerance=1e-08, rz_max_iterations=2000, rz_length_factor=1.0, rz_cn_factor=1.0, rz_k_factor=1.0, rz_k_time_unit='', rootzone_params=<factory>, aq_gw_factor=1.0, aq_time_factor=1.0, aq_time_unit='', aquifer_params=<factory>, ic_factor=1.0, initial_conditions=<factory>)#
class pyiwfm.io.small_watershed.SmallWatershedMainReader[source]#

Bases: ReaderMixin

Reader for IWFM small watershed component main file.

Parses the complete small watershed configuration including geospatial data, root zone parameters, aquifer parameters, and output file paths.

__init__()[source]#
read(filepath, base_dir=None)[source]#

Read small watershed main file.

Parameters:
  • filepath (Path | str) – Path to the small watershed main file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

SmallWatershedMainConfig with all configuration data

Return type:

SmallWatershedMainConfig

pyiwfm.io.small_watershed.read_small_watershed_main(filepath, base_dir=None)[source]#

Read IWFM small watershed component main file.

Parameters:
  • filepath (Path | str) – Path to the small watershed main file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

SmallWatershedMainConfig with all configuration data

Return type:

SmallWatershedMainConfig

Small Watershed Writer#

Writer for IWFM small watershed component files using Jinja2 templates.

Small Watershed Component Writer for IWFM models.

This module provides the writer for IWFM small watershed component files, generating the main file with geospatial data, root zone parameters, and aquifer parameters for all watershed units.

class pyiwfm.io.small_watershed_writer.SmallWatershedWriterConfig(output_dir, version='4.0', swshed_subdir='SmallWatershed', main_file='SmallWatershed_MAIN.dat', budget_file='../Results/SWShedBud.hdf', final_results_file='../Results/FinalSWShed.out')[source]#

Bases: BaseComponentWriterConfig

Configuration for small watershed component file writing.

Variables:
  • output_dir (pathlib.Path) – Base output directory

  • swshed_subdir (str) – Subdirectory name for small watershed files

  • version (str) – IWFM component version

  • main_file (str) – Main file name

  • budget_file (str) – Budget output file path

  • final_results_file (str) – Final results output file path

swshed_subdir: str = 'SmallWatershed'#
main_file: str = 'SmallWatershed_MAIN.dat'#
budget_file: str = '../Results/SWShedBud.hdf'#
final_results_file: str = '../Results/FinalSWShed.out'#
property swshed_dir: Path#

Get the small watershed subdirectory path.

__init__(output_dir, version='4.0', swshed_subdir='SmallWatershed', main_file='SmallWatershed_MAIN.dat', budget_file='../Results/SWShedBud.hdf', final_results_file='../Results/FinalSWShed.out')#
class pyiwfm.io.small_watershed_writer.SmallWatershedComponentWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM Small Watershed Component files.

Writes the small watershed main file including geospatial data, root zone parameters, and aquifer parameters.

Example

>>> from pyiwfm.io.small_watershed_writer import (
...     SmallWatershedComponentWriter, SmallWatershedWriterConfig,
... )
>>> config = SmallWatershedWriterConfig(output_dir=Path("model/Simulation"))
>>> writer = SmallWatershedComponentWriter(model, config)
>>> files = writer.write_all()
__init__(model, config, template_engine=None)[source]#

Initialize the template writer.

Parameters:
  • output_dir – Directory for output files

  • template_engine (TemplateEngine | None) – Optional TemplateEngine instance

  • comment_metadata – Optional CommentMetadata for comment preservation

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all small watershed files.

write_all(write_defaults=True)[source]#

Write all small watershed component files.

Parameters:

write_defaults (bool) – If True, write default files even when no component is loaded.

Returns:

Mapping of file type to output path.

Return type:

dict[str, Path]

write_main()[source]#

Write the main small watershed control file.

Returns:

Path to written file.

Return type:

Path

pyiwfm.io.small_watershed_writer.write_small_watershed_component(model, output_dir, config=None)[source]#

Write small watershed component files for a model.

Parameters:
  • model (IWFMModel) – Model to write.

  • output_dir (Path or str) – Output directory.

  • config (SmallWatershedWriterConfig, optional) – File configuration.

Returns:

Mapping of file type to output path.

Return type:

dict[str, Path]

Unsaturated Zone Module#

Reader for IWFM unsaturated zone component files.

Unsaturated Zone Main File Reader for IWFM.

This module reads the IWFM unsaturated zone component main file, which defines vadose zone modeling parameters including: 1. Number of unsaturated layers 2. Solver parameters 3. Budget output file paths 4. Layer properties (per element) 5. Initial soil moisture conditions

The unsaturated zone is an optional component used for detailed vadose zone dynamics between the root zone and groundwater.

Reference: Package_AppUnsatZone.f90 - New()

class pyiwfm.io.unsaturated_zone.UnsatZoneElementData(element_id, thickness_max, total_porosity, lambda_param, hyd_cond, kunsat_method)[source]#

Bases: object

Per-element unsaturated zone parameters for all layers.

Fortran format per line: ElemID, [ThickMax_L1, Porosity_L1, Lambda_L1, HydCond_L1, KMethod_L1, ...(repeat per layer)]. Total values per line: 1 + 5 * n_layers.

Variables:
element_id: int#
thickness_max: ndarray[tuple[Any, ...], dtype[float64]]#
total_porosity: ndarray[tuple[Any, ...], dtype[float64]]#
lambda_param: ndarray[tuple[Any, ...], dtype[float64]]#
hyd_cond: ndarray[tuple[Any, ...], dtype[float64]]#
kunsat_method: ndarray[tuple[Any, ...], dtype[int32]]#
__init__(element_id, thickness_max, total_porosity, lambda_param, hyd_cond, kunsat_method)#
class pyiwfm.io.unsaturated_zone.UnsatZoneMainConfig(version='', n_layers=0, solver_tolerance=1e-08, max_iterations=2000, budget_file=None, zbudget_file=None, final_results_file=None, n_parametric_grids=0, coord_factor=1.0, thickness_factor=1.0, hyd_cond_factor=1.0, time_unit='', element_data=<factory>, initial_soil_moisture=<factory>)[source]#

Bases: object

Configuration parsed from Unsaturated Zone component main file.

Variables:
  • version (str) – File format version

  • n_layers (int) – Number of unsaturated zone layers (0=disabled)

  • solver_tolerance (float) – Solver convergence tolerance

  • max_iterations (int) – Maximum solver iterations

  • budget_file (pathlib.Path | None) – Path to HDF5 budget output file

  • zbudget_file (pathlib.Path | None) – Path to HDF5 zone budget output file

  • final_results_file (pathlib.Path | None) – Path to final simulation results file

  • n_parametric_grids (int) – Number of parametric grids (0=direct input)

  • coord_factor (float) – Conversion factor for x-y coordinates

  • thickness_factor (float) – Conversion factor for layer thickness

  • hyd_cond_factor (float) – Conversion factor for hydraulic conductivity

  • time_unit (str) – Time unit for hydraulic conductivity

  • element_data (list[pyiwfm.io.unsaturated_zone.UnsatZoneElementData]) – Per-element unsaturated zone parameters

  • initial_soil_moisture (dict[int, numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]]) – Initial soil moisture per element per layer. Maps element_id -> moisture array. Key 0 means uniform for all.

version: str = ''#
n_layers: int = 0#
solver_tolerance: float = 1e-08#
max_iterations: int = 2000#
budget_file: Path | None = None#
zbudget_file: Path | None = None#
final_results_file: Path | None = None#
n_parametric_grids: int = 0#
coord_factor: float = 1.0#
thickness_factor: float = 1.0#
hyd_cond_factor: float = 1.0#
time_unit: str = ''#
element_data: list[UnsatZoneElementData]#
initial_soil_moisture: dict[int, ndarray[tuple[Any, ...], dtype[float64]]]#
__init__(version='', n_layers=0, solver_tolerance=1e-08, max_iterations=2000, budget_file=None, zbudget_file=None, final_results_file=None, n_parametric_grids=0, coord_factor=1.0, thickness_factor=1.0, hyd_cond_factor=1.0, time_unit='', element_data=<factory>, initial_soil_moisture=<factory>)#
class pyiwfm.io.unsaturated_zone.UnsatZoneMainReader[source]#

Bases: object

Reader for IWFM unsaturated zone component main file.

Parses the full file including header configuration, per-element parameter data (when NGROUP=0), and initial conditions.

__init__()[source]#
read(filepath, base_dir=None)[source]#

Read unsaturated zone main file.

Parameters:
  • filepath (Path | str) – Path to the unsaturated zone main file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

UnsatZoneMainConfig with configuration data

Return type:

UnsatZoneMainConfig

pyiwfm.io.unsaturated_zone.read_unsaturated_zone_main(filepath, base_dir=None)[source]#

Read IWFM unsaturated zone component main file.

Parameters:
  • filepath (Path | str) – Path to the unsaturated zone main file

  • base_dir (Path | None) – Base directory for resolving relative paths

Returns:

UnsatZoneMainConfig with configuration data

Return type:

UnsatZoneMainConfig

Unsaturated Zone Writer#

Writer for IWFM unsaturated zone component files using Jinja2 templates.

Unsaturated Zone Component Writer for IWFM models.

This module provides the writer for IWFM unsaturated zone component files, generating the main file with solver parameters, per-element layer properties, and initial soil moisture conditions.

class pyiwfm.io.unsaturated_zone_writer.UnsatZoneWriterConfig(output_dir, version='4.0', unsatzone_subdir='UnsatZone', main_file='UnsatZone_MAIN.dat', budget_file='../Results/UZBud.hdf', zbudget_file='../Results/UZZBud.hdf', final_results_file='../Results/FinalUZ.out')[source]#

Bases: BaseComponentWriterConfig

Configuration for unsaturated zone component file writing.

Variables:
  • output_dir (pathlib.Path) – Base output directory

  • unsatzone_subdir (str) – Subdirectory name for unsat zone files

  • version (str) – IWFM component version

  • main_file (str) – Main file name

  • budget_file (str) – Budget output file path

  • zbudget_file (str) – Zone budget output file path

  • final_results_file (str) – Final results output file path

unsatzone_subdir: str = 'UnsatZone'#
main_file: str = 'UnsatZone_MAIN.dat'#
budget_file: str = '../Results/UZBud.hdf'#
zbudget_file: str = '../Results/UZZBud.hdf'#
final_results_file: str = '../Results/FinalUZ.out'#
property unsatzone_dir: Path#

Get the unsaturated zone subdirectory path.

__init__(output_dir, version='4.0', unsatzone_subdir='UnsatZone', main_file='UnsatZone_MAIN.dat', budget_file='../Results/UZBud.hdf', zbudget_file='../Results/UZZBud.hdf', final_results_file='../Results/FinalUZ.out')#
class pyiwfm.io.unsaturated_zone_writer.UnsatZoneComponentWriter(model, config, template_engine=None)[source]#

Bases: TemplateWriter

Writer for IWFM Unsaturated Zone Component files.

Writes the unsaturated zone main file including solver parameters, per-element layer properties, and initial soil moisture conditions.

Example

>>> from pyiwfm.io.unsaturated_zone_writer import (
...     UnsatZoneComponentWriter, UnsatZoneWriterConfig,
... )
>>> config = UnsatZoneWriterConfig(output_dir=Path("model/Simulation"))
>>> writer = UnsatZoneComponentWriter(model, config)
>>> files = writer.write_all()
__init__(model, config, template_engine=None)[source]#

Initialize the template writer.

Parameters:
  • output_dir – Directory for output files

  • template_engine (TemplateEngine | None) – Optional TemplateEngine instance

  • comment_metadata – Optional CommentMetadata for comment preservation

property format: str#

Return the file format identifier.

write(data=None)[source]#

Write all unsaturated zone files.

write_all(write_defaults=True)[source]#

Write all unsaturated zone component files.

Parameters:

write_defaults (bool) – If True, write default files even when no component is loaded.

Returns:

Mapping of file type to output path.

Return type:

dict[str, Path]

write_main()[source]#

Write the main unsaturated zone control file.

Returns:

Path to written file.

Return type:

Path

pyiwfm.io.unsaturated_zone_writer.write_unsaturated_zone_component(model, output_dir, config=None)[source]#

Write unsaturated zone component files for a model.

Parameters:
  • model (IWFMModel) – Model to write.

  • output_dir (Path or str) – Output directory.

  • config (UnsatZoneWriterConfig, optional) – File configuration.

Returns:

Mapping of file type to output path.

Return type:

dict[str, Path]

Supply Adjustment#

Reader and writer for IWFM supply adjustment (irrigation) files.

Supply adjustment file reader and writer for IWFM.

IWFM supply adjustment files use the IntTSDataInFileType format:
  • Header comments

  • NCOLADJ (number of columns)

  • NSPADJ (time step update frequency)

  • NFQADJ (data repetition frequency)

  • DSSFL (DSS filename, blank for inline)

  • Data lines: timestamp + integer adjustment codes (00, 01, 10)

The adjustment codes are two-digit integers:
  • 1st digit: 0 = no agriculture adjustment, 1 = adjust agriculture

  • 2nd digit: 0 = no urban adjustment, 1 = adjust urban

  • Combined: 00=none, 01=urban only, 10=ag only, 11=both (deprecated)

class pyiwfm.io.supply_adjust.SupplyAdjustment(n_columns=0, nsp=1, nfq=0, dss_file='', times=<factory>, values=<factory>, header_lines=<factory>)[source]#

Bases: object

Parsed supply adjustment specification data.

Variables:
  • n_columns (int) – Number of adjustment columns (NCOLADJ).

  • nsp (int) – Time step update frequency (NSPADJ).

  • nfq (int) – Data repetition frequency (NFQADJ).

  • dss_file (str) – DSS filename (empty string if inline data).

  • times (list[datetime.datetime]) – List of timestamps for each data row.

  • values (list[list[int]]) – List of rows, each row is a list of integer adjustment codes.

  • header_lines (list[str]) – Original header comment lines.

n_columns: int = 0#
nsp: int = 1#
nfq: int = 0#
dss_file: str = ''#
times: list[datetime]#
values: list[list[int]]#
header_lines: list[str]#
__init__(n_columns=0, nsp=1, nfq=0, dss_file='', times=<factory>, values=<factory>, header_lines=<factory>)#
pyiwfm.io.supply_adjust.read_supply_adjustment(filepath)[source]#

Read a supply adjustment file.

Parses the IWFM integer time series format (NCOL, NSP, NFQ, DSSFL) followed by timestamp + integer data rows.

Parameters:

filepath (Path | str) – Path to the supply adjustment file.

Returns:

SupplyAdjustment with parsed data.

Raises:
Return type:

SupplyAdjustment

pyiwfm.io.supply_adjust.write_supply_adjustment(data, filepath)[source]#

Write a supply adjustment file.

Writes the IWFM integer time series format: NCOL, NSP, NFQ, DSSFL, followed by timestamp + integer data rows.

Parameters:
Returns:

Path to the written file.

Return type:

Path

Time Series I/O#

Time Series ASCII Module#

The time series ASCII module provides readers and writers for IWFM ASCII time series files.

ASCII time series I/O handlers for IWFM model files.

This module provides functions for reading and writing IWFM ASCII time series files using the standard 16-character timestamp format (MM/DD/YYYY_HH:MM). IWFM uses 24:00 to represent midnight (end of day).

pyiwfm.io.timeseries_ascii.format_iwfm_timestamp(dt)[source]#

Format a datetime as an IWFM timestamp string.

IWFM uses the format MM/DD/YYYY_HH:MM (16 characters). Midnight (00:00) is represented as 24:00 of the previous day. For example, 2050-11-01 00:00 becomes 10/31/2050_24:00.

Parameters:

dt (datetime | datetime64) – datetime object or numpy datetime64

Returns:

Formatted timestamp string (16 chars)

Return type:

str

pyiwfm.io.timeseries_ascii.parse_iwfm_timestamp(ts_str)[source]#

Parse an IWFM timestamp string to datetime.

IWFM timestamps are exactly 16 characters: MM/DD/YYYY_HH:MM The 24:00 convention means midnight at the end of the given day, which is converted to 00:00 of the next day internally.

Parameters:

ts_str (str) – Timestamp string (16 chars, MM/DD/YYYY_HH:MM)

Returns:

datetime object

Raises:

ValueError – If timestamp format is invalid

Return type:

datetime

pyiwfm.io.timeseries_ascii.parse_iwfm_datetime(date_str)[source]#

Parse an IWFM datetime string to Python datetime (lenient).

Unlike parse_iwfm_timestamp(), which raises on invalid input, this function tries 8 format variants and returns None on failure. Handles the IWFM _24:00 end-of-day convention as well as ISO and date-only formats.

Parameters:

date_str (str) – Datetime string in any recognised IWFM or ISO format.

Returns:

Parsed datetime, or None if no format matched.

Return type:

datetime or None

pyiwfm.io.timeseries_ascii.iwfm_date_to_iso(date_str)[source]#

Convert IWFM date string to ISO YYYY-MM-DD format.

Parameters:

date_str (str) – IWFM datetime string (e.g. "10/01/1921_24:00").

Returns:

ISO date string (e.g. "1921-10-02"), or the original string unchanged if parsing fails.

Return type:

str

class pyiwfm.io.timeseries_ascii.TimeSeriesFileConfig(n_columns, column_ids, units='', factor=1.0, header_lines=None)[source]#

Bases: object

Configuration for an IWFM time series file.

Variables:
  • n_columns (int) – Number of value columns

  • column_ids (list[str | int]) – List of location/column identifiers

  • units (str) – Units string for values

  • factor (float) – Conversion factor to apply to values

  • header_lines (list[str] | None) – Header comment lines

n_columns: int#
column_ids: list[str | int]#
units: str = ''#
factor: float = 1.0#
header_lines: list[str] | None = None#
__init__(n_columns, column_ids, units='', factor=1.0, header_lines=None)#
class pyiwfm.io.timeseries_ascii.TimeSeriesWriter(value_format='%14.6f', timestamp_format='%m/%d/%Y_%H:%M')[source]#

Bases: object

Writer for IWFM ASCII time series files.

IWFM time series files have the format:

C Header comments NDATA / Number of data columns FACTOR / Conversion factor MM/DD/YYYY_HH:MM:SS val1 val2 val3 …

Example

>>> writer = TimeSeriesWriter()
>>> writer.write(
...     filepath="pumping.dat",
...     times=times,
...     values=values,
...     column_ids=[1, 2, 3],
...     units="TAF"
... )
__init__(value_format='%14.6f', timestamp_format='%m/%d/%Y_%H:%M')[source]#

Initialize the time series writer.

Parameters:
  • value_format (str) – Printf-style format for values

  • timestamp_format (str) – strftime format for timestamps

write(filepath, times, values, column_ids=None, units='', factor=1.0, header=None)[source]#

Write time series data to an IWFM ASCII file.

Parameters:
  • filepath (Path | str) – Output file path

  • times (Sequence[datetime] | ndarray[tuple[Any, ...], dtype[datetime64]]) – Sequence of datetime values

  • values (ndarray[tuple[Any, ...], dtype[float64]]) – 2D array of values (n_times, n_columns) or 1D for single column

  • column_ids (list[str | int] | None) – Optional list of column identifiers

  • units (str) – Units string for header

  • factor (float) – Conversion factor (written to file)

  • header (str | None) – Optional header comment

write_from_timeseries(filepath, ts, header=None, factor=1.0)[source]#

Write a TimeSeries object to file.

Parameters:
  • filepath (Path | str) – Output file path

  • ts (TimeSeries) – TimeSeries object

  • header (str | None) – Optional header comment

  • factor (float) – Conversion factor

write_from_collection(filepath, collection, header=None, factor=1.0)[source]#

Write a TimeSeriesCollection to a single file.

All time series must have the same timestamps.

Parameters:
  • filepath (Path | str) – Output file path

  • collection (TimeSeriesCollection) – TimeSeriesCollection object

  • header (str | None) – Optional header comment

  • factor (float) – Conversion factor

class pyiwfm.io.timeseries_ascii.TimeSeriesReader[source]#

Bases: object

Reader for IWFM ASCII time series files.

Example

>>> reader = TimeSeriesReader()
>>> times, values, config = reader.read("pumping.dat")
read(filepath)[source]#

Read time series data from an IWFM ASCII file.

Parameters:

filepath (Path | str) – Input file path

Returns:

Tuple of (times, values, config)

Return type:

tuple[list[datetime], ndarray[tuple[Any, …], dtype[float64]], TimeSeriesFileConfig]

read_to_timeseries(filepath, name='', location='')[source]#

Read file and return as TimeSeries object.

Parameters:
  • filepath (Path | str) – Input file path

  • name (str) – Name for the time series

  • location (str) – Location identifier

Returns:

TimeSeries object

Return type:

TimeSeries

read_to_collection(filepath, column_ids=None, variable='')[source]#

Read file and return as TimeSeriesCollection.

Parameters:
  • filepath (Path | str) – Input file path

  • column_ids (list[str] | None) – Optional column identifiers

  • variable (str) – Variable name for the collection

Returns:

TimeSeriesCollection object

Return type:

TimeSeriesCollection

pyiwfm.io.timeseries_ascii.write_timeseries(filepath, times, values, column_ids=None, units='', factor=1.0, header=None)[source]#

Write time series data to an IWFM ASCII file.

Parameters:
pyiwfm.io.timeseries_ascii.read_timeseries(filepath)[source]#

Read time series data from an IWFM ASCII file.

Parameters:

filepath (Path | str) – Input file path

Returns:

Tuple of (times, values, config)

Return type:

tuple[list[datetime], ndarray[tuple[Any, …], dtype[float64]], TimeSeriesFileConfig]

Time Series Module#

Unified time series reader supporting multiple formats.

Unified Time Series I/O Infrastructure for IWFM.

This module provides a unified interface for reading and writing time series data across all supported formats (ASCII, DSS, HDF5). It enables format-agnostic handling of IWFM time series data with automatic format detection and conversion.

class pyiwfm.io.timeseries.TimeSeriesFileType(*values)[source]#

Bases: Enum

Supported time series file formats.

ASCII = 'ascii'#
DSS = 'dss'#
HDF5 = 'hdf5'#
BINARY = 'binary'#
class pyiwfm.io.timeseries.TimeUnit(*values)[source]#

Bases: Enum

Standard time units for IWFM.

SECOND = 'second'#
MINUTE = 'minute'#
HOUR = 'hour'#
DAY = 'day'#
WEEK = 'week'#
MONTH = 'month'#
YEAR = 'year'#
class pyiwfm.io.timeseries.TimeSeriesMetadata(file_type=TimeSeriesFileType.ASCII, n_columns=0, column_ids=<factory>, variable_name='', data_unit='', time_unit='', conversion_factor=1.0, is_rate_data=False, recycling_interval=0, start_time=None, end_time=None, n_timesteps=0, source_path=None)[source]#

Bases: object

Metadata for a time series file.

Variables:
  • file_type (pyiwfm.io.timeseries.TimeSeriesFileType) – Type of the source file

  • n_columns (int) – Number of data columns

  • column_ids (list[str | int]) – Identifier for each column (location/entity ID)

  • variable_name (str) – Name of the variable (e.g., “Pumping”, “Head”)

  • data_unit (str) – Physical units of the data

  • time_unit (str) – Time unit for the series

  • conversion_factor (float) – Factor applied during read

  • is_rate_data (bool) – Whether data represents rates (flow/time)

  • recycling_interval (int) – Number of months to recycle (0 = no recycling)

  • start_time (datetime.datetime | None) – Start of time series

  • end_time (datetime.datetime | None) – End of time series

  • n_timesteps (int) – Number of timesteps

  • source_path (pathlib.Path | None) – Original file path

file_type: TimeSeriesFileType = 'ascii'#
n_columns: int = 0#
column_ids: list[str | int]#
variable_name: str = ''#
data_unit: str = ''#
time_unit: str = ''#
conversion_factor: float = 1.0#
is_rate_data: bool = False#
recycling_interval: int = 0#
start_time: datetime | None = None#
end_time: datetime | None = None#
n_timesteps: int = 0#
source_path: Path | None = None#
__init__(file_type=TimeSeriesFileType.ASCII, n_columns=0, column_ids=<factory>, variable_name='', data_unit='', time_unit='', conversion_factor=1.0, is_rate_data=False, recycling_interval=0, start_time=None, end_time=None, n_timesteps=0, source_path=None)#
class pyiwfm.io.timeseries.UnifiedTimeSeriesConfig(filepath, file_type=None, n_columns=None, column_ids=None, data_unit='', time_unit='', conversion_factor=1.0, is_rate_data=False, recycling_interval=0, start_filter=None, end_filter=None, dss_pathname=None, hdf5_dataset=None)[source]#

Bases: object

Configuration for unified time series reading.

Variables:
  • filepath (pathlib.Path) – Path to the time series file

  • file_type (pyiwfm.io.timeseries.TimeSeriesFileType | None) – Type of file (auto-detected if None)

  • n_columns (int | None) – Expected number of columns (for validation)

  • column_ids (list[int] | None) – Entity IDs for each column

  • data_unit (str) – Expected data unit

  • time_unit (str) – Expected time unit

  • conversion_factor (float) – Factor to apply to values

  • is_rate_data (bool) – Whether to normalize by timestep

  • recycling_interval (int) – Recycle period (0 = no recycling)

  • start_filter (datetime.datetime | None) – Only read data after this time

  • end_filter (datetime.datetime | None) – Only read data before this time

  • dss_pathname (str | None) – DSS pathname pattern (for DSS files)

  • hdf5_dataset (str | None) – HDF5 dataset path (for HDF5 files)

filepath: Path#
file_type: TimeSeriesFileType | None = None#
n_columns: int | None = None#
column_ids: list[int] | None = None#
data_unit: str = ''#
time_unit: str = ''#
conversion_factor: float = 1.0#
is_rate_data: bool = False#
recycling_interval: int = 0#
start_filter: datetime | None = None#
end_filter: datetime | None = None#
dss_pathname: str | None = None#
hdf5_dataset: str | None = None#
__init__(filepath, file_type=None, n_columns=None, column_ids=None, data_unit='', time_unit='', conversion_factor=1.0, is_rate_data=False, recycling_interval=0, start_filter=None, end_filter=None, dss_pathname=None, hdf5_dataset=None)#
class pyiwfm.io.timeseries.BaseTimeSeriesReader[source]#

Bases: ABC

Abstract base class for time series readers.

abstractmethod read(filepath, **kwargs)[source]#

Read time series data from file.

Parameters:
  • filepath (Path | str) – Path to the file

  • **kwargs (Any) – Format-specific options

Returns:

Tuple of (times, values, metadata)

Return type:

tuple[ndarray[tuple[Any, …], dtype[datetime64]], ndarray[tuple[Any, …], dtype[float64]], TimeSeriesMetadata]

abstractmethod read_metadata(filepath, **kwargs)[source]#

Read only metadata without loading full data.

Parameters:
  • filepath (Path | str) – Path to the file

  • **kwargs (Any) – Format-specific options

Returns:

TimeSeriesMetadata

Return type:

TimeSeriesMetadata

class pyiwfm.io.timeseries.AsciiTimeSeriesAdapter[source]#

Bases: BaseTimeSeriesReader

Adapter for ASCII time series files.

read(filepath, **kwargs)[source]#

Read ASCII time series file.

read_metadata(filepath, **kwargs)[source]#

Read ASCII file metadata (requires reading header).

class pyiwfm.io.timeseries.DssTimeSeriesAdapter[source]#

Bases: BaseTimeSeriesReader

Adapter for HEC-DSS time series files.

read(filepath, **kwargs)[source]#

Read DSS time series file.

read_metadata(filepath, **kwargs)[source]#

Read DSS file metadata.

class pyiwfm.io.timeseries.Hdf5TimeSeriesAdapter[source]#

Bases: BaseTimeSeriesReader

Adapter for HDF5 time series files.

read(filepath, **kwargs)[source]#

Read HDF5 time series file.

read_metadata(filepath, **kwargs)[source]#

Read HDF5 file metadata.

class pyiwfm.io.timeseries.UnifiedTimeSeriesReader[source]#

Bases: object

Unified reader for all time series formats.

This class provides a single interface for reading time series data from ASCII, DSS, and HDF5 files with automatic format detection and consistent output format.

Example

>>> reader = UnifiedTimeSeriesReader()
>>> config = UnifiedTimeSeriesConfig(filepath=Path("pumping.dat"))
>>> times, values, metadata = reader.read(config)
>>> # Or with auto-detection
>>> times, values, metadata = reader.read_file("pumping.dat")
__init__()[source]#

Initialize the unified reader with format adapters.

read(config)[source]#

Read time series data using configuration.

Parameters:

config (UnifiedTimeSeriesConfig) – UnifiedTimeSeriesConfig with file path and options

Returns:

Tuple of (times, values, metadata)

Return type:

tuple[ndarray[tuple[Any, …], dtype[datetime64]], ndarray[tuple[Any, …], dtype[float64]], TimeSeriesMetadata]

read_file(filepath, file_type=None, **kwargs)[source]#

Convenience method to read a time series file.

Parameters:
  • filepath (Path | str) – Path to the file

  • file_type (TimeSeriesFileType | None) – File type (auto-detected if None)

  • **kwargs (Any) – Additional options passed to adapter

Returns:

Tuple of (times, values, metadata)

Return type:

tuple[ndarray[tuple[Any, …], dtype[datetime64]], ndarray[tuple[Any, …], dtype[float64]], TimeSeriesMetadata]

read_to_collection(filepath, column_ids=None, variable='', file_type=None, **kwargs)[source]#

Read file and return as TimeSeriesCollection.

Parameters:
  • filepath (Path | str) – Path to the file

  • column_ids (list[str] | None) – Optional column identifiers

  • variable (str) – Variable name for the collection

  • file_type (TimeSeriesFileType | None) – File type (auto-detected if None)

  • **kwargs (Any) – Additional options

Returns:

TimeSeriesCollection

Return type:

TimeSeriesCollection

read_metadata(filepath, file_type=None, **kwargs)[source]#

Read only metadata without loading full data.

Parameters:
  • filepath (Path | str) – Path to the file

  • file_type (TimeSeriesFileType | None) – File type (auto-detected if None)

  • **kwargs (Any) – Additional options

Returns:

TimeSeriesMetadata

Return type:

TimeSeriesMetadata

class pyiwfm.io.timeseries.RecyclingTimeSeriesReader(base_reader=None)[source]#

Bases: object

Reader that handles IWFM time series recycling.

IWFM supports repeating time series patterns. For example, monthly data for a single year can be recycled across the entire simulation period.

__init__(base_reader=None)[source]#

Initialize with optional base reader.

read_with_recycling(filepath, target_times, recycling_interval=12, **kwargs)[source]#

Read time series and recycle to match target times.

Parameters:
  • filepath (Path | str) – Source file path

  • target_times (ndarray[tuple[Any, ...], dtype[datetime64]]) – Target timestamps to generate values for

  • recycling_interval (int) – Number of months to recycle (12 = yearly)

  • **kwargs (Any) – Additional read options

Returns:

Values array matching target_times

Return type:

ndarray[tuple[Any, …], dtype[float64]]

pyiwfm.io.timeseries.detect_timeseries_format(filepath)[source]#

Detect time series file format.

Parameters:

filepath (Path | str) – Path to file

Returns:

TimeSeriesFileType enum value

Return type:

TimeSeriesFileType

pyiwfm.io.timeseries.read_timeseries_unified(filepath, file_type=None, **kwargs)[source]#

Read time series from any supported format.

Parameters:
  • filepath (Path | str) – Path to file

  • file_type (TimeSeriesFileType | None) – Optional explicit file type

  • **kwargs (Any) – Format-specific options

Returns:

Tuple of (times, values, metadata)

Return type:

tuple[ndarray[tuple[Any, …], dtype[datetime64]], ndarray[tuple[Any, …], dtype[float64]], TimeSeriesMetadata]

pyiwfm.io.timeseries.get_timeseries_metadata(filepath, file_type=None, **kwargs)[source]#

Get time series metadata without reading full data.

Parameters:
  • filepath (Path | str) – Path to file

  • file_type (TimeSeriesFileType | None) – Optional explicit file type

  • **kwargs (Any) – Format-specific options

Returns:

TimeSeriesMetadata

Return type:

TimeSeriesMetadata

Time Series Writer#

Writer for IWFM time series data files.

Generic IWFM time series data file writer.

All IWFM time series files (pumping, inflows, ET, precip, diversions, crop coefficients, return flow fractions, reuse fractions, irrigation periods, ag water demand, max lake elevation, stream surface area, etc.) share a common structure:

  • Comment header

  • NCOL, FACT, [TUNIT], NSP, NFQ, DSSFL

  • Optional column mapping section

  • Date-indexed data rows or DSS pathnames

This module provides a single writer + dataclass that handles all variants.

class pyiwfm.io.timeseries_writer.DSSPathItem(index, path)[source]#

Bases: object

A single DSS pathname entry for column mapping.

index: int#
path: str#
__init__(index, path)#
class pyiwfm.io.timeseries_writer.TimeSeriesDataConfig(title='', ncol=0, factor=1.0, time_unit='', has_time_unit=False, nsp=1, nfq=0, dss_file='', ncol_tag='NCOL', factor_tag='FACT', time_unit_tag='TUNIT', nsp_tag='NSP', nfq_tag='NFQ', description_lines=<factory>, column_mapping=<factory>, column_header='', dates=None, data=None, data_header='Time Series Data', data_fmt='%14.6f', dss_paths=<factory>)[source]#

Bases: object

Configuration for a generic IWFM time series data file.

This covers all IWFM TS file types: pumping, inflows, ET, precip, diversions, crop coefficients, return flow fractions, reuse fractions, irrigation periods, ag water demand, max lake elevation, stream surface area, generic moisture, etc.

title: str = ''#
ncol: int = 0#
factor: float = 1.0#
time_unit: str = ''#
has_time_unit: bool = False#
nsp: int = 1#
nfq: int = 0#
dss_file: str = ''#
ncol_tag: str = 'NCOL'#
factor_tag: str = 'FACT'#
time_unit_tag: str = 'TUNIT'#
nsp_tag: str = 'NSP'#
nfq_tag: str = 'NFQ'#
description_lines: list[str]#
column_mapping: list[str]#
column_header: str = ''#
dates: list[str] | None = None#
data: NDArray[np.float64] | None = None#
data_header: str = 'Time Series Data'#
data_fmt: str = '%14.6f'#
dss_paths: list[DSSPathItem]#
property use_dss: bool#

Whether to write DSS pathname references instead of inline data.

__init__(title='', ncol=0, factor=1.0, time_unit='', has_time_unit=False, nsp=1, nfq=0, dss_file='', ncol_tag='NCOL', factor_tag='FACT', time_unit_tag='TUNIT', nsp_tag='NSP', nfq_tag='NFQ', description_lines=<factory>, column_mapping=<factory>, column_header='', dates=None, data=None, data_header='Time Series Data', data_fmt='%14.6f', dss_paths=<factory>)#
class pyiwfm.io.timeseries_writer.IWFMTimeSeriesDataWriter(engine=None)[source]#

Bases: object

Generic writer for IWFM time series data files.

Handles: TSPumping, StreamInflow, DiversionData, Precip, ET, CropCoeff, IrrigPeriod, ReturnFlowFrac, ReuseFrac, AgWaterDemand, MaxLakeElev, StreamSurfaceArea, GenericMoisture, etc.

Example

>>> from pyiwfm.io.timeseries_writer import (
...     IWFMTimeSeriesDataWriter, TimeSeriesDataConfig,
... )
>>> config = TimeSeriesDataConfig(
...     title="Pumping Time Series",
...     ncol=3,
...     factor=1.0,
...     nsp=1,
...     nfq=0,
...     ncol_tag="NCOLPUMP",
...     factor_tag="FACTPUMP",
...     nsp_tag="NSPPUMP",
...     nfq_tag="NFQPUMP",
...     dates=["10/01/1990_24:00", "10/02/1990_24:00"],
...     data=np.array([[100.0, 200.0, 300.0],
...                    [110.0, 210.0, 310.0]]),
... )
>>> writer = IWFMTimeSeriesDataWriter()
>>> writer.write(config, Path("TSPumping.dat"))
__init__(engine=None)[source]#
write(config, filepath)[source]#

Write a complete IWFM time series data file.

Parameters:
Returns:

Path to written file

Return type:

Path

write_dss_mode(config, filepath)[source]#

Write with DSS pathname references instead of inline data.

This is a convenience wrapper that ensures use_dss is True.

Parameters:
  • config (TimeSeriesDataConfig) – File specification (must have dss_paths populated)

  • filepath (Path) – Output file path

Returns:

Path to written file

Return type:

Path

pyiwfm.io.timeseries_writer.make_pumping_ts_config(ncol, factor=1.0, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig pre-configured for pumping TS files.

pyiwfm.io.timeseries_writer.make_stream_inflow_ts_config(ncol, factor=1.0, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig pre-configured for stream inflow TS.

pyiwfm.io.timeseries_writer.make_diversion_ts_config(ncol, factor=1.0, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig pre-configured for diversion data TS.

pyiwfm.io.timeseries_writer.make_precip_ts_config(ncol, factor=1.0, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig pre-configured for precipitation TS.

pyiwfm.io.timeseries_writer.make_et_ts_config(ncol, factor=1.0, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig pre-configured for ET TS files.

pyiwfm.io.timeseries_writer.make_crop_coeff_ts_config(ncol, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig pre-configured for crop coeff TS.

pyiwfm.io.timeseries_writer.make_return_flow_ts_config(ncol, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig for return flow fraction TS.

pyiwfm.io.timeseries_writer.make_reuse_ts_config(ncol, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig for reuse fraction TS.

pyiwfm.io.timeseries_writer.make_irig_period_ts_config(ncol, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig for irrigation period TS.

pyiwfm.io.timeseries_writer.make_ag_water_demand_ts_config(ncol, factor=1.0, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig for ag water demand TS.

pyiwfm.io.timeseries_writer.make_max_lake_elev_ts_config(ncol, factor=1.0, nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig for max lake elevation TS.

pyiwfm.io.timeseries_writer.make_stream_surface_area_ts_config(ncol, factor=1.0, has_time_unit=True, time_unit='1DAY', nsp=1, nfq=0, **kwargs)[source]#

Create a TimeSeriesDataConfig for stream surface area TS.

Budget / Results I/O#

Budget Module#

Reader for IWFM budget output files (HDF5 and binary formats).

Budget file reader for IWFM simulation output.

This module provides classes for reading IWFM budget binary and HDF5 files, which contain water balance data for groundwater, streams, lakes, root zone, and other model components.

Example

Read a groundwater budget file:

>>> from pyiwfm.io.budget import BudgetReader
>>> reader = BudgetReader("GW_Budget.hdf")
>>> print(reader.descriptor)
'GROUNDWATER BUDGET'
>>> print(reader.locations)
['Subregion 1', 'Subregion 2', ...]
>>> df = reader.get_dataframe(location="Subregion 1")
class pyiwfm.io.budget.TimeStepInfo(track_time=True, delta_t=1.0, delta_t_minutes=1440, unit='1DAY', start_datetime=None, start_time=0.0, n_timesteps=0)[source]#

Bases: object

Time step information from budget file.

track_time: bool = True#
delta_t: float = 1.0#
delta_t_minutes: int = 1440#
unit: str = '1DAY'#
start_datetime: datetime | None = None#
start_time: float = 0.0#
n_timesteps: int = 0#
__init__(track_time=True, delta_t=1.0, delta_t_minutes=1440, unit='1DAY', start_datetime=None, start_time=0.0, n_timesteps=0)#
class pyiwfm.io.budget.ASCIIOutputInfo(title_len=160, n_titles=0, titles=<factory>, title_persist=<factory>, format_spec='', n_column_header_lines=3)[source]#

Bases: object

ASCII output formatting information.

title_len: int = 160#
n_titles: int = 0#
titles: list[str]#
title_persist: list[bool]#
format_spec: str = ''#
n_column_header_lines: int = 3#
__init__(title_len=160, n_titles=0, titles=<factory>, title_persist=<factory>, format_spec='', n_column_header_lines=3)#
class pyiwfm.io.budget.LocationData(n_columns=0, storage_units=0, column_headers=<factory>, column_types=<factory>, column_widths=<factory>, dss_pathnames=<factory>, dss_data_types=<factory>)[source]#

Bases: object

Data structure for a single budget location.

n_columns: int = 0#
storage_units: int = 0#
column_headers: list[str]#
column_types: list[int]#
column_widths: list[int]#
dss_pathnames: list[str]#
dss_data_types: list[int]#
__init__(n_columns=0, storage_units=0, column_headers=<factory>, column_types=<factory>, column_widths=<factory>, dss_pathnames=<factory>, dss_data_types=<factory>)#
class pyiwfm.io.budget.BudgetHeader(descriptor='', timestep=<factory>, n_areas=0, areas=<factory>, ascii_output=<factory>, n_locations=0, location_names=<factory>, location_data=<factory>, file_position=0)[source]#

Bases: object

Complete budget file header information.

descriptor: str = ''#
timestep: TimeStepInfo#
n_areas: int = 0#
areas: ndarray[tuple[Any, ...], dtype[float64]]#
ascii_output: ASCIIOutputInfo#
n_locations: int = 0#
location_names: list[str]#
location_data: list[LocationData]#
file_position: int = 0#
__init__(descriptor='', timestep=<factory>, n_areas=0, areas=<factory>, ascii_output=<factory>, n_locations=0, location_names=<factory>, location_data=<factory>, file_position=0)#
pyiwfm.io.budget.julian_to_datetime(julian_day)[source]#

Convert Julian day to Python datetime.

IWFM uses astronomical Julian dates. Excel Julian dates have an offset of 2415020 days.

pyiwfm.io.budget.excel_julian_to_datetime(excel_julian)[source]#

Convert Excel-style Julian day to Python datetime.

Excel day 1 = January 1, 1900.

class pyiwfm.io.budget.BudgetReader(filepath)[source]#

Bases: object

Reader for IWFM budget files (binary or HDF5 format).

This class reads budget output files generated by IWFM simulation, including groundwater, stream, lake, and root zone budgets.

Parameters:

filepath (str or Path) – Path to the budget file (.bin or .hdf/.h5).

Variables:
  • filepath (Path) – Path to the budget file.

  • format (str) – File format (‘binary’ or ‘hdf5’).

  • header (BudgetHeader) – Parsed header information.

  • descriptor (str) – Budget type descriptor (e.g., ‘GROUNDWATER BUDGET’).

  • locations (list[str]) – List of location names.

  • n_timesteps (int) – Number of time steps in the file.

Examples

>>> reader = BudgetReader("GW_Budget.hdf")
>>> print(reader.descriptor)
'GROUNDWATER BUDGET'
>>> print(reader.locations)
['Subregion 1', 'Subregion 2', 'Subregion 3']
>>>
>>> # Get column headers for a location
>>> headers = reader.get_column_headers("Subregion 1")
>>> print(headers)
['Deep Percolation', 'Stream-GW Interaction', ...]
>>>
>>> # Read data as numpy array
>>> times, data = reader.get_values("Subregion 1")
>>>
>>> # Read data as pandas DataFrame
>>> df = reader.get_dataframe("Subregion 1")
__init__(filepath)[source]#
property descriptor: str#

Return budget type descriptor.

property locations: list[str]#

Return list of location names.

property n_locations: int#

Return number of locations.

property n_timesteps: int#

Return number of time steps.

get_location_index(location)[source]#

Get location index from name or index.

get_column_headers(location=0)[source]#

Get column headers for a location.

Parameters:

location (str or int) – Location name or index.

Returns:

List of column header names.

Return type:

list[str]

get_values(location=0, columns=None)[source]#

Read budget values for a location.

Parameters:
  • location (str or int) – Location name or index.

  • columns (list[int], optional) – Column indices to read (0-based). If None, reads all columns.

Returns:

Tuple of (times, values) where: - times: 1D array of time values (or datetimes as floats) - values: 2D array of shape (n_timesteps, n_columns)

Return type:

tuple[NDArray[np.float64], NDArray[np.float64]]

get_dataframe(location=0, columns=None, *, length_factor=1.0, area_factor=1.0, volume_factor=1.0)[source]#

Read budget values as a pandas DataFrame.

When all conversion factors are left at their default of 1.0 the raw simulation values are returned unchanged. Pass non-unity factors (FACTLTOU / FACTAROU / FACTVLOU) to get unit-converted output — each column is multiplied by the factor that matches its IWFM data-type code (volume, area, or length).

Parameters:
  • location (str or int) – Location name or index.

  • columns (list, optional) – Column indices or names to read. If None, reads all columns.

  • length_factor (float) – Multiplier for length columns (type 5). Default 1.0.

  • area_factor (float) – Multiplier for area columns (type 4). Default 1.0.

  • volume_factor (float) – Multiplier for volume columns (types 1-3, 6-11). Default 1.0.

Returns:

DataFrame with datetime index and budget columns.

Return type:

pd.DataFrame

get_all_dataframes()[source]#

Read budget data for all locations as DataFrames.

Returns:

Dictionary mapping location name to DataFrame.

Return type:

dict[str, pd.DataFrame]

get_monthly_averages(location=0, columns=None)[source]#

Calculate monthly averages for budget data.

Parameters:
  • location (str or int) – Location name or index.

  • columns (list, optional) – Column indices or names to include.

Returns:

DataFrame with monthly averaged values.

Return type:

pd.DataFrame

get_annual_totals(location=0, columns=None)[source]#

Calculate annual totals for budget data.

Parameters:
  • location (str or int) – Location name or index.

  • columns (list, optional) – Column indices or names to include.

Returns:

DataFrame with annual total values.

Return type:

pd.DataFrame

get_cumulative(location=0, columns=None)[source]#

Calculate cumulative sums for budget data.

Parameters:
  • location (str or int) – Location name or index.

  • columns (list, optional) – Column indices or names to include.

Returns:

DataFrame with cumulative values.

Return type:

pd.DataFrame

ZBudget Module#

Reader for IWFM zone budget output files.

Zone budget (ZBudget) file reader for IWFM simulation output.

This module provides classes for reading IWFM zone budget HDF5 files, which contain spatially aggregated water balance data by user-defined zones.

Example

Read a zone budget file:

>>> from pyiwfm.io.zbudget import ZBudgetReader
>>> reader = ZBudgetReader("GWZBud.hdf")
>>> print(reader.zones)
['Zone 1', 'Zone 2', 'Zone 3']
>>> df = reader.get_dataframe(zone="Zone 1")
class pyiwfm.io.zbudget.ZBudgetHeader(software_version='', descriptor='', vert_flows_at_node=False, face_flows_defined=False, storages_defined=False, compute_error=False, n_data=0, data_types=<factory>, data_names=<factory>, data_hdf_paths=<factory>, n_layers=0, n_elements=0, n_timesteps=0, start_datetime=None, delta_t_minutes=1440, time_unit='1DAY', elem_data_columns=<factory>)[source]#

Bases: object

Zone budget file header information.

software_version: str = ''#
descriptor: str = ''#
vert_flows_at_node: bool = False#
face_flows_defined: bool = False#
storages_defined: bool = False#
compute_error: bool = False#
n_data: int = 0#
data_types: list[int]#
data_names: list[str]#
data_hdf_paths: list[str]#
n_layers: int = 0#
n_elements: int = 0#
n_timesteps: int = 0#
start_datetime: datetime | None = None#
delta_t_minutes: int = 1440#
time_unit: str = '1DAY'#
elem_data_columns: dict[int, ndarray[tuple[Any, ...], dtype[int32]]]#
__init__(software_version='', descriptor='', vert_flows_at_node=False, face_flows_defined=False, storages_defined=False, compute_error=False, n_data=0, data_types=<factory>, data_names=<factory>, data_hdf_paths=<factory>, n_layers=0, n_elements=0, n_timesteps=0, start_datetime=None, delta_t_minutes=1440, time_unit='1DAY', elem_data_columns=<factory>)#
class pyiwfm.io.zbudget.ZoneInfo(id, name, n_elements=0, element_ids=<factory>, area=0.0, adjacent_zones=<factory>)[source]#

Bases: object

Information about a zone.

id: int#
name: str#
n_elements: int = 0#
element_ids: list[int]#
area: float = 0.0#
adjacent_zones: list[int]#
__init__(id, name, n_elements=0, element_ids=<factory>, area=0.0, adjacent_zones=<factory>)#
class pyiwfm.io.zbudget.ZBudgetReader(filepath)[source]#

Bases: object

Reader for IWFM zone budget HDF5 files.

Zone budget files contain spatially aggregated water balance data for user-defined zones (groups of elements).

Parameters:

filepath (str or Path) – Path to the zone budget HDF5 file.

Variables:
  • filepath (Path) – Path to the zone budget file.

  • header (ZBudgetHeader) – Parsed header information.

  • zones (list[str]) – List of zone names.

  • data_names (list[str]) – List of budget data column names.

Examples

>>> reader = ZBudgetReader("GWZBud.hdf")
>>> print(reader.descriptor)
'GROUNDWATER ZONE BUDGET'
>>> print(reader.zones)
['Zone 1', 'Zone 2', 'Zone 3']
>>>
>>> # Get data column names
>>> print(reader.data_names)
['Deep Percolation', 'Pumping', 'Subsurface Inflow', ...]
>>>
>>> # Read data for a zone
>>> df = reader.get_dataframe(zone="Zone 1", layer=1)
__init__(filepath)[source]#
property descriptor: str#

Return budget descriptor.

property zones: list[str]#

Return list of zone names.

property n_zones: int#

Return number of zones.

property data_names: list[str]#

Return list of budget data column names.

property n_layers: int#

Return number of model layers.

property n_timesteps: int#

Return number of time steps.

get_zone_info(zone)[source]#

Get information about a zone.

Parameters:

zone (str or int) – Zone name or index (1-based).

Returns:

Zone information including elements and area.

Return type:

ZoneInfo

get_element_data(data_name, layer=1)[source]#

Read raw element-level data for a budget component.

Parameters:
  • data_name (str or int) – Data column name or index (0-based).

  • layer (int) – Layer number (1-based).

Returns:

2D array of shape (n_timesteps, n_items). For datasets that cover all elements, n_items == n_elements. For sparse datasets (e.g. Streams), n_items < n_elements — use header.elem_data_columns to map element IDs to column indices.

Return type:

NDArray[np.float64]

get_zone_data(zone, data_name=None, layer=1)[source]#

Read aggregated zone budget data.

Parameters:
  • zone (str or int) – Zone name or index.

  • data_name (str or int, optional) – Data column name or index. If None, returns all data columns.

  • layer (int) – Layer number (1-based).

Returns:

Tuple of (times, values) where: - times: 1D array of time values - values: 1D or 2D array of zone budget values

Return type:

tuple[NDArray[np.float64], NDArray[np.float64]]

get_dataframe(zone, layer=1, data_columns=None, *, volume_factor=1.0)[source]#

Read zone budget data as a pandas DataFrame.

When volume_factor is left at its default of 1.0 the raw simulation values are returned unchanged. Pass the FACTVLOU value to get unit-converted output.

Parameters:
  • zone (str or int) – Zone name or index.

  • layer (int) – Layer number (1-based).

  • data_columns (list[str], optional) – Specific data columns to include. If None, includes all.

  • volume_factor (float) – Multiplier for all data columns (zone budget columns are volumetric). Default 1.0.

Returns:

DataFrame with datetime index and budget columns.

Return type:

pd.DataFrame

get_all_zones_dataframe(data_name, layer=1)[source]#

Get data for all zones as a DataFrame.

Parameters:
  • data_name (str) – Data column name.

  • layer (int) – Layer number (1-based).

Returns:

DataFrame with datetime index and zone columns.

Return type:

pd.DataFrame

get_monthly_averages(zone, layer=1)[source]#

Calculate monthly averages for zone budget data.

Parameters:
  • zone (str or int) – Zone name or index.

  • layer (int) – Layer number (1-based).

Returns:

DataFrame with monthly averaged values.

Return type:

pd.DataFrame

get_annual_totals(zone, layer=1)[source]#

Calculate annual totals for zone budget data.

Parameters:
  • zone (str or int) – Zone name or index.

  • layer (int) – Layer number (1-based).

Returns:

DataFrame with annual total values.

Return type:

pd.DataFrame

get_water_balance(zone, layer=1, inflow_columns=None, outflow_columns=None)[source]#

Calculate water balance for a zone.

Parameters:
  • zone (str or int) – Zone name or index.

  • layer (int) – Layer number (1-based).

  • inflow_columns (list[str], optional) – Columns to sum as inflows.

  • outflow_columns (list[str], optional) – Columns to sum as outflows (will be negated).

Returns:

DataFrame with inflows, outflows, and balance.

Return type:

pd.DataFrame

to_csv(output_dir, zones=None, layer=1)[source]#

Export zone budget data to CSV files.

Parameters:
  • output_dir (str or Path) – Output directory for CSV files.

  • zones (list[str], optional) – Zones to export. If None, exports all zones.

  • layer (int) – Layer number (1-based).

Returns:

List of created CSV file paths.

Return type:

list[Path]

Budget Control File Parser#

Parser for IWFM budget post-processor control files (.bud / .in).

Budget control file parser for IWFM post-processing.

Reads a .bud / .in budget control file that specifies which HDF5 budget output files to process, unit conversion factors, time windows, and which locations to export.

class pyiwfm.io.budget_control.BudgetOutputSpec(hdf_file, output_file, output_interval=None, location_ids=<factory>)[source]#

Bases: object

Specification for a single budget output in the control file.

hdf_file: Path#

HDF5 budget input file.

output_file: Path#

Output file path (.xlsx or .txt).

output_interval: str | None = None#

Print interval (e.g. "1MON"). None = same as data.

location_ids: list[int]#

1-based location IDs. [-1] = all, [] = none.

__init__(hdf_file, output_file, output_interval=None, location_ids=<factory>)#
class pyiwfm.io.budget_control.BudgetControlConfig(length_factor=1.0, length_unit='FEET', area_factor=1.0, area_unit='AC', volume_factor=1.0, volume_unit='AC.FT.', cache_size=100, begin_date=None, end_date=None, budgets=<factory>)[source]#

Bases: object

Parsed budget control file.

length_factor: float = 1.0#

FACTLTOU — length unit conversion factor.

length_unit: str = 'FEET'#

UNITLTOU — output length unit string.

area_factor: float = 1.0#

FACTAROU — area unit conversion factor.

__init__(length_factor=1.0, length_unit='FEET', area_factor=1.0, area_unit='AC', volume_factor=1.0, volume_unit='AC.FT.', cache_size=100, begin_date=None, end_date=None, budgets=<factory>)#
area_unit: str = 'AC'#

UNITAROU — output area unit string.

volume_factor: float = 1.0#

FACTVLOU — volume unit conversion factor.

volume_unit: str = 'AC.FT.'#

UNITVLOU — output volume unit string.

cache_size: int = 100#

NTIME — cache/block size for reading.

begin_date: str | None = None#

Begin date (MM/DD/YYYY_HH:MM) or None for start-of-data.

end_date: str | None = None#

End date (MM/DD/YYYY_HH:MM) or None for end-of-data.

budgets: list[BudgetOutputSpec]#

List of budget output specifications.

pyiwfm.io.budget_control.read_budget_control(filepath)[source]#

Parse an IWFM budget control file.

Parameters:

filepath (Path or str) – Path to the budget control / input file.

Returns:

Parsed configuration.

Return type:

BudgetControlConfig

ZBudget Control File Parser#

Parser for IWFM zone budget post-processor control files.

Zone budget (ZBudget) control file parser for IWFM post-processing.

Reads a zbudget control file that specifies which HDF5 zone-budget output files to process, zone definition files, unit conversion factors, and which zones to export.

class pyiwfm.io.zbudget_control.ZBudgetOutputSpec(zone_def_file=None, hdf_file=<factory>, output_file=<factory>, output_interval=None, zone_ids=<factory>)[source]#

Bases: object

Specification for a single zone-budget output in the control file.

zone_def_file: Path | None = None#

Zone definition file path (None if blank).

hdf_file: Path#

HDF5 zone-budget input file.

output_file: Path#

Output file path (.xlsx or .txt).

output_interval: str | None = None#

Print interval (e.g. "1MON"). None = same as data.

zone_ids: list[int]#

1-based zone IDs. [-1] = all, [] = none.

__init__(zone_def_file=None, hdf_file=<factory>, output_file=<factory>, output_interval=None, zone_ids=<factory>)#
class pyiwfm.io.zbudget_control.ZBudgetControlConfig(area_factor=1.0, area_unit='AC', volume_factor=1.0, volume_unit='AC.FT.', cache_size=100, begin_date=None, end_date=None, zbudgets=<factory>)[source]#

Bases: object

Parsed zone-budget control file.

area_factor: float = 1.0#

FACTAROU — area unit conversion factor.

area_unit: str = 'AC'#

UNITAROU — output area unit string.

__init__(area_factor=1.0, area_unit='AC', volume_factor=1.0, volume_unit='AC.FT.', cache_size=100, begin_date=None, end_date=None, zbudgets=<factory>)#
volume_factor: float = 1.0#

FACTVLOU — volume unit conversion factor.

volume_unit: str = 'AC.FT.'#

UNITVLOU — output volume unit string.

cache_size: int = 100#

NTIME — cache/block size for reading.

begin_date: str | None = None#

Begin date (MM/DD/YYYY_HH:MM) or None for start-of-data.

end_date: str | None = None#

End date (MM/DD/YYYY_HH:MM) or None for end-of-data.

zbudgets: list[ZBudgetOutputSpec]#

List of zone-budget output specifications.

pyiwfm.io.zbudget_control.read_zbudget_control(filepath)[source]#

Parse an IWFM zone-budget control file.

Parameters:

filepath (Path or str) – Path to the zbudget control / input file.

Returns:

Parsed configuration.

Return type:

ZBudgetControlConfig

Budget Utilities#

Shared helpers for unit conversion, title-line formatting, and time filtering used by both budget and zone budget Excel export.

Shared utilities for budget and zone-budget Excel export and unit conversion.

Provides unit conversion factor application, title-line marker substitution, and time-range filtering used by both budget_excel and zbudget_excel.

pyiwfm.io.budget_utils.apply_unit_conversion(values, column_types, length_factor=1.0, area_factor=1.0, volume_factor=1.0)[source]#

Apply IWFM unit conversion factors to budget data columns.

Parameters:
  • values (NDArray[np.float64]) – 2-D array of shape (n_timesteps, n_columns).

  • column_types (list[int]) – IWFM data-type code for each column (see Budget_Parameters.f90).

  • length_factor (float) – Multiplicative factor for length columns (type 5).

  • area_factor (float) – Multiplicative factor for area columns (type 4).

  • volume_factor (float) – Multiplicative factor for volume columns (types 1-3, 6-11).

Returns:

Converted array (new copy; input is not modified).

Return type:

NDArray[np.float64]

pyiwfm.io.budget_utils.format_title_lines(titles, location_name, area, length_unit, area_unit, volume_unit)[source]#

Substitute IWFM unit markers in title strings.

Recognised markers: @UNITVL@, @UNITAR@, @UNITLT@, @LOCNAME@, @AREA@.

Parameters:
  • titles (list[str]) – Raw title lines from BudgetHeader.ascii_output.titles.

  • location_name (str) – Name inserted for @LOCNAME@.

  • area (float or None) – Area value inserted for @AREA@. None becomes "N/A".

  • length_unit (str) – Unit strings inserted for the corresponding markers.

  • area_unit (str) – Unit strings inserted for the corresponding markers.

  • volume_unit (str) – Unit strings inserted for the corresponding markers.

Returns:

Title lines with all markers replaced.

Return type:

list[str]

pyiwfm.io.budget_utils.filter_time_range(df, begin_date, end_date)[source]#

Filter a DataFrame to the requested time window.

Parameters:
  • df (pd.DataFrame) – DataFrame whose index is a DatetimeIndex.

  • begin_date (str or None) – IWFM datetime strings (MM/DD/YYYY_HH:MM). None means no bound in that direction.

  • end_date (str or None) – IWFM datetime strings (MM/DD/YYYY_HH:MM). None means no bound in that direction.

Returns:

Filtered copy (or the original if neither bound applies).

Return type:

pd.DataFrame

Budget Excel Export#

Excel workbook generation from budget HDF5 data (one sheet per location).

Budget Excel export for IWFM post-processing.

Generates Excel workbooks from IWFM budget HDF5 data with one sheet per location, title lines, bold headers, and auto-fitted column widths — matching the reference implementation in SGMOModeling/pywfm-budget-excel.

pyiwfm.io.budget_excel.budget_to_excel(reader, output_path, length_factor=1.0, area_factor=1.0, volume_factor=1.0, length_unit='FEET', area_unit='AC', volume_unit='AC.FT.', location_ids=None, begin_date=None, end_date=None, output_interval=None)[source]#

Export budget data to an Excel workbook.

One sheet per location. Each sheet contains:

  • Title lines (bold) from ASCIIOutputInfo with unit substitution.

  • Column headers (bold).

  • Data rows with unit conversion applied.

  • Auto-fitted column widths.

Parameters:
  • reader (BudgetReader) – Open budget reader.

  • output_path (Path or str) – Destination .xlsx file.

  • length_factor (float) – FACTLTOU / FACTAROU / FACTVLOU from the control file.

  • area_factor (float) – FACTLTOU / FACTAROU / FACTVLOU from the control file.

  • volume_factor (float) – FACTLTOU / FACTAROU / FACTVLOU from the control file.

  • length_unit (str) – UNITLTOU / UNITAROU / UNITVLOU for title-line substitution.

  • area_unit (str) – UNITLTOU / UNITAROU / UNITVLOU for title-line substitution.

  • volume_unit (str) – UNITLTOU / UNITAROU / UNITVLOU for title-line substitution.

  • location_ids (list[int] or None) – 1-based location IDs to include. None or [-1] → all.

  • begin_date (str or None) – IWFM datetime strings for time filtering.

  • end_date (str or None) – IWFM datetime strings for time filtering.

  • output_interval (str or None) – Not yet implemented — reserved for future resampling.

Returns:

The written workbook path.

Return type:

Path

pyiwfm.io.budget_excel.budget_control_to_excel(config)[source]#

Process a full budget control file, generating one .xlsx per spec.

Parameters:

config (BudgetControlConfig) – Parsed budget control configuration.

Returns:

Paths of created workbook files.

Return type:

list[Path]

ZBudget Excel Export#

Excel workbook generation from zone budget HDF5 data (one sheet per zone).

Zone budget Excel export for IWFM post-processing.

Generates Excel workbooks from IWFM zone-budget HDF5 data with one sheet per zone, title lines, bold headers, and auto-fitted column widths.

pyiwfm.io.zbudget_excel.zbudget_to_excel(reader, output_path, area_factor=1.0, volume_factor=1.0, area_unit='AC', volume_unit='AC.FT.', zone_ids=None, zone_def_file=None, begin_date=None, end_date=None, output_interval=None, layer=1)[source]#

Export zone budget data to an Excel workbook.

One sheet per zone. Same formatting as budget export: title area (bold), column headers (bold), data rows, auto-fit widths.

Parameters:
  • reader (ZBudgetReader) – Open zone-budget reader.

  • output_path (Path or str) – Destination .xlsx file.

  • area_factor (float) – FACTAROU / FACTVLOU from the control file.

  • volume_factor (float) – FACTAROU / FACTVLOU from the control file.

  • area_unit (str) – UNITAROU / UNITVLOU for title substitution.

  • volume_unit (str) – UNITAROU / UNITVLOU for title substitution.

  • zone_ids (list[int] or None) – 1-based zone IDs to include. None or [-1] → all.

  • zone_def_file (Path or str or None) – External zone definition file (not yet used — reserved).

  • begin_date (str or None) – IWFM datetime strings for time filtering.

  • end_date (str or None) – IWFM datetime strings for time filtering.

  • output_interval (str or None) – Reserved for future resampling.

  • layer (int) – Model layer (1-based).

Returns:

The written workbook path.

Return type:

Path

pyiwfm.io.zbudget_excel.zbudget_control_to_excel(config)[source]#

Process a full zbudget control file, generating one .xlsx per spec.

Parameters:

config (ZBudgetControlConfig) – Parsed zone-budget control configuration.

Returns:

Paths of created workbook files.

Return type:

list[Path]

Model I/O#

Model Loader#

Complete model loading from simulation and preprocessor files.

Complete IWFM Model Loader.

This module provides a unified entry point for loading a complete IWFM model from simulation and preprocessor files. It handles the hierarchical file structure where the simulation main file references component main files, which in turn reference sub-files for specific data.

The loader supports two simulation file formats: - Description-based format (pyiwfm writer format with / KEYWORD suffixes) - Positional sequential format (native IWFM Fortran format)

Includes optional comment preservation for round-trip file operations.

class pyiwfm.io.model_loader.ModelLoadResult(model=None, simulation_config=None, errors=<factory>, warnings=<factory>)[source]#

Bases: object

Result of loading an IWFM model.

Variables:
  • model (IWFMModel | None) – The loaded IWFMModel instance (or None if loading failed)

  • simulation_config (SimulationConfig | None) – Parsed simulation configuration

  • errors (dict[str, str]) – Dictionary of component-level errors encountered

  • warnings (list[str]) – List of non-fatal warnings

model: IWFMModel | None = None#
simulation_config: SimulationConfig | None = None#
errors: dict[str, str]#
warnings: list[str]#
property success: bool#

Whether the model was loaded successfully.

property has_errors: bool#

Whether any component errors occurred.

__init__(model=None, simulation_config=None, errors=<factory>, warnings=<factory>)#
class pyiwfm.io.model_loader.CompleteModelLoader(simulation_file, preprocessor_file=None, use_positional_format=None)[source]#

Bases: object

Load a complete IWFM model from simulation and preprocessor files.

This class provides a high-level API for loading IWFM models that automatically handles: - Detection of simulation file format (description-based vs positional) - Hierarchical file resolution (main files -> sub-files) - Component loading with graceful error handling - Metadata collection from all loaded components

Example:

loader = CompleteModelLoader(
    simulation_file="Simulation/C2VSimFG.in",
    preprocessor_file="Preprocessor/C2VSimFG_PreProcessor.in",
)
result = loader.load()
if result.success:
    model = result.model
    print(f"Loaded {model.n_nodes} nodes")
__init__(simulation_file, preprocessor_file=None, use_positional_format=None)[source]#

Initialize the model loader.

Parameters:
  • simulation_file (Path | str) – Path to the simulation main input file

  • preprocessor_file (Path | str | None) – Path to the preprocessor main input file. If None, the loader will try to find it from the simulation config or use binary preprocessor output.

  • use_positional_format (bool | None) – Force positional format reading (True), description-based reading (False), or auto-detect (None).

load()[source]#

Load the complete IWFM model.

Returns:

ModelLoadResult with the loaded model and any errors

Return type:

ModelLoadResult

load_model()[source]#

Load the complete model, raising on failure.

Returns:

IWFMModel instance

Raises:

RuntimeError – If model loading fails

Return type:

IWFMModel

pyiwfm.io.model_loader.load_complete_model(simulation_file, preprocessor_file=None, use_positional_format=None)[source]#

Load a complete IWFM model from simulation and preprocessor files.

This is a convenience function that creates a CompleteModelLoader and loads the model, raising on failure.

Parameters:
  • simulation_file (Path | str) – Path to the simulation main input file

  • preprocessor_file (Path | str | None) – Path to the preprocessor main input file

  • use_positional_format (bool | None) – Force format detection

Returns:

IWFMModel instance

Raises:

RuntimeError – If model loading fails

Return type:

IWFMModel

class pyiwfm.io.model_loader.ModelLoadResultWithComments(model=None, simulation_config=None, errors=<factory>, warnings=<factory>, comment_metadata=<factory>)[source]#

Bases: ModelLoadResult

Result of loading an IWFM model with comment preservation.

Extends ModelLoadResult to include extracted comment metadata for all loaded files.

Variables:

comment_metadata (dict[str, CommentMetadata]) – Dictionary mapping file type to CommentMetadata. Keys include “preprocessor_main”, “simulation_main”, “gw_main”, etc.

comment_metadata: dict[str, CommentMetadata]#
get_file_comments(file_type)[source]#

Get comment metadata for a specific file type.

Parameters:

file_type (str) – File type key (e.g., “preprocessor_main”).

Returns:

CommentMetadata if available, None otherwise.

Return type:

CommentMetadata | None

__init__(model=None, simulation_config=None, errors=<factory>, warnings=<factory>, comment_metadata=<factory>)#
class pyiwfm.io.model_loader.CommentAwareModelLoader(simulation_file, preprocessor_file=None, use_positional_format=None, preserve_comments=True)[source]#

Bases: CompleteModelLoader

Load an IWFM model with comment preservation.

Extends CompleteModelLoader to extract and preserve comments from all loaded input files. The preserved comments can be used for round-trip file operations.

Example:

loader = CommentAwareModelLoader(
    simulation_file="Simulation/Main.in",
    preprocessor_file="Preprocessor/Main.in",
)
result = loader.load()
if result.success:
    model = result.model
    comments = result.comment_metadata
    # Later: write model with preserved comments
    write_model_with_comments(model, "output/", comments)
__init__(simulation_file, preprocessor_file=None, use_positional_format=None, preserve_comments=True)[source]#

Initialize the comment-aware model loader.

Parameters:
  • simulation_file (Path | str) – Path to the simulation main input file

  • preprocessor_file (Path | str | None) – Path to the preprocessor main input file

  • use_positional_format (bool | None) – Force positional format reading

  • preserve_comments (bool) – Whether to extract and preserve comments

load()[source]#

Load the complete IWFM model with comment metadata.

Returns:

ModelLoadResultWithComments with model, errors, and comments

Return type:

ModelLoadResultWithComments

pyiwfm.io.model_loader.load_model_with_comments(simulation_file, preprocessor_file=None, use_positional_format=None)[source]#

Load an IWFM model with comment preservation.

This is a convenience function that loads a model and extracts comments from all input files for round-trip preservation.

Parameters:
  • simulation_file (Path | str) – Path to the simulation main input file

  • preprocessor_file (Path | str | None) – Path to the preprocessor main input file

  • use_positional_format (bool | None) – Force positional format reading

Returns:

Tuple of (IWFMModel, comment_metadata_dict)

Raises:

RuntimeError – If model loading fails

Return type:

tuple[IWFMModel, dict[str, CommentMetadata]]

Example:

model, comments = load_model_with_comments("Simulation/Main.in")
# Modify model...
model.nodes.add_node(...)
# Write back with preserved comments
write_model_with_comments(model, "output/", comment_metadata=comments)

Model Writer#

Complete model writer that orchestrates all component writers.

Complete Model Writer for IWFM models.

This module provides the main writer for exporting a complete IWFM model to disk with per-file path control and time series format conversion.

Supports optional comment preservation for round-trip operations.

Classes:

ModelWriteResult: Result of a complete model write operation. TimeSeriesCopier: Copies/converts time series files between locations. CompleteModelWriter: Orchestrates all component writers.

Functions:

write_model: Convenience function for writing a complete model. write_model_with_comments: Write model with preserved comments. save_model_with_comments: High-level API for comment-preserving writes.

class pyiwfm.io.model_writer.ModelWriteResult(files=<factory>, errors=<factory>, warnings=<factory>)[source]#

Bases: object

Result of a complete model write operation.

Variables:
  • files (dict[str, pathlib.Path]) – Mapping of file_key to written output path.

  • errors (dict[str, str]) – Mapping of component name to error message.

  • warnings (list[str]) – List of non-fatal warning messages.

files: dict[str, Path]#
errors: dict[str, str]#
warnings: list[str]#
property success: bool#

True if no errors occurred during writing.

__init__(files=<factory>, errors=<factory>, warnings=<factory>)#
class pyiwfm.io.model_writer.TimeSeriesCopier(model, config)[source]#

Bases: object

Copies or converts time series files from source to destination.

Handles format conversion between text and DSS formats when the target format differs from the source format.

__init__(model, config)[source]#
copy_all()[source]#

Copy all time series files from source to destination.

Returns:

Tuple of (files dict, warnings list).

Return type:

tuple[dict[str, Path], list[str]]

class pyiwfm.io.model_writer.CompleteModelWriter(model, config, comment_metadata=None, preserve_comments=True)[source]#

Bases: object

Orchestrates all component writers to produce a complete IWFM model.

Uses ModelWriteConfig for all path resolution, enabling arbitrary directory structures. Each component writer receives paths derived from the config, and cross-file references use get_relative_path().

Supports optional comment preservation via comment_metadata parameter. When provided, preserved comments from the original files will be injected into the output files.

Example:

config = ModelWriteConfig(output_dir=Path("C:/models/output"))
writer = CompleteModelWriter(model, config)
result = writer.write_all()
if result.success:
    print(f"Wrote {len(result.files)} files")

Example with comment preservation:

model, comments = load_model_with_comments("Simulation/Main.in")
config = ModelWriteConfig(output_dir=Path("output"))
writer = CompleteModelWriter(model, config, comment_metadata=comments)
result = writer.write_all()
__init__(model, config, comment_metadata=None, preserve_comments=True)[source]#

Initialize the complete model writer.

Parameters:
  • model (IWFMModel) – IWFMModel instance to write.

  • config (ModelWriteConfig) – Configuration for output paths and formats.

  • comment_metadata (dict[str, CommentMetadata] | None) – Dictionary mapping file type to CommentMetadata. Keys should match file type names (e.g., “preprocessor_main”, “gw_main”, “stream_main”).

  • preserve_comments (bool) – If True and comment_metadata is provided, inject preserved comments into output files.

get_file_comments(file_type)[source]#

Get comment metadata for a specific file type.

Parameters:

file_type (str) – File type key (e.g., “preprocessor_main”).

Returns:

CommentMetadata if available and preservation is enabled.

Return type:

CommentMetadata | None

write_all()[source]#

Write the complete model to disk.

Returns:

ModelWriteResult with files written, errors, and warnings.

Return type:

ModelWriteResult

write_preprocessor()[source]#

Write preprocessor files (nodes, elements, stratigraphy).

Returns:

Mapping of file type to output path.

Return type:

dict[str, Path]

pyiwfm.io.model_writer.write_model(model, output_dir, file_paths=None, ts_format='text', **kwargs)[source]#

Write a complete IWFM model to disk.

This is the main convenience function for writing a model. It creates a ModelWriteConfig and delegates to CompleteModelWriter.

Parameters:
  • model (IWFMModel) – IWFMModel instance to write.

  • output_dir (Path | str) – Base output directory.

  • file_paths (dict[str, str] | None) – Optional dict of {file_key: relative_path} overrides. If None, uses default nested layout.

  • ts_format (str) – Time series format - "text" or "dss".

  • **kwargs (Any) – Additional arguments passed to ModelWriteConfig.

Returns:

ModelWriteResult with written files, errors, and warnings.

Return type:

ModelWriteResult

Example:

result = write_model(model, "C:/models/output")
if result.success:
    print(f"Model written to {len(result.files)} files")
pyiwfm.io.model_writer.write_model_with_comments(model, output_dir, comment_metadata=None, file_paths=None, ts_format='text', save_sidecars=True, **kwargs)[source]#

Write a complete IWFM model with preserved comments.

This is like write_model() but with support for comment preservation. When comment_metadata is provided, preserved comments from the original files are injected into the output files.

Parameters:
  • model (IWFMModel) – IWFMModel instance to write.

  • output_dir (Path | str) – Base output directory.

  • comment_metadata (dict[str, CommentMetadata] | None) – Dictionary mapping file type to CommentMetadata. Typically obtained from load_model_with_comments().

  • file_paths (dict[str, str] | None) – Optional dict of {file_key: relative_path} overrides.

  • ts_format (str) – Time series format - "text" or "dss".

  • save_sidecars (bool) – If True, save comment metadata as sidecar files alongside the output files for future round-trips.

  • **kwargs (Any) – Additional arguments passed to ModelWriteConfig.

Returns:

ModelWriteResult with written files, errors, and warnings.

Return type:

ModelWriteResult

Example:

# Load model with comments
model, comments = load_model_with_comments("Simulation/Main.in")

# Modify the model
model.nodes.add_node(...)

# Write back with preserved comments
result = write_model_with_comments(
    model,
    "output/",
    comment_metadata=comments,
)
pyiwfm.io.model_writer.save_model_with_comments(model, output_dir, comment_metadata=None)[source]#

High-level API for writing a model with preserved comments.

This is the simplest API for round-trip model operations with comment preservation.

Parameters:
  • model (IWFMModel) – IWFMModel instance to write.

  • output_dir (Path | str) – Base output directory.

  • comment_metadata (dict[str, CommentMetadata] | None) – Dictionary mapping file type to CommentMetadata. If None, writes without comment preservation (uses templates).

Returns:

Dictionary mapping file type to written path.

Raises:

RuntimeError – If writing fails with errors.

Return type:

dict[str, Path]

Example:

# Load model with comments
model, comments = load_model_with_comments("Simulation/Main.in")

# Modify the model
model.nodes.add_node(...)

# Write back with preserved comments
files = save_model_with_comments(model, "output/", comments)
print(f"Wrote files: {list(files.keys())}")

# Or write NEW model without preserved comments (uses templates)
new_files = save_model_with_comments(new_model, "output/")

Model Packager#

Package an IWFM model directory into a distributable ZIP archive with an embedded manifest.json.

Package IWFM model directories into distributable ZIP archives.

class pyiwfm.io.model_packager.ModelPackageResult(archive_path, files_included=<factory>, total_size_bytes=0, manifest=<factory>)[source]#

Bases: object

Result of a model packaging operation.

Variables:
  • archive_path (Path) – Path to the created ZIP archive.

  • files_included (list[Path]) – Absolute paths of all files that were added to the archive.

  • total_size_bytes (int) – Total uncompressed size of included files in bytes.

  • manifest (dict[str, str]) – Mapping of relative path (inside ZIP) to the file-type category.

archive_path: Path#
files_included: list[Path]#
total_size_bytes: int = 0#
manifest: dict[str, str]#
__init__(archive_path, files_included=<factory>, total_size_bytes=0, manifest=<factory>)#
pyiwfm.io.model_packager.collect_model_files(model_dir, *, include_executables=False, include_results=False)[source]#

Collect all model files from a directory tree.

Walks model_dir recursively and returns files relevant to an IWFM model, excluding output/cache directories by default.

Parameters:
  • model_dir (Path) – Root directory of the IWFM model.

  • include_executables (bool) – If True, include .exe, .dll, and .so files.

  • include_results (bool) – If True, include the Results/ directory and HDF5 output files.

Returns:

Sorted list of absolute paths to included files.

Return type:

list[Path]

pyiwfm.io.model_packager.package_model(model_dir, output_path=None, *, include_executables=False, include_results=False, compression=8, compresslevel=6)[source]#

Package an IWFM model directory into a ZIP archive.

Creates a ZIP file preserving the directory structure (Preprocessor/, Simulation/, etc.) with a manifest.json embedded in the archive.

Parameters:
  • model_dir (Path) – Root directory of the IWFM model.

  • output_path (Path | None) – Path for the output ZIP file. If None, defaults to <model_dir_name>.zip in the parent of model_dir.

  • include_executables (bool) – If True, include .exe, .dll, and .so files.

  • include_results (bool) – If True, include the Results/ directory and HDF5 output files.

  • compression (int) – ZIP compression method (default ZIP_DEFLATED).

  • compresslevel (int) – Compression level (0-9, default 6).

Returns:

Result with archive path, file list, size, and manifest.

Return type:

ModelPackageResult

Raises:

FileNotFoundError – If model_dir does not exist or is not a directory.

Infrastructure#

IWFM Reader Utilities#

Central module for IWFM file line-reading, comment handling, version parsing, and path resolution. All io/ reader modules import helpers from here rather than defining their own copies.

Unified IWFM file line-reading utilities.

Matches the I/O approach in IWFM Fortran source code (GeneralUtilities.f90, Class_AsciiFileType.f90).

Every io/ reader should import helpers from this module rather than defining its own copy.

pyiwfm.io.iwfm_reader.is_comment_line(line)[source]#

Check if line is an IWFM full-line comment or blank.

Matches IWFM’s SkipComment() which skips lines starting with C, c, or * in column 1 (the first character of the raw line). Lines with leading whitespace are data lines even if the first non-space character is C.

A Windows drive-letter prefix (C:\) is not treated as a comment.

pyiwfm.io.iwfm_reader.strip_inline_comment(line)[source]#

Strip inline comment from an IWFM data line.

Returns (value, description).

Matches IWFM’s FindInlineCommentPosition() + StripTextUntilCharacter() (GeneralUtilities.f90:716-782). Only / preceded by whitespace is treated as a comment delimiter. # is not a comment character in IWFM.

pyiwfm.io.iwfm_reader.next_data_value(f, line_counter=None)[source]#

Read next non-comment line and strip inline comment.

Use for scalar or string data (file paths, parameters) where inline / description should be removed.

Matches IWFM pattern: SkipComment() + READ + StripTextUntilCharacter().

pyiwfm.io.iwfm_reader.next_data_line(f, line_counter=None)[source]#

Read next non-comment line without stripping inline comments.

Use for numeric data rows (arrays, tables) where Fortran’s free-format READ handles inline comments by reading exactly N values and ignoring the rest of the line.

The caller should split() and take the first N tokens.

pyiwfm.io.iwfm_reader.next_data_or_empty(f, line_counter=None)[source]#

Read next non-comment data value, or "" at EOF.

Like next_data_value() but returns "" instead of raising on EOF, and returns "" for blank lines. Useful for optional file paths that may be represented by blank lines.

pyiwfm.io.iwfm_reader.parse_int(value, context='', line_number=None)[source]#

Parse a string as an integer with descriptive error on failure.

Parameters:
  • value (str) – The string to parse.

  • context (str) – Description of what was being parsed (for error messages).

  • line_number (int, optional) – Line number in the source file (for error messages).

pyiwfm.io.iwfm_reader.parse_float(value, context='', line_number=None)[source]#

Parse a string as a float with descriptive error on failure.

Parameters:
  • value (str) – The string to parse.

  • context (str) – Description of what was being parsed (for error messages).

  • line_number (int, optional) – Line number in the source file (for error messages).

pyiwfm.io.iwfm_reader.resolve_path(base_dir: Path, filepath: str) Path[source]#
pyiwfm.io.iwfm_reader.resolve_path(base_dir: Path, filepath: str, *, allow_empty: Literal[False]) Path
pyiwfm.io.iwfm_reader.resolve_path(base_dir: Path, filepath: str, *, allow_empty: Literal[True]) Path | None
pyiwfm.io.iwfm_reader.resolve_path(base_dir: Path, filepath: str, *, allow_empty: bool) Path | None

Resolve a file path relative to base_dir.

IWFM paths can be absolute or relative (relative to the main input file’s directory).

Parameters:
  • base_dir (Path) – Directory to resolve relative paths against.

  • filepath (str) – Raw file path string from an IWFM input file.

  • allow_empty (bool) – If True, return None for blank/empty filepath instead of creating a Path(""). Useful for optional file paths in root-zone and other sub-files.

pyiwfm.io.iwfm_reader.parse_version(version)[source]#

Parse a version string like '4.12' or '4-12' into (4, 12).

Handles both . and - as separators so the same function works for root zone versions (4.12) and stream versions (4-21).

pyiwfm.io.iwfm_reader.version_ge(version, target)[source]#

Return True if version >= target.

class pyiwfm.io.iwfm_reader.ReaderMixin[source]#

Bases: object

Mixin providing standard IWFM line-reading helpers for reader classes.

Any class that mixes this in must initialise self._line_num = 0 before calling these methods (typically in __init__ or at the start of read()).

class pyiwfm.io.iwfm_reader.LineBuffer(lines)[source]#

Bases: object

Read-ahead buffer for positional-sequential IWFM files.

Supports pushing a line back so that a tabular reader can stop at a boundary and leave the next line available for the caller.

__init__(lines)[source]#
property line_num: int#
next_line()[source]#

Return the next raw line, or None at EOF.

pushback()[source]#

Push the last-read line back (decrement position).

next_data()[source]#

Return next non-comment data value (inline comment stripped).

Raises FileFormatError on EOF.

next_data_or_empty()[source]#

Return next data value, or "" at EOF.

Skips comment lines (C/c/*) but stops at (and returns) the first non-comment line even if it is blank.

next_raw_data()[source]#

Return next non-comment raw line (no inline comment stripping).

Use for numeric data rows where the caller splits and takes the first N tokens.

Comment Extractor#

Extracts comments from IWFM files for preservation during roundtrip I/O.

Comment extraction from IWFM input files.

This module provides tools for extracting comments from IWFM input files, preserving them for later restoration during file writing.

IWFM supports several comment styles: - Full-line comments starting with ‘C’, ‘c’, or ‘*’ in column 1 - Inline comments starting with ‘/’ preceded by whitespace - Header blocks using banner patterns (C***…*)

Classes:

LineType: Enum for classifying input lines. CommentExtractor: Main class for extracting comments from files.

class pyiwfm.io.comment_extractor.LineType(*values)[source]#

Bases: Enum

Classification of IWFM input file lines.

BLANK = 1#
FULL_LINE_COMMENT = 2#
INLINE_COMMENT = 3#
DATA = 4#
SECTION_HEADER = 5#
BANNER = 6#
class pyiwfm.io.comment_extractor.ParsedLine(line_number, raw, stripped, line_type, data_part='', comment_part='', keyword='')[source]#

Bases: object

A parsed line from an IWFM input file.

Variables:
  • line_number (int) – 1-based line number in the file.

  • raw (str) – Original line text including newline.

  • stripped (str) – Line with trailing whitespace removed.

  • line_type (pyiwfm.io.comment_extractor.LineType) – Classification of the line.

  • data_part (str) – Data portion of the line (for DATA and INLINE_COMMENT).

  • comment_part (str) – Comment portion of the line (for INLINE_COMMENT).

  • keyword (str) – Keyword from / KEYWORD suffix (e.g., “NNODES”).

line_number: int#
raw: str#
stripped: str#
line_type: LineType#
data_part: str = ''#
comment_part: str = ''#
keyword: str = ''#
__init__(line_number, raw, stripped, line_type, data_part='', comment_part='', keyword='')#
class pyiwfm.io.comment_extractor.CommentExtractor(preserve_mode=PreserveMode.FULL)[source]#

Bases: object

Extract comments from IWFM input files.

This class parses IWFM input files and extracts all comment information, organizing it into a CommentMetadata structure that can be used to preserve comments during round-trip file operations.

IWFM comment conventions (from IWFM source code): - Full-line comment indicators: ‘C’, ‘c’, ‘*’ in column 1 - Inline comment indicator: ‘/’ preceded by whitespace - Banner patterns: Lines of C***** or C—– used as section dividers

Example

>>> extractor = CommentExtractor()
>>> metadata = extractor.extract(Path("Preprocessor.in"))
>>> metadata.save_for_file(Path("Preprocessor.in"))
Variables:
  • FULL_LINE_CHARS – Characters that indicate a full-line comment.

  • INLINE_CHAR – Character that indicates an inline comment.

  • BANNER_PATTERN – Regex pattern for banner lines.

  • KEYWORD_PATTERN – Regex pattern for / KEYWORD suffixes.

FULL_LINE_CHARS = frozenset({'*', 'C', 'c'})#
INLINE_CHAR = '/'#
BANNER_PATTERN = re.compile('^[Cc]\\s*[\\*\\-=]{10,}')#
KEYWORD_PATTERN = re.compile('\\s*/\\s*(\\w+)(?:\\s|$)')#
NUMBERED_KEYWORD_PATTERN = re.compile('/\\s*\\d+:\\s*(.+?)(?:\\s*\\(|$)')#
SECTION_PATTERNS = {'ELEMENTS': re.compile('NELEM|element.*config', re.IGNORECASE), 'GROUNDWATER': re.compile('groundwater|aquifer', re.IGNORECASE), 'LAKES': re.compile('NLAKES|lake.*config', re.IGNORECASE), 'NODES': re.compile('NNODES|node.*coord', re.IGNORECASE), 'ROOTZONE': re.compile('root.*zone|land.*use', re.IGNORECASE), 'SIMULATION': re.compile('simulation|time.*step', re.IGNORECASE), 'STRATIGRAPHY': re.compile('NLAYERS|stratigraph', re.IGNORECASE), 'STREAMS': re.compile('NREACH|stream.*config', re.IGNORECASE)}#
__init__(preserve_mode=PreserveMode.FULL)[source]#

Initialize the comment extractor.

Parameters:

preserve_mode (PreserveMode) – Level of comment preservation.

extract(filepath)[source]#

Extract comments from an IWFM input file.

Parameters:

filepath (Path | str) – Path to the IWFM input file.

Returns:

CommentMetadata containing all extracted comments.

Raises:
Return type:

CommentMetadata

pyiwfm.io.comment_extractor.extract_comments(filepath)[source]#

Convenience function to extract comments from an IWFM file.

Parameters:

filepath (Path | str) – Path to the IWFM input file.

Returns:

CommentMetadata containing all extracted comments.

Return type:

CommentMetadata

Example

>>> metadata = extract_comments("Preprocessor.in")
>>> print(len(metadata.header_block))
15
pyiwfm.io.comment_extractor.extract_and_save_comments(filepath)[source]#

Extract comments and save to a sidecar file.

Parameters:

filepath (Path | str) – Path to the IWFM input file.

Returns:

Path to the saved sidecar file.

Return type:

Path

Example

>>> sidecar = extract_and_save_comments("Preprocessor.in")
>>> print(sidecar)
Preprocessor.in.iwfm_comments.json

Comment Metadata#

Stores extracted comment metadata for use during file writing.

Comment metadata storage for IWFM input files.

This module provides data classes for storing and serializing comments extracted from IWFM input files, enabling round-trip preservation of user-defined comments when reading and writing models.

The comment metadata is stored in JSON sidecar files (.iwfm_comments.json) alongside the original input files.

Classes:

PreserveMode: Enum for comment preservation levels. SectionComments: Comments associated with a specific file section. CommentMetadata: Complete comment metadata for an IWFM file.

class pyiwfm.io.comment_metadata.PreserveMode(*values)[source]#

Bases: Enum

Comment preservation level.

Variables:
  • NONE – Do not preserve any comments.

  • HEADERS – Preserve only file header blocks.

  • FULL – Preserve all comments including inline comments.

NONE = 'none'#
HEADERS = 'headers'#
FULL = 'full'#
class pyiwfm.io.comment_metadata.SectionComments(section_name, header_comments=<factory>, inline_comments=<factory>, data_comments=<factory>, trailing_comments=<factory>)[source]#

Bases: object

Comments associated with a specific section of an IWFM file.

A section is a logical grouping within an IWFM file, such as “NODES”, “ELEMENTS”, “STRATIGRAPHY”, etc. Each section can have: - Header comments that appear before the section data - Inline comments on data lines - Data comments keyed by element ID - Trailing comments after the section data

Variables:
  • section_name (str) – Name/identifier of the section.

  • header_comments (list[str]) – Comment lines appearing before section data.

  • inline_comments (dict[str, str]) – Map of line key to inline comment text. Keys are typically field names or column headers.

  • data_comments (dict[str, str]) – Map of data identifier to comment text. Keys use format “type:id” (e.g., “node:157”, “elem:42”).

  • trailing_comments (list[str]) – Comment lines appearing after section data.

section_name: str#
header_comments: list[str]#
inline_comments: dict[str, str]#
data_comments: dict[str, str]#
trailing_comments: list[str]#
to_dict()[source]#

Convert to dictionary for JSON serialization.

classmethod from_dict(data)[source]#

Create from dictionary (JSON deserialization).

has_comments()[source]#

Check if this section has any preserved comments.

get_data_comment(data_type, data_id)[source]#

Get comment for a specific data item.

Parameters:
  • data_type (str) – Type of data (e.g., “node”, “elem”, “reach”).

  • data_id (int | str) – ID of the data item.

Returns:

Comment text if found, None otherwise.

Return type:

str | None

set_data_comment(data_type, data_id, comment)[source]#

Set comment for a specific data item.

Parameters:
  • data_type (str) – Type of data (e.g., “node”, “elem”, “reach”).

  • data_id (int | str) – ID of the data item.

  • comment (str) – Comment text.

__init__(section_name, header_comments=<factory>, inline_comments=<factory>, data_comments=<factory>, trailing_comments=<factory>)#
class pyiwfm.io.comment_metadata.CommentMetadata(version='1.0', source_file='', iwfm_version='', preserve_mode=PreserveMode.FULL, header_block=<factory>, sections=<factory>, file_metadata=<factory>)[source]#

Bases: object

Complete comment metadata for an IWFM input file.

This class stores all extracted comments from an IWFM input file, organized by section. It supports JSON serialization for storage in sidecar files.

Variables:
version: str = '1.0'#
source_file: str = ''#
iwfm_version: str = ''#
preserve_mode: PreserveMode = 'full'#
header_block: list[str]#
sections: dict[str, SectionComments]#
file_metadata: dict[str, Any]#
SIDECAR_SUFFIX = '.iwfm_comments.json'#
to_dict()[source]#

Convert to dictionary for JSON serialization.

classmethod from_dict(data)[source]#

Create from dictionary (JSON deserialization).

save(path)[source]#

Save comment metadata to a JSON file.

Parameters:

path (Path | str) – Path to the JSON file to create.

Raises:

IOError – If the file cannot be written.

classmethod load(path)[source]#

Load comment metadata from a JSON file.

Parameters:

path (Path | str) – Path to the JSON file to read.

Returns:

CommentMetadata instance if file exists and is valid, None otherwise.

Return type:

CommentMetadata | None

classmethod sidecar_path(source_path)[source]#

Get the sidecar file path for a source file.

The sidecar file is stored alongside the source file with the suffix ‘.iwfm_comments.json’.

Parameters:

source_path (Path | str) – Path to the IWFM input file.

Returns:

Path to the corresponding sidecar file.

Return type:

Path

classmethod load_for_file(source_path)[source]#

Load comment metadata for an IWFM input file.

Looks for a sidecar file alongside the source file.

Parameters:

source_path (Path | str) – Path to the IWFM input file.

Returns:

CommentMetadata if sidecar exists and is valid, None otherwise.

Return type:

CommentMetadata | None

save_for_file(source_path)[source]#

Save comment metadata as a sidecar file.

Parameters:

source_path (Path | str) – Path to the IWFM input file.

Returns:

Path to the saved sidecar file.

Return type:

Path

get_section(section_name)[source]#

Get comments for a specific section.

Parameters:

section_name (str) – Name of the section.

Returns:

SectionComments if found, None otherwise.

Return type:

SectionComments | None

get_or_create_section(section_name)[source]#

Get or create comments for a specific section.

Parameters:

section_name (str) – Name of the section.

Returns:

SectionComments instance (existing or newly created).

Return type:

SectionComments

has_comments()[source]#

Check if any comments are stored.

merge(other)[source]#

Merge comments from another metadata instance.

Used when combining comments from multiple source files during model loading.

Parameters:

other (CommentMetadata) – Another CommentMetadata instance to merge from.

__init__(version='1.0', source_file='', iwfm_version='', preserve_mode=PreserveMode.FULL, header_block=<factory>, sections=<factory>, file_metadata=<factory>)#
class pyiwfm.io.comment_metadata.FileCommentMetadata(files=<factory>)[source]#

Bases: object

Container for comment metadata across multiple IWFM files.

When loading a complete IWFM model, this class holds the comment metadata for all component files, keyed by file type.

Variables:

files (dict[str, pyiwfm.io.comment_metadata.CommentMetadata]) – Dictionary mapping file type to CommentMetadata. Keys are standardized names like “preprocessor_main”, “gw_main”, “stream_main”, etc.

files: dict[str, CommentMetadata]#
get(file_type)[source]#

Get comment metadata for a file type.

set(file_type, metadata)[source]#

Set comment metadata for a file type.

save_all(output_dir, file_paths)[source]#

Save all comment metadata as sidecar files.

Parameters:
  • output_dir (Path) – Base output directory.

  • file_paths (dict[str, Path]) – Mapping of file type to output path.

classmethod load_all(file_paths)[source]#

Load comment metadata for all files.

Parameters:

file_paths (dict[str, Path]) – Mapping of file type to source path.

Returns:

FileCommentMetadata with loaded metadata for each file.

Return type:

FileCommentMetadata

has_comments()[source]#

Check if any file has preserved comments.

__init__(files=<factory>)#

Comment Writer#

Injects preserved comments back into written IWFM files.

Comment restoration for IWFM input files.

This module provides tools for restoring preserved comments when writing IWFM input files, enabling round-trip preservation of user-defined comments.

Classes:

CommentWriter: Main class for restoring comments during file writing.

class pyiwfm.io.comment_writer.CommentWriter(metadata=None, use_fallback=True)[source]#

Bases: object

Restore comments when writing IWFM input files.

This class provides methods for injecting preserved comments into generated IWFM output files. It works with CommentMetadata extracted by CommentExtractor.

The writer supports: - Restoring file header blocks - Adding section header comments - Appending inline comments to data lines - Inserting trailing comments after sections

Example

>>> from pyiwfm.io.comment_metadata import CommentMetadata
>>> metadata = CommentMetadata.load_for_file("Preprocessor.in")
>>> writer = CommentWriter(metadata)
>>> header = writer.restore_header()
>>> print(header)
Variables:
  • metadata – The CommentMetadata to use for restoration.

  • use_fallback – If True, use default formatting when no comments exist.

DEFAULT_BANNER = 'C******************************************************************************'#
DEFAULT_DIVIDER = 'C------------------------------------------------------------------------------'#
__init__(metadata=None, use_fallback=True)[source]#

Initialize the comment writer.

Parameters:
  • metadata (CommentMetadata | None) – CommentMetadata to use for restoration. If None, no comments will be restored.

  • use_fallback (bool) – If True, use default formatting when no preserved comments exist for a section.

restore_header(fallback_lines=None)[source]#

Restore the file header block.

Parameters:

fallback_lines (list[str] | None) – Lines to use if no header is preserved.

Returns:

Header text with newlines, or empty string if no header.

Return type:

str

restore_section_header(section_name, fallback_lines=None)[source]#

Restore header comments for a section.

Parameters:
  • section_name (str) – Name of the section.

  • fallback_lines (list[str] | None) – Lines to use if no comments are preserved.

Returns:

Section header text with newlines.

Return type:

str

restore_section_trailing(section_name, fallback_lines=None)[source]#

Restore trailing comments for a section.

Parameters:
  • section_name (str) – Name of the section.

  • fallback_lines (list[str] | None) – Lines to use if no comments are preserved.

Returns:

Trailing comment text with newlines.

Return type:

str

format_data_with_comment(data, section_name, key, fallback_comment=None)[source]#

Format a data line with its preserved inline comment.

Parameters:
  • data (str) – Data portion of the line.

  • section_name (str) – Name of the section containing this data.

  • key (str) – Key for looking up the comment (keyword or “line:N”).

  • fallback_comment (str | None) – Comment to use if none preserved.

Returns:

Formatted line with inline comment if available.

Return type:

str

format_value_with_keyword(value, keyword, section_name=None, width=20)[source]#

Format a value line with / KEYWORD suffix.

Tries to restore the original comment format if preserved, otherwise uses the standard / KEYWORD format.

Parameters:
  • value (str) – Value to format.

  • keyword (str) – IWFM keyword (e.g., “NNODES”, “NLAYERS”).

  • section_name (str | None) – Section to look up comments from.

  • width (int) – Width for value field.

Returns:

Formatted line with keyword suffix.

Return type:

str

get_data_comment(section_name, data_type, data_id)[source]#

Get preserved comment for a data item.

Parameters:
  • section_name (str) – Section containing the data.

  • data_type (str) – Type of data (e.g., “node”, “elem”).

  • data_id (int | str) – ID of the data item.

Returns:

Comment text if preserved, None otherwise.

Return type:

str | None

write_header_to_file(file, fallback_lines=None)[source]#

Write header block to a file.

Parameters:
  • file (TextIO) – File object to write to.

  • fallback_lines (list[str] | None) – Lines to use if no header preserved.

write_section_header_to_file(file, section_name, fallback_lines=None)[source]#

Write section header comments to a file.

Parameters:
  • file (TextIO) – File object to write to.

  • section_name (str) – Name of the section.

  • fallback_lines (list[str] | None) – Lines to use if no comments preserved.

write_section_trailing_to_file(file, section_name, fallback_lines=None)[source]#

Write section trailing comments to a file.

Parameters:
  • file (TextIO) – File object to write to.

  • section_name (str) – Name of the section.

  • fallback_lines (list[str] | None) – Lines to use if no comments preserved.

write_data_line(file, data, section_name, key, fallback_comment=None)[source]#

Write a data line with optional inline comment.

Parameters:
  • file (TextIO) – File object to write to.

  • data (str) – Data portion of the line.

  • section_name (str) – Section containing this data.

  • key (str) – Key for comment lookup.

  • fallback_comment (str | None) – Comment to use if none preserved.

has_preserved_comments()[source]#

Check if any comments are available for restoration.

has_section_comments(section_name)[source]#

Check if a section has preserved comments.

class pyiwfm.io.comment_writer.CommentInjector(metadata=None)[source]#

Bases: object

Inject comments into template-rendered content.

This class post-processes template output to inject preserved comments, allowing templates to generate structure while preserving user comments.

Example

>>> injector = CommentInjector(metadata)
>>> output = injector.inject_header(rendered_content)
__init__(metadata=None)[source]#

Initialize the comment injector.

Parameters:

metadata (CommentMetadata | None) – CommentMetadata containing preserved comments.

inject_header(content, header_marker='C*****')[source]#

Inject preserved header into content.

Replaces the template-generated header (identified by marker) with the preserved header if available.

Parameters:
  • content (str) – Template-rendered content.

  • header_marker (str) – Marker identifying the header start.

Returns:

Content with injected header.

Return type:

str

inject_section_comments(content, section_name, section_marker)[source]#

Inject section comments into content.

Finds the section by marker and injects preserved comments.

Parameters:
  • content (str) – Template-rendered content.

  • section_name (str) – Name of the section.

  • section_marker (str) – Text that identifies section start.

Returns:

Content with injected section comments.

Return type:

str

process_content(content, sections=None)[source]#

Process content with all comment injections.

Parameters:
  • content (str) – Template-rendered content.

  • sections (dict[str, str] | None) – Dict mapping section names to their markers.

Returns:

Fully processed content with all comments injected.

Return type:

str

Parametric Grid#

Parametric grid interpolation utilities.

Parametric grid interpolation for IWFM aquifer parameters.

When NGROUP > 0 in the GW main file, aquifer parameters are defined on a coarser parametric finite-element mesh and interpolated onto the model nodes. This module implements the FE interpolation algorithm used in IWFM’s ParametricGrid.f90.

The parametric grid uses standard linear shape functions:
  • Triangles: barycentric (area) coordinates

  • Quadrilaterals: bilinear isoparametric mapping

class pyiwfm.io.parametric_grid.ParamNode(node_id, x, y, values)[source]#

Bases: object

A parametric grid node with coordinates and parameter values.

Variables:
  • node_id (int) – Node identifier (1-based from file).

  • x (float) – X coordinate.

  • y (float) – Y coordinate.

  • values (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Parameter values, shape (n_layers, n_params).

node_id: int#
x: float#
y: float#
values: ndarray[tuple[Any, ...], dtype[float64]]#
__init__(node_id, x, y, values)#
class pyiwfm.io.parametric_grid.ParamElement(elem_id, vertices)[source]#

Bases: object

A parametric grid element (triangle or quad).

Variables:
  • elem_id (int) – Element identifier (1-based from file).

  • vertices (tuple[int, ...]) – Indices into the ParametricGrid.nodes list (0-based).

elem_id: int#
vertices: tuple[int, ...]#
__init__(elem_id, vertices)#
class pyiwfm.io.parametric_grid.ParametricGrid(nodes, elements)[source]#

Bases: object

Lightweight FE grid for parametric interpolation.

Mirrors IWFM’s ParametricGrid module. Supports point-in-element testing, shape function evaluation, and parameter interpolation at arbitrary (x, y) points.

Parameters:
  • nodes (list[ParamNode]) – Parametric grid nodes with coordinates and parameter values.

  • elements (list[ParamElement]) – Parametric grid elements referencing node indices.

__init__(nodes, elements)[source]#
interpolate(x, y)[source]#

Interpolate parameter values at point (x, y).

Searches all elements to find the one containing the point, then computes shape-function-weighted parameter values.

Returns:

Array of shape (n_layers, n_params) with interpolated values, or None if the point is outside the grid.

Return type:

NDArray[np.float64] or None

HEC-DSS Module#

The DSS module provides support for reading and writing HEC-DSS 7 files.

Note

The HEC-DSS 7 C library is bundled with pyiwfm on Windows (io/dss/lib/hecdss.dll). On Linux, compile from source using dss-build/ or set the HECDSS_LIB environment variable to point to your libhecdss.so.

DSS Package#

HEC-DSS support for pyiwfm.

This package provides Python bindings for reading and writing HEC-DSS 7 files, which are commonly used for time series data storage in water resources modeling.

The HEC-DSS library must be installed separately. Set the HECDSS_LIB environment variable to point to the library location.

Example

>>> from pyiwfm.io.dss import DSSFile, DSSPathname
>>>
>>> # Build a pathname
>>> pathname = DSSPathname.build(
...     project="IWFM_MODEL",
...     location="STREAM_NODE_1",
...     parameter="flow",
...     interval="daily",
... )
>>>
>>> # Write time series
>>> with DSSFile("output.dss", mode="w") as dss:
...     dss.write_regular_timeseries(str(pathname), values, start_date)
class pyiwfm.io.dss.DSSPathname(a_part='', b_part='', c_part='', d_part='', e_part='', f_part='')[source]#

Bases: object

HEC-DSS pathname with structured parts.

DSS pathnames identify data records using six parts: /A/B/C/D/E/F/

Variables:
  • a_part (str) – Project/Basin name

  • b_part (str) – Location identifier

  • c_part (str) – Parameter type (FLOW, HEAD, etc.)

  • d_part (str) – Date/time window

  • e_part (str) – Time interval (1DAY, 1HOUR, etc.)

  • f_part (str) – Version/scenario name

a_part: str = ''#
b_part: str = ''#
c_part: str = ''#
d_part: str = ''#
e_part: str = ''#
f_part: str = ''#
__post_init__()[source]#

Validate and normalize pathname parts.

__str__()[source]#

Return the full pathname string.

classmethod from_string(pathname)[source]#

Parse a pathname from string.

Parameters:

pathname (str) – DSS pathname string (e.g., “/PROJECT/LOC/FLOW//1DAY/V1/”)

Returns:

DSSPathname object

Raises:

ValueError – If pathname format is invalid

Return type:

DSSPathname

classmethod build(project='', location='', parameter='', date_range='', interval='1DAY', version='')[source]#

Build a pathname with more descriptive parameter names.

Parameters:
  • project (str) – Project or basin name (A part)

  • location (str) – Location identifier (B part)

  • parameter (str) – Parameter name (C part) - will be converted to DSS code

  • date_range (str) – Date range (D part)

  • interval (str) – Time interval (E part) - will be converted to DSS code

  • version (str) – Version or scenario (F part)

Returns:

DSSPathname object

Return type:

DSSPathname

with_location(location)[source]#

Return a new pathname with a different location (B part).

with_parameter(parameter)[source]#

Return a new pathname with a different parameter (C part).

with_date_range(date_range)[source]#

Return a new pathname with a different date range (D part).

with_version(version)[source]#

Return a new pathname with a different version (F part).

matches(pattern)[source]#

Check if this pathname matches a pattern.

Pattern parts can be empty to match any value.

Parameters:

pattern (DSSPathname | str) – DSSPathname pattern or string

Returns:

True if pathname matches pattern

Return type:

bool

property is_regular_interval: bool#

Check if this is a regular-interval time series.

property is_irregular_interval: bool#

Check if this is an irregular-interval time series.

__init__(a_part='', b_part='', c_part='', d_part='', e_part='', f_part='')#
class pyiwfm.io.dss.DSSPathnameTemplate(a_part='', b_part='', c_part='', d_part='', e_part='1DAY', f_part='')[source]#

Bases: object

Template for generating multiple DSS pathnames.

Allows creating pathnames with variable parts.

Example

>>> template = DSSPathnameTemplate(
...     a_part="PROJECT",
...     c_part="FLOW",
...     e_part="1DAY",
...     f_part="OBS",
... )
>>> pathname = template.make_pathname(location="STREAM_01")
a_part: str = ''#
b_part: str = ''#
c_part: str = ''#
d_part: str = ''#
e_part: str = '1DAY'#
f_part: str = ''#
make_pathname(location=None, date_range=None, **kwargs)[source]#

Create a pathname from the template.

Parameters:
  • location (str | None) – Location to use (overrides b_part)

  • date_range (str | None) – Date range to use (overrides d_part)

  • **kwargs (str) – Additional parts to override (a_part, c_part, etc.)

Returns:

DSSPathname object

Return type:

DSSPathname

make_pathnames(locations, date_range='')[source]#

Generate pathnames for multiple locations.

Parameters:
  • locations (list[str]) – List of location identifiers

  • date_range (str) – Date range to use

Yields:

DSSPathname objects

__init__(a_part='', b_part='', c_part='', d_part='', e_part='1DAY', f_part='')#
pyiwfm.io.dss.format_dss_date(dt)[source]#

Format a datetime for DSS D-part.

Parameters:

dt (datetime) – datetime object

Returns:

Date string in DSS format (e.g., “01JAN2000”)

Return type:

str

pyiwfm.io.dss.format_dss_date_range(start, end)[source]#

Format a date range for DSS D-part.

Parameters:
Returns:

Date range string (e.g., “01JAN2000-31DEC2000”)

Return type:

str

pyiwfm.io.dss.parse_dss_date(date_str)[source]#

Parse a DSS date string.

Parameters:

date_str (str) – Date string in DSS format (e.g., “01JAN2000”)

Returns:

datetime object

Return type:

datetime

pyiwfm.io.dss.interval_to_minutes(interval)[source]#

Convert DSS interval to minutes.

Parameters:

interval (str) – DSS interval string (e.g., “1DAY”, “1HOUR”)

Returns:

Interval in minutes

Return type:

int

pyiwfm.io.dss.minutes_to_interval(minutes)[source]#

Convert minutes to the nearest DSS interval.

Parameters:

minutes (int) – Interval in minutes

Returns:

DSS interval string

Return type:

str

class pyiwfm.io.dss.DSSFile(filepath, mode='r')[source]#

Bases: object

Context manager for HEC-DSS file operations.

Provides methods for reading and writing time series data to DSS files.

Example

>>> with DSSFile("data.dss", mode="rw") as dss:
...     times, values = dss.read_regular_timeseries("/A/B/C/D/E/F/")
...     dss.write_regular_timeseries("/A/B/C2/D/E/F/", values * 2, times[0])
__init__(filepath, mode='r')[source]#

Initialize DSS file handle.

Parameters:
  • filepath (Path | str) – Path to DSS file

  • mode (str) – File mode (‘r’ = read, ‘w’ = write, ‘rw’ = read/write)

__enter__()[source]#

Open the DSS file.

__exit__(exc_type, exc_val, exc_tb)[source]#

Close the DSS file.

open()[source]#

Open the DSS file.

close()[source]#

Close the DSS file.

write_regular_timeseries(pathname, values, start_date, units='', data_type='INST-VAL')[source]#

Write a regular-interval time series to the DSS file.

Uses the HEC-DSS 7 constructor pattern: zstructTsNewRegFloats() -> ztsStore() -> zstructFree()

Parameters:
  • pathname (str) – DSS pathname

  • values (ndarray[tuple[Any, ...], dtype[float64]] | Sequence[float]) – Array of values

  • start_date (datetime) – Start datetime

  • units (str) – Units string

  • data_type (str) – Data type (e.g., “INST-VAL”, “PER-AVER”)

read_regular_timeseries(pathname, start_date=None, end_date=None)[source]#

Read a regular-interval time series from the DSS file.

Uses the HEC-DSS 7 constructor pattern: zstructTsNewTimes() -> ztsRetrieve() -> extract fields -> zstructFree()

Parameters:
  • pathname (str) – DSS pathname

  • start_date (datetime | None) – Start datetime (optional)

  • end_date (datetime | None) – End datetime (optional)

Returns:

Tuple of (times, values)

Return type:

tuple[list[datetime], ndarray[tuple[Any, …], dtype[float64]]]

get_timeseries_info(pathname)[source]#

Get information about a time series record.

Retrieves the record and extracts metadata (number of values, units, data type, date range).

Parameters:

pathname (str) – DSS pathname

Returns:

DSSTimeSeriesInfo object

Return type:

DSSTimeSeriesInfo

catalog(pattern='')[source]#

Get a list of pathnames in the file.

Parameters:

pattern (str) – Optional pattern to filter results

Returns:

List of pathname strings

Return type:

list[str]

class pyiwfm.io.dss.DSSFileMock(filepath, mode='r')[source]#

Bases: object

Mock DSS file for when the library is not available.

Allows code to run without the DSS library, but operations will raise informative errors.

__init__(filepath, mode='r')[source]#

Initialize mock DSS file.

open()[source]#
close()[source]#
write_regular_timeseries(*args, **kwargs)[source]#
read_regular_timeseries(*args, **kwargs)[source]#
pyiwfm.io.dss.DSSFileClass#

alias of DSSFileMock

exception pyiwfm.io.dss.DSSFileError[source]#

Bases: Exception

Error with DSS file operations.

exception pyiwfm.io.dss.DSSLibraryError[source]#

Bases: Exception

Error loading or using the HEC-DSS library.

class pyiwfm.io.dss.DSSTimeSeriesInfo(pathname, start_date=None, end_date=None, n_values=0, units='', data_type='')[source]#

Bases: object

Information about a DSS time series record.

Variables:
  • pathname (str) – Full DSS pathname

  • start_date (datetime.datetime | None) – Start datetime

  • end_date (datetime.datetime | None) – End datetime

  • n_values (int) – Number of values

  • units (str) – Units string

  • data_type (str) – Data type string

pathname: str#
start_date: datetime | None = None#
end_date: datetime | None = None#
n_values: int = 0#
units: str = ''#
data_type: str = ''#
__init__(pathname, start_date=None, end_date=None, n_values=0, units='', data_type='')#
pyiwfm.io.dss.check_dss_available()[source]#

Check if the DSS library is available.

Raises:

DSSLibraryError – If the library is not available

class pyiwfm.io.dss.DSSTimeSeriesWriter(filepath)[source]#

Bases: object

High-level writer for time series data to HEC-DSS files.

Example

>>> writer = DSSTimeSeriesWriter(Path("output.dss"))
>>> template = DSSPathnameTemplate(
...     a_part="PROJECT",
...     c_part="FLOW",
...     e_part="1DAY",
... )
>>> writer.write_timeseries(ts, template.make_pathname(location="STREAM_01"))
>>> writer.close()
__init__(filepath)[source]#

Initialize the DSS time series writer.

Parameters:

filepath (Path | str) – Path to DSS file (will be created if doesn’t exist)

__enter__()[source]#

Open the DSS file.

__exit__(exc_type, exc_val, exc_tb)[source]#

Close the DSS file.

open()[source]#

Open the DSS file for writing.

close()[source]#

Close the DSS file and return write result.

Returns:

DSSWriteResult with summary of written data

Return type:

DSSWriteResult

write_timeseries(ts, pathname, units=None, data_type='INST-VAL')[source]#

Write a TimeSeries to the DSS file.

Parameters:
  • ts (TimeSeries) – TimeSeries object to write

  • pathname (DSSPathname | str) – DSS pathname for the record

  • units (str | None) – Units string (defaults to ts.units)

  • data_type (str) – Data type (e.g., “INST-VAL”, “PER-AVER”)

Returns:

True if successful, False otherwise

Return type:

bool

write_collection(collection, pathname_factory, units=None, data_type='INST-VAL')[source]#

Write a TimeSeriesCollection to the DSS file.

Parameters:
Returns:

Number of time series successfully written

Return type:

int

write_multiple_timeseries(times, values_dict, template, units='', data_type='INST-VAL')[source]#

Write multiple time series with a common time axis.

Parameters:
Returns:

Number of time series successfully written

Return type:

int

class pyiwfm.io.dss.DSSTimeSeriesReader(filepath)[source]#

Bases: object

High-level reader for time series data from HEC-DSS files.

Example

>>> reader = DSSTimeSeriesReader(Path("input.dss"))
>>> ts = reader.read_timeseries("/A/B/C/D/E/F/")
>>> reader.close()
__init__(filepath)[source]#

Initialize the DSS time series reader.

Parameters:

filepath (Path | str) – Path to DSS file

__enter__()[source]#

Open the DSS file.

__exit__(exc_type, exc_val, exc_tb)[source]#

Close the DSS file.

open()[source]#

Open the DSS file for reading.

close()[source]#

Close the DSS file.

read_timeseries(pathname, start_date=None, end_date=None, name='')[source]#

Read a time series from the DSS file.

Parameters:
  • pathname (DSSPathname | str) – DSS pathname

  • start_date (datetime | None) – Optional start datetime

  • end_date (datetime | None) – Optional end datetime

  • name (str) – Name for the TimeSeries

Returns:

TimeSeries object

Return type:

TimeSeries

read_collection(pathnames, variable='')[source]#

Read multiple time series into a collection.

Parameters:
  • pathnames (list[DSSPathname | str]) – List of DSS pathnames to read

  • variable (str) – Variable name for the collection

Returns:

TimeSeriesCollection object

Return type:

TimeSeriesCollection

class pyiwfm.io.dss.DSSWriteResult(filepath, pathnames_written, n_records, errors)[source]#

Bases: object

Result of a DSS write operation.

Variables:
  • filepath (pathlib.Path) – Path to DSS file

  • pathnames_written (list[str]) – List of pathnames written

  • n_records (int) – Number of records written

  • errors (list[str]) – List of error messages (if any)

filepath: Path#
pathnames_written: list[str]#
n_records: int#
errors: list[str]#
property success: bool#

Return True if write was successful (no errors).

__init__(filepath, pathnames_written, n_records, errors)#
pyiwfm.io.dss.write_timeseries_to_dss(filepath, ts, pathname, units=None)[source]#

Write a single TimeSeries to a DSS file.

Parameters:
  • filepath (Path | str) – Path to DSS file

  • ts (TimeSeries) – TimeSeries to write

  • pathname (DSSPathname | str) – DSS pathname

  • units (str | None) – Optional units string

Returns:

DSSWriteResult

Return type:

DSSWriteResult

pyiwfm.io.dss.read_timeseries_from_dss(filepath, pathname, start_date=None, end_date=None)[source]#

Read a single TimeSeries from a DSS file.

Parameters:
  • filepath (Path | str) – Path to DSS file

  • pathname (DSSPathname | str) – DSS pathname

  • start_date (datetime | None) – Optional start datetime

  • end_date (datetime | None) – Optional end datetime

Returns:

TimeSeries object

Return type:

TimeSeries

pyiwfm.io.dss.write_collection_to_dss(filepath, collection, template, units='')[source]#

Write a TimeSeriesCollection to a DSS file.

Parameters:
Returns:

DSSWriteResult

Return type:

DSSWriteResult

DSS Pathname Utilities#

HEC-DSS pathname utilities.

This module provides classes and functions for working with HEC-DSS pathnames which are used to identify data records in DSS files.

DSS Pathname Format: /A/B/C/D/E/F/ - A: Project or Basin name - B: Location (e.g., stream gage, well ID) - C: Parameter (e.g., FLOW, STAGE, HEAD) - D: Date range (e.g., 01JAN2000) - E: Time interval (e.g., 1DAY, 1HOUR) - F: Version or scenario

class pyiwfm.io.dss.pathname.DSSPathname(a_part='', b_part='', c_part='', d_part='', e_part='', f_part='')[source]#

Bases: object

HEC-DSS pathname with structured parts.

DSS pathnames identify data records using six parts: /A/B/C/D/E/F/

Variables:
  • a_part (str) – Project/Basin name

  • b_part (str) – Location identifier

  • c_part (str) – Parameter type (FLOW, HEAD, etc.)

  • d_part (str) – Date/time window

  • e_part (str) – Time interval (1DAY, 1HOUR, etc.)

  • f_part (str) – Version/scenario name

a_part: str = ''#
b_part: str = ''#
c_part: str = ''#
d_part: str = ''#
e_part: str = ''#
f_part: str = ''#
__post_init__()[source]#

Validate and normalize pathname parts.

__str__()[source]#

Return the full pathname string.

classmethod from_string(pathname)[source]#

Parse a pathname from string.

Parameters:

pathname (str) – DSS pathname string (e.g., “/PROJECT/LOC/FLOW//1DAY/V1/”)

Returns:

DSSPathname object

Raises:

ValueError – If pathname format is invalid

Return type:

DSSPathname

classmethod build(project='', location='', parameter='', date_range='', interval='1DAY', version='')[source]#

Build a pathname with more descriptive parameter names.

Parameters:
  • project (str) – Project or basin name (A part)

  • location (str) – Location identifier (B part)

  • parameter (str) – Parameter name (C part) - will be converted to DSS code

  • date_range (str) – Date range (D part)

  • interval (str) – Time interval (E part) - will be converted to DSS code

  • version (str) – Version or scenario (F part)

Returns:

DSSPathname object

Return type:

DSSPathname

with_location(location)[source]#

Return a new pathname with a different location (B part).

with_parameter(parameter)[source]#

Return a new pathname with a different parameter (C part).

with_date_range(date_range)[source]#

Return a new pathname with a different date range (D part).

with_version(version)[source]#

Return a new pathname with a different version (F part).

matches(pattern)[source]#

Check if this pathname matches a pattern.

Pattern parts can be empty to match any value.

Parameters:

pattern (DSSPathname | str) – DSSPathname pattern or string

Returns:

True if pathname matches pattern

Return type:

bool

property is_regular_interval: bool#

Check if this is a regular-interval time series.

property is_irregular_interval: bool#

Check if this is an irregular-interval time series.

__init__(a_part='', b_part='', c_part='', d_part='', e_part='', f_part='')#
class pyiwfm.io.dss.pathname.DSSPathnameTemplate(a_part='', b_part='', c_part='', d_part='', e_part='1DAY', f_part='')[source]#

Bases: object

Template for generating multiple DSS pathnames.

Allows creating pathnames with variable parts.

Example

>>> template = DSSPathnameTemplate(
...     a_part="PROJECT",
...     c_part="FLOW",
...     e_part="1DAY",
...     f_part="OBS",
... )
>>> pathname = template.make_pathname(location="STREAM_01")
a_part: str = ''#
b_part: str = ''#
c_part: str = ''#
d_part: str = ''#
e_part: str = '1DAY'#
f_part: str = ''#
make_pathname(location=None, date_range=None, **kwargs)[source]#

Create a pathname from the template.

Parameters:
  • location (str | None) – Location to use (overrides b_part)

  • date_range (str | None) – Date range to use (overrides d_part)

  • **kwargs (str) – Additional parts to override (a_part, c_part, etc.)

Returns:

DSSPathname object

Return type:

DSSPathname

make_pathnames(locations, date_range='')[source]#

Generate pathnames for multiple locations.

Parameters:
  • locations (list[str]) – List of location identifiers

  • date_range (str) – Date range to use

Yields:

DSSPathname objects

__init__(a_part='', b_part='', c_part='', d_part='', e_part='1DAY', f_part='')#
pyiwfm.io.dss.pathname.format_dss_date(dt)[source]#

Format a datetime for DSS D-part.

Parameters:

dt (datetime) – datetime object

Returns:

Date string in DSS format (e.g., “01JAN2000”)

Return type:

str

pyiwfm.io.dss.pathname.format_dss_date_range(start, end)[source]#

Format a date range for DSS D-part.

Parameters:
Returns:

Date range string (e.g., “01JAN2000-31DEC2000”)

Return type:

str

pyiwfm.io.dss.pathname.parse_dss_date(date_str)[source]#

Parse a DSS date string.

Parameters:

date_str (str) – Date string in DSS format (e.g., “01JAN2000”)

Returns:

datetime object

Return type:

datetime

pyiwfm.io.dss.pathname.interval_to_minutes(interval)[source]#

Convert DSS interval to minutes.

Parameters:

interval (str) – DSS interval string (e.g., “1DAY”, “1HOUR”)

Returns:

Interval in minutes

Return type:

int

pyiwfm.io.dss.pathname.minutes_to_interval(minutes)[source]#

Convert minutes to the nearest DSS interval.

Parameters:

minutes (int) – Interval in minutes

Returns:

DSS interval string

Return type:

str

DSS Wrapper#

HEC-DSS 7 C library wrapper using ctypes.

This module provides a Python interface to the HEC-DSS 7 C library for reading and writing DSS files. The HEC-DSS library must be installed separately and the path specified via the HECDSS_LIB environment variable.

Note: HEC-DSS 7 uses 64-bit integers for IFLTAB (250 elements), not the legacy 32-bit version (600 elements).

Example

>>> from pyiwfm.io.dss import DSSFile
>>> with DSSFile("output.dss", mode="w") as dss:
...     dss.write_regular_timeseries(pathname, values, start_date)
exception pyiwfm.io.dss.wrapper.DSSLibraryError[source]#

Bases: Exception

Error loading or using the HEC-DSS library.

exception pyiwfm.io.dss.wrapper.DSSFileError[source]#

Bases: Exception

Error with DSS file operations.

pyiwfm.io.dss.wrapper.check_dss_available()[source]#

Check if the DSS library is available.

Raises:

DSSLibraryError – If the library is not available

class pyiwfm.io.dss.wrapper.DSSTimeSeriesInfo(pathname, start_date=None, end_date=None, n_values=0, units='', data_type='')[source]#

Bases: object

Information about a DSS time series record.

Variables:
  • pathname (str) – Full DSS pathname

  • start_date (datetime.datetime | None) – Start datetime

  • end_date (datetime.datetime | None) – End datetime

  • n_values (int) – Number of values

  • units (str) – Units string

  • data_type (str) – Data type string

pathname: str#
start_date: datetime | None = None#
end_date: datetime | None = None#
n_values: int = 0#
units: str = ''#
data_type: str = ''#
__init__(pathname, start_date=None, end_date=None, n_values=0, units='', data_type='')#
class pyiwfm.io.dss.wrapper.DSSFile(filepath, mode='r')[source]#

Bases: object

Context manager for HEC-DSS file operations.

Provides methods for reading and writing time series data to DSS files.

Example

>>> with DSSFile("data.dss", mode="rw") as dss:
...     times, values = dss.read_regular_timeseries("/A/B/C/D/E/F/")
...     dss.write_regular_timeseries("/A/B/C2/D/E/F/", values * 2, times[0])
__init__(filepath, mode='r')[source]#

Initialize DSS file handle.

Parameters:
  • filepath (Path | str) – Path to DSS file

  • mode (str) – File mode (‘r’ = read, ‘w’ = write, ‘rw’ = read/write)

__enter__()[source]#

Open the DSS file.

__exit__(exc_type, exc_val, exc_tb)[source]#

Close the DSS file.

open()[source]#

Open the DSS file.

close()[source]#

Close the DSS file.

write_regular_timeseries(pathname, values, start_date, units='', data_type='INST-VAL')[source]#

Write a regular-interval time series to the DSS file.

Uses the HEC-DSS 7 constructor pattern: zstructTsNewRegFloats() -> ztsStore() -> zstructFree()

Parameters:
  • pathname (str) – DSS pathname

  • values (ndarray[tuple[Any, ...], dtype[float64]] | Sequence[float]) – Array of values

  • start_date (datetime) – Start datetime

  • units (str) – Units string

  • data_type (str) – Data type (e.g., “INST-VAL”, “PER-AVER”)

read_regular_timeseries(pathname, start_date=None, end_date=None)[source]#

Read a regular-interval time series from the DSS file.

Uses the HEC-DSS 7 constructor pattern: zstructTsNewTimes() -> ztsRetrieve() -> extract fields -> zstructFree()

Parameters:
  • pathname (str) – DSS pathname

  • start_date (datetime | None) – Start datetime (optional)

  • end_date (datetime | None) – End datetime (optional)

Returns:

Tuple of (times, values)

Return type:

tuple[list[datetime], ndarray[tuple[Any, …], dtype[float64]]]

get_timeseries_info(pathname)[source]#

Get information about a time series record.

Retrieves the record and extracts metadata (number of values, units, data type, date range).

Parameters:

pathname (str) – DSS pathname

Returns:

DSSTimeSeriesInfo object

Return type:

DSSTimeSeriesInfo

catalog(pattern='')[source]#

Get a list of pathnames in the file.

Parameters:

pattern (str) – Optional pattern to filter results

Returns:

List of pathname strings

Return type:

list[str]

class pyiwfm.io.dss.wrapper.DSSFileMock(filepath, mode='r')[source]#

Bases: object

Mock DSS file for when the library is not available.

Allows code to run without the DSS library, but operations will raise informative errors.

__init__(filepath, mode='r')[source]#

Initialize mock DSS file.

open()[source]#
close()[source]#
write_regular_timeseries(*args, **kwargs)[source]#
read_regular_timeseries(*args, **kwargs)[source]#
pyiwfm.io.dss.wrapper.get_dss_file_class()[source]#

Get the appropriate DSS file class.

Returns DSSFile if library is available, DSSFileMock otherwise.

pyiwfm.io.dss.wrapper.DSSFileClass#

alias of DSSFileMock

DSS Time Series#

HEC-DSS time series read/write utilities.

This module provides high-level functions for reading and writing time series data to HEC-DSS files, with integration to pyiwfm’s TimeSeries class.

class pyiwfm.io.dss.timeseries.DSSWriteResult(filepath, pathnames_written, n_records, errors)[source]#

Bases: object

Result of a DSS write operation.

Variables:
  • filepath (pathlib.Path) – Path to DSS file

  • pathnames_written (list[str]) – List of pathnames written

  • n_records (int) – Number of records written

  • errors (list[str]) – List of error messages (if any)

filepath: Path#
pathnames_written: list[str]#
n_records: int#
errors: list[str]#
property success: bool#

Return True if write was successful (no errors).

__init__(filepath, pathnames_written, n_records, errors)#
class pyiwfm.io.dss.timeseries.DSSTimeSeriesWriter(filepath)[source]#

Bases: object

High-level writer for time series data to HEC-DSS files.

Example

>>> writer = DSSTimeSeriesWriter(Path("output.dss"))
>>> template = DSSPathnameTemplate(
...     a_part="PROJECT",
...     c_part="FLOW",
...     e_part="1DAY",
... )
>>> writer.write_timeseries(ts, template.make_pathname(location="STREAM_01"))
>>> writer.close()
__init__(filepath)[source]#

Initialize the DSS time series writer.

Parameters:

filepath (Path | str) – Path to DSS file (will be created if doesn’t exist)

__enter__()[source]#

Open the DSS file.

__exit__(exc_type, exc_val, exc_tb)[source]#

Close the DSS file.

open()[source]#

Open the DSS file for writing.

close()[source]#

Close the DSS file and return write result.

Returns:

DSSWriteResult with summary of written data

Return type:

DSSWriteResult

write_timeseries(ts, pathname, units=None, data_type='INST-VAL')[source]#

Write a TimeSeries to the DSS file.

Parameters:
  • ts (TimeSeries) – TimeSeries object to write

  • pathname (DSSPathname | str) – DSS pathname for the record

  • units (str | None) – Units string (defaults to ts.units)

  • data_type (str) – Data type (e.g., “INST-VAL”, “PER-AVER”)

Returns:

True if successful, False otherwise

Return type:

bool

write_collection(collection, pathname_factory, units=None, data_type='INST-VAL')[source]#

Write a TimeSeriesCollection to the DSS file.

Parameters:
Returns:

Number of time series successfully written

Return type:

int

write_multiple_timeseries(times, values_dict, template, units='', data_type='INST-VAL')[source]#

Write multiple time series with a common time axis.

Parameters:
Returns:

Number of time series successfully written

Return type:

int

class pyiwfm.io.dss.timeseries.DSSTimeSeriesReader(filepath)[source]#

Bases: object

High-level reader for time series data from HEC-DSS files.

Example

>>> reader = DSSTimeSeriesReader(Path("input.dss"))
>>> ts = reader.read_timeseries("/A/B/C/D/E/F/")
>>> reader.close()
__init__(filepath)[source]#

Initialize the DSS time series reader.

Parameters:

filepath (Path | str) – Path to DSS file

__enter__()[source]#

Open the DSS file.

__exit__(exc_type, exc_val, exc_tb)[source]#

Close the DSS file.

open()[source]#

Open the DSS file for reading.

close()[source]#

Close the DSS file.

read_timeseries(pathname, start_date=None, end_date=None, name='')[source]#

Read a time series from the DSS file.

Parameters:
  • pathname (DSSPathname | str) – DSS pathname

  • start_date (datetime | None) – Optional start datetime

  • end_date (datetime | None) – Optional end datetime

  • name (str) – Name for the TimeSeries

Returns:

TimeSeries object

Return type:

TimeSeries

read_collection(pathnames, variable='')[source]#

Read multiple time series into a collection.

Parameters:
  • pathnames (list[DSSPathname | str]) – List of DSS pathnames to read

  • variable (str) – Variable name for the collection

Returns:

TimeSeriesCollection object

Return type:

TimeSeriesCollection

pyiwfm.io.dss.timeseries.write_timeseries_to_dss(filepath, ts, pathname, units=None)[source]#

Write a single TimeSeries to a DSS file.

Parameters:
  • filepath (Path | str) – Path to DSS file

  • ts (TimeSeries) – TimeSeries to write

  • pathname (DSSPathname | str) – DSS pathname

  • units (str | None) – Optional units string

Returns:

DSSWriteResult

Return type:

DSSWriteResult

pyiwfm.io.dss.timeseries.read_timeseries_from_dss(filepath, pathname, start_date=None, end_date=None)[source]#

Read a single TimeSeries from a DSS file.

Parameters:
  • filepath (Path | str) – Path to DSS file

  • pathname (DSSPathname | str) – DSS pathname

  • start_date (datetime | None) – Optional start datetime

  • end_date (datetime | None) – Optional end datetime

Returns:

TimeSeries object

Return type:

TimeSeries

pyiwfm.io.dss.timeseries.write_collection_to_dss(filepath, collection, template, units='')[source]#

Write a TimeSeriesCollection to a DSS file.

Parameters:
Returns:

DSSWriteResult

Return type:

DSSWriteResult

Data Loaders#

These modules provide lazy, cached access to IWFM output data (heads, hydrographs, land-use areas). They were moved from visualization.webapi so that CLI tools, notebooks, and scripts can use them without importing the web viewer.

Head Data Loader#

Lazy HDF5 reader for time-varying groundwater head data.

Lazy nodal data loader for IWFM HDF5 output.

This module provides the LazyHeadDataLoader class for loading time-varying nodal data (heads or subsidence) from HDF5 files without reading all timesteps into memory at once.

Supports three IWFM native HDF5 dataset formats:

  • GWHeadAtAllNodes — groundwater head at all nodes

  • SubsidenceAtAllNodes — subsidence at all nodes (v4.1/v5.1)

  • pyiwfm format (dataset head with shape (n_timesteps, n_nodes, n_layers))

All native formats store data as (n_timesteps, n_nodes * n_layers) with layer-major ordering.

class pyiwfm.io.head_loader.LazyHeadDataLoader(file_path, dataset_name='head', cache_size=50, n_layers=None)[source]#

Bases: object

Lazy loader for time-varying nodal data backed by HDF5 files.

Uses an LRU cache to keep a bounded number of timesteps in memory. Supports dict-like access via loader[datetime].

Auto-detects the dataset type (head vs subsidence) from the HDF5 contents. The data_type property reports what was found.

Parameters:
  • file_path (Path or str) – Path to the HDF5 file containing nodal data.

  • dataset_name (str, optional) – Name of the HDF5 dataset for pyiwfm-format files. Default is “head”.

  • cache_size (int, optional) – Maximum number of timesteps to cache. Default is 50.

Examples

>>> loader = LazyHeadDataLoader("GW_HeadAll.hdf5")
>>> print(loader.times[:3])
>>> head_at_t0 = loader[loader.times[0]]
>>> print(head_at_t0.shape)  # (n_nodes, n_layers)
__init__(file_path, dataset_name='head', cache_size=50, n_layers=None)[source]#

Initialize the lazy loader.

Parameters:
  • file_path (Path or str) – Path to the HDF5 file containing nodal data.

  • dataset_name (str, optional) – Name of the HDF5 dataset for pyiwfm-format files.

  • cache_size (int, optional) – Maximum number of timesteps to cache. Default is 50.

  • n_layers (int or None, optional) – Number of model layers. When provided, takes precedence over any NLayers attribute in the HDF5 file. Required for IWFM native HDF5 files that lack the attribute (which is the common case — the Fortran writer does not store it).

property times: list[datetime]#

Get available time steps.

property n_frames: int#

Get number of available frames.

property shape: tuple[int, int]#

Get shape of each data array (n_nodes, n_layers).

property data_type: str#

"head" or "subsidence".

Type:

Get the detected data type

property n_layers: int#

Get number of layers.

property n_nodes: int#

Get number of nodes.

get_frame(frame_idx)[source]#

Get nodal data for a specific frame index.

Parameters:

frame_idx (int) – Frame index (0-based).

Returns:

Nodal values, shape (n_nodes, n_layers).

Return type:

NDArray[np.float64]

get_head(frame_idx, layer, node_ids=None)[source]#

Get nodal values for a single layer at a specific timestep.

This method is used by ResultsExtractor to access per-layer data for FE interpolation.

Parameters:
  • frame_idx (int) – Frame index (0-based).

  • layer (int) – 0-based layer index.

  • node_ids (tuple[int, ] or None) – If provided, return values only at these 1-based node IDs. If None, return values at all nodes.

Returns:

Values at the requested nodes for the given layer, or None if the frame is out of range.

Return type:

NDArray[np.float64] or None

get_composite_subsidence(frame_idx)[source]#

Get total subsidence at each node by summing across all layers.

Subsidence is additive across layers (unlike head which uses T-weighted averaging). This matches the Fortran behavior in Class_IWFM2OBS.f90:ApplyMultiLayerSubsidence.

Parameters:

frame_idx (int) – Frame index (0-based).

Returns:

Total subsidence per node, shape (n_nodes,), or None if out of range.

Return type:

NDArray[np.float64] or None

__getitem__(key)[source]#

Get data by datetime or frame index.

Parameters:

key (datetime or int) – Time step or frame index.

Returns:

Nodal values.

Return type:

NDArray[np.float64]

to_dict()[source]#

Load all frames into a dict (for use with TimeAnimationController).

This loads all data into memory. For large datasets, prefer using the lazy access via __getitem__.

Returns:

All data keyed by datetime.

Return type:

dict[datetime, NDArray[np.float64]]

get_layer_range(layer, percentile_lo=2.0, percentile_hi=98.0, max_frames=0)[source]#

Compute robust min/max values across all (or sampled) timesteps.

Parameters:
  • layer (int) – 1-based layer number. Use 0 for composite subsidence (sum across all layers).

  • percentile_lo (float) – Percentiles for robust range (default 2nd-98th).

  • percentile_hi (float) – Percentiles for robust range (default 2nd-98th).

  • max_frames (int) – If > 0, sample at most this many evenly-spaced frames instead of scanning all timesteps.

Returns:

(min_value, max_value, n_frames_scanned)

Return type:

tuple[float, float, int]

clear_cache()[source]#

Clear the frame cache.

Hydrograph Reader#

Parser for IWFM .out text hydrograph files.

Reader for IWFM hydrograph output text files (.out).

Parses both GW and stream hydrograph output files which share the same IWFM text format with header metadata and whitespace-delimited time series.

class pyiwfm.io.hydrograph_reader.IWFMHydrographReader(filepath)[source]#

Bases: object

Reader for IWFM hydrograph output text files.

Parses the header for column metadata (hydrograph IDs, layers, node/element IDs) and reads the full time series data into a NumPy array for efficient column extraction.

Parameters:

filepath (Path or str) – Path to the IWFM hydrograph output file (.out).

__init__(filepath)[source]#
property n_columns: int#

Number of data columns.

property n_timesteps: int#

Number of timesteps.

property times: list[str]#

ISO 8601 datetime strings for each timestep.

property hydrograph_ids: list[int]#

1-based hydrograph IDs from header.

property layers: list[int]#

Layer numbers per column (GW only).

property node_ids: list[int]#

Node/element IDs per column.

get_time_series(column_index)[source]#

Get time series for a specific column.

Parameters:

column_index (int) – 0-based column index.

Returns:

(times, values) where times are ISO 8601 strings.

Return type:

tuple[list[str], list[float]]

get_columns_as_smp_dict(bore_ids, filter_ids=None)[source]#

Extract specified columns as SMPTimeSeries keyed by bore ID.

Bridges the .out file reader to the interpolation pipeline.

Parameters:
  • bore_ids (dict[int, str]) – Mapping of 0-based column index to bore ID string.

  • filter_ids (set[int] | None) – Optional set of column indices to process. When provided, only columns whose index is in filter_ids are included; all others are skipped. This avoids loading every column from large .out files when only a subset is needed.

Returns:

Time series keyed by bore ID, suitable for interpolate_batch().

Return type:

dict[str, SMPTimeSeries]

find_column_by_node_id(node_id)[source]#

Find column index for a given node/element ID.

Parameters:

node_id (int) – Node or element ID to search for.

Returns:

0-based column index, or None if not found.

Return type:

int or None

Hydrograph Loader#

Lazy HDF5-backed hydrograph loader (same interface as IWFMHydrographReader).

Lazy hydrograph data loader backed by HDF5.

Provides the same interface as IWFMHydrographReader but reads from HDF5 cache files produced by hydrograph_converter.py. This avoids loading the full text file into memory and enables LRU-cached access.

class pyiwfm.io.hydrograph_loader.LazyHydrographDataLoader(file_path, cache_size=100)[source]#

Bases: object

Lazy loader for hydrograph time series backed by HDF5.

Exposes the same public interface as IWFMHydrographReader so the web viewer route code can use either interchangeably.

Parameters:
  • file_path (Path or str) – Path to the HDF5 cache file.

  • cache_size (int) – Number of timestep rows to keep in the LRU cache.

__init__(file_path, cache_size=100)[source]#
property n_columns: int#
property n_timesteps: int#
property times: list[str]#
property hydrograph_ids: list[int]#
property layers: list[int]#
property node_ids: list[int]#
get_row(row_idx)[source]#

Get a cached row by index.

get_time_series(column_index)[source]#

Get time series for a specific column.

Parameters:

column_index (int) – 0-based column index.

Returns:

(times, values) where times are ISO 8601 strings.

Return type:

tuple[list[str], list[float]]

find_column_by_node_id(node_id)[source]#

Find column index for a given node/element ID.

Area Data Loader#

Lazy HDF5 reader for land-use area data and multi-type area manager.

Lazy area data loader for IWFM web visualization.

Provides LazyAreaDataLoader for loading time-varying land-use area data from HDF5 files without reading all timesteps into memory, and AreaDataManager which manages all four land-use area file types.

class pyiwfm.io.area_loader.LazyAreaDataLoader(file_path, dataset='area', cache_size=50)[source]#

Bases: object

Lazy HDF5 reader for land-use area data with LRU cache.

Parameters:
  • file_path (Path or str) – Path to the HDF5 file containing area data.

  • dataset (str) – Name of the HDF5 dataset. Default is “area”.

  • cache_size (int) – Maximum number of timestep frames to cache. Default is 50.

__init__(file_path, dataset='area', cache_size=50)[source]#
property n_frames: int#
property n_elements: int#
property n_cols: int#
property times: list[str]#
property element_ids: ndarray[tuple[Any, ...], dtype[int32]]#
get_frame(frame_idx)[source]#

Get area data for one timestep.

Returns array of shape (n_elements, n_cols).

get_element_timeseries(element_idx)[source]#

Get all timesteps for one element using HDF5 hyperslab slicing.

Returns array of shape (n_timesteps, n_cols).

get_layer_range(col=-1, percentile_lo=2.0, percentile_hi=98.0, max_frames=50)[source]#

Get percentile range across sampled timesteps.

Parameters:
  • col (int) – Column index, or -1 to sum all columns (total area).

  • percentile_lo (float) – Percentiles for robust range.

  • percentile_hi (float) – Percentiles for robust range.

  • max_frames (int) – Max number of frames to sample.

Returns:

(min_value, max_value, n_frames_scanned)

Return type:

tuple[float, float, int]

clear_cache()[source]#
class pyiwfm.io.area_loader.AreaDataManager[source]#

Bases: object

Manages lazy loaders for all 4 land-use area file types.

__init__()[source]#
load_from_rootzone(rz, cache_dir)[source]#

Convert area files to HDF5 if needed and create lazy loaders.

get_snapshot(timestep)[source]#

Get aggregated land-use data for all elements at one timestep.

Returns a dict mapping element_id to a dict with keys: agricultural, urban, native_riparian, water, total_area, dominant.

get_element_breakdown(element_id, timestep=0)[source]#

Get per-column area breakdown for one element at one timestep.

Returns a dict mapping land-use label (e.g. "nonponded") to a list of per-column areas. Each entry corresponds to a column in the HDF5 dataset for that land-use type.

get_element_timeseries(element_id)[source]#

Get all-timestep timeseries for one element across all land-use types.

Returns a dict with keys per land-use type, each containing areas (list of per-timestep values) and column count info.

get_dates(iso=True)[source]#

Return timestep dates from whichever loader is available.

Parameters:

iso (bool) – If True, convert IWFM dates to ISO YYYY-MM-DD format so JavaScript/Plotly can parse them.

property n_timesteps: int#

SQLite Cache Builder#

Pre-computes aggregates from HDF5/text loaders into a single SQLite database.

SQLite cache builder for the IWFM web viewer.

Reads from existing HDF5/text loaders and pre-computes aggregates into a single SQLite database for fast serving. The cache provides:

  • Pre-computed element-averaged head values (by-element)

  • Pre-computed head ranges per layer (2nd/98th percentile)

  • Pre-computed budget summaries (total/average per column)

  • Pre-aggregated zone budget totals

  • Unified hydrograph timeseries (GW/stream/subsidence from output files)

  • Stream rating tables

  • Land-use area snapshots with dominant-type classification

class pyiwfm.io.cache_builder.SqliteCacheBuilder(cache_path)[source]#

Bases: object

Builds the SQLite viewer cache from model data and loaders.

Usage:

builder = SqliteCacheBuilder(cache_path)
builder.build(model, head_loader, budget_readers, ...)
__init__(cache_path)[source]#
build(model, head_loader=None, budget_readers=None, area_manager=None, gw_hydrograph_reader=None, stream_hydrograph_reader=None, subsidence_reader=None, tile_drain_reader=None, subsidence_loader=None, progress_callback=None)[source]#

Build the complete cache.

Parameters:
  • model (IWFMModel) – The loaded model.

  • head_loader (LazyHeadDataLoader, optional) – Head data loader (HDF5-backed).

  • budget_readers (dict, optional) – Mapping of budget_type -> BudgetReader.

  • area_manager (AreaDataManager, optional) – Land-use area data manager.

  • gw_hydrograph_reader (IWFMHydrographReader, optional)

  • stream_hydrograph_reader (IWFMHydrographReader, optional)

  • subsidence_reader (IWFMHydrographReader, optional)

  • tile_drain_reader (IWFMHydrographReader, optional)

  • subsidence_loader (LazyHeadDataLoader, optional) – Subsidence surface data loader (SubsidenceAtAllNodes HDF5).

  • progress_callback (callable, optional) – Called with (step_name, pct) for progress reporting.

pyiwfm.io.cache_builder.get_source_mtimes(model)[source]#

Collect modification times of key source files for staleness check.

pyiwfm.io.cache_builder.is_cache_stale(cache_path, model)[source]#

Check if the cache file is older than any source data file.

SQLite Cache Loader#

Read-only access to the viewer SQLite cache.

SQLite cache loader for the IWFM web viewer.

Provides fast read access to pre-computed data stored by SqliteCacheBuilder. Uses WAL mode for concurrent reads and connection pooling for thread safety.

class pyiwfm.io.cache_loader.SqliteCacheLoader(cache_path)[source]#

Bases: object

Read-only access to the viewer SQLite cache.

Thread-safe via per-thread connections with WAL mode.

Parameters:

cache_path (Path) – Path to the SQLite cache file.

__init__(cache_path)[source]#
close()[source]#

Close the per-thread connection if open.

get_metadata(key)[source]#

Get a metadata value by key.

get_timesteps()[source]#

Get all timestep datetimes as ISO strings.

get_head_frame(frame_idx)[source]#

Get a raw head frame (n_nodes, n_layers).

Returns None if frame not cached.

get_head_by_element(frame_idx, layer)[source]#

Get pre-computed element-averaged heads.

Returns (values_array, min_val, max_val) or None.

get_head_range(layer)[source]#

Get the pre-computed head range for a layer.

Returns dict with percentile_02, percentile_98, abs_min, abs_max.

get_n_head_frames()[source]#

Get the number of cached head frames.

get_subsidence_by_element(frame_idx, layer)[source]#

Get pre-computed element-averaged subsidence.

Parameters:
  • frame_idx (int) – Frame index (0-based).

  • layer (int) – Layer number (1-based), or 0 for composite (sum across layers).

  • (values_array (Returns)

  • min_val

  • None. (max_val) or)

get_subsidence_range(layer)[source]#

Get the pre-computed subsidence range for a layer.

Returns dict with percentile_02, percentile_98, abs_min, abs_max.

get_n_subsidence_frames()[source]#

Get the number of cached subsidence frames.

get_subsidence_timesteps()[source]#

Get subsidence timestep datetimes as ISO strings.

get_budget_types()[source]#

Get list of cached budget types.

get_budget_locations(budget_type)[source]#

Get locations for a budget type as (idx, name, area) tuples.

get_budget_columns(budget_type)[source]#

Get columns for a budget type as (idx, name, units) tuples.

get_budget_data(budget_type, location_idx)[source]#

Get budget data for a location as (n_timesteps, n_cols) array.

get_budget_summary(budget_type, location_idx)[source]#

Get budget summaries as (col_idx, total, average) tuples.

get_hydrograph(hydro_type, column_idx)[source]#

Get a hydrograph timeseries.

Returns (times_list, values_array) or None.

get_hydrograph_columns(hydro_type)[source]#

Get hydrograph column metadata as (col_idx, node_id, layer) tuples.

get_gw_hydrograph_all_layers(node_id)[source]#

Get all GW hydrograph layers for a node.

Returns list of (layer, times, values) tuples, or None.

get_gw_hydrograph_by_columns(base_col, n_layers)[source]#

Get GW hydrograph layers by consecutive column range.

IWFM GW hydrograph output files store n_layers consecutive columns per location: [loc_lay1, loc_lay2, …, loc_layN].

Returns list of (layer, times, values) tuples, or None.

get_stream_rating(stream_node_id)[source]#

Get a stream node’s rating table.

Returns (bottom_elev, stages, flows) or None.

get_area_snapshot(frame_idx)[source]#

Get a land-use snapshot for a timestep.

Returns list of element dicts or None.

get_area_timesteps(area_type='landuse')[source]#

Get area timestep dates.

get_stats()[source]#

Get cache statistics for diagnostics.

Observation File I/O#

SMP Module#

Reader and writer for IWFM SMP (Sample/Bore) observation files, the standard format for observed and simulated time series at well locations.

SMP (Sample/Bore) file reader and writer for IWFM observation data.

The SMP format is IWFM’s standard observation file format used by IWFM2OBS and other calibration utilities. It uses fixed-width columns:

  • Columns 1-25: Bore ID (left-justified)

  • Columns 26-37: Date (MM/DD/YYYY)

  • Columns 38-49: Time (HH:MM:SS)

  • Columns 50-60: Value (numeric)

  • Column 61+: Optional ‘X’ for excluded records

Sentinel values (-1.1E38, -9.1E37, etc.) are treated as NaN.

Example

>>> from pyiwfm.io.smp import SMPReader, SMPWriter
>>> reader = SMPReader("observations.smp")
>>> data = reader.read()
>>> for bore_id, ts in data.items():
...     print(f"{bore_id}: {len(ts.values)} records")
class pyiwfm.io.smp.SMPRecord(bore_id, datetime, value, excluded=False)[source]#

Bases: object

A single SMP observation record.

Variables:
  • bore_id (str) – Observation well / bore identifier.

  • datetime (datetime) – Date and time of observation.

  • value (float) – Observed value (e.g., water level elevation).

  • excluded (bool) – If True, record is flagged as excluded (X marker).

bore_id: str#
datetime: datetime#
value: float#
excluded: bool = False#
__init__(bore_id, datetime, value, excluded=False)#
class pyiwfm.io.smp.SMPTimeSeries(bore_id, times, values, excluded)[source]#

Bases: object

Time series for a single bore from an SMP file.

Variables:
  • bore_id (str) – Observation well / bore identifier.

  • times (NDArray[np.datetime64]) – Array of observation timestamps.

  • values (NDArray[np.float64]) – Array of observed values.

  • excluded (NDArray[np.bool_]) – Boolean mask; True where records are excluded.

bore_id: str#
times: ndarray[tuple[Any, ...], dtype[datetime64]]#
values: ndarray[tuple[Any, ...], dtype[float64]]#
excluded: ndarray[tuple[Any, ...], dtype[bool]]#
property n_records: int#

Number of records in the time series.

property valid_mask: ndarray[tuple[Any, ...], dtype[bool]]#

Mask of non-NaN, non-excluded records.

__init__(bore_id, times, values, excluded)#
class pyiwfm.io.smp.SMPReader(filepath)[source]#

Bases: BaseReader

Reader for IWFM SMP (Sample/Bore) observation files.

Parameters:

filepath (Path | str) – Path to the SMP file.

Example

>>> reader = SMPReader("obs_wells.smp")
>>> data = reader.read()
>>> ts = data["WELL_01"]
>>> print(ts.n_records)
property format: str#

Return the file format identifier.

read()[source]#

Read all bore time series from the SMP file.

Returns:

Mapping of bore ID to time series.

Return type:

dict[str, SMPTimeSeries]

property bore_ids: list[str]#

Return list of bore IDs without reading all data.

read_bore(bore_id)[source]#

Read time series for a single bore.

Parameters:

bore_id (str) – The bore identifier to read.

Returns:

Time series for the bore, or None if not found.

Return type:

SMPTimeSeries | None

class pyiwfm.io.smp.SMPWriter(filepath, fixed_format=False)[source]#

Bases: BaseWriter

Writer for IWFM SMP (Sample/Bore) observation files.

Parameters:
  • filepath (Path | str) – Path to the output SMP file.

  • fixed_format (bool) – If True, enforce CalcTypeHyd Fortran format (A25,A12,A12,A11) with left-justified date/time fields. Default False.

Example

>>> writer = SMPWriter("output.smp")
>>> writer.write(data)
>>> # CalcTypeHyd-compatible output
>>> writer = SMPWriter("output.smp", fixed_format=True)
>>> writer.write(data)
__init__(filepath, fixed_format=False)[source]#

Initialize the writer.

Parameters:

filepath (Path | str) – Path to the output file

property format: str#

Return the file format identifier.

write(data)[source]#

Write all bore time series to the SMP file.

Parameters:

data (dict[str, SMPTimeSeries]) – Mapping of bore ID to time series.

write_bore(ts)[source]#

Append a single bore’s time series to the file.

Parameters:

ts (SMPTimeSeries) – Time series to write.

static merge(files, output)[source]#

Concatenate multiple SMP files into one, with proper newline handling.

Parameters:
  • files (list[Path | str]) – Input SMP file paths.

  • output (Path | str) – Output SMP file path.

Returns:

Total number of lines written.

Return type:

int

pyiwfm.io.smp.smp_transform(input_path, output_path, func)[source]#

Apply a value transform to all records in an SMP file.

Reads the input SMP file, applies func to each value, and writes the result to the output file. Preserves bore IDs, dates, and formatting.

Parameters:
  • input_path (Path | str) – Input SMP file.

  • output_path (Path | str) – Output SMP file.

  • func (callable) – Transform function applied to each value. Receives a float, returns a float. Example: lambda v: math.copysign(math.sqrt(abs(v)), v) for signed sqrt.

Returns:

Number of records transformed.

Return type:

int

Example

>>> import math
>>> smp_transform("stream.smp", "stream_sqrt.smp",
...     lambda v: math.copysign(math.sqrt(abs(v)), v))

Simulation Messages#

Parser for IWFM SimulationMessages.out files, extracting messages by severity (INFO, WARN, FATAL) and spatial IDs (node, element, reach, layer) via regex patterns.

Parser for IWFM SimulationMessages.out files.

SimulationMessages.out is produced by IWFM during simulation and contains warnings, errors, and informational messages with spatial context (node IDs, element IDs, reach IDs, layer IDs). Additionally parses convergence iteration counts, mass balance errors, and timestep cut / damping factor events.

Example

>>> from pyiwfm.io.simulation_messages import SimulationMessagesReader
>>> reader = SimulationMessagesReader("SimulationMessages.out")
>>> result = reader.read()
>>> print(f"Warnings: {result.warning_count}, Errors: {result.error_count}")
>>> for msg in result.filter_by_severity(MessageSeverity.WARN):
...     print(msg.text[:80])
>>> summary = result.get_convergence_summary()
>>> print(f"Max iterations: {summary['max_iterations']}")
class pyiwfm.io.simulation_messages.MessageSeverity(*values)[source]#

Bases: Enum

Severity levels for simulation messages.

MESSAGE = 0#
INFO = 1#
WARN = 2#
FATAL = 3#
class pyiwfm.io.simulation_messages.SimulationMessage(severity, text, procedure, line_number, node_ids=<factory>, element_ids=<factory>, reach_ids=<factory>, layer_ids=<factory>)[source]#

Bases: object

A single parsed simulation message.

Variables:
  • severity (MessageSeverity) – Message severity level.

  • text (str) – Full message text (joined from continuation lines).

  • procedure (str) – Name of the Fortran procedure that generated the message.

  • line_number (int) – Line number in the file where the message starts.

  • node_ids (list[int]) – Node IDs extracted from the message text.

  • element_ids (list[int]) – Element IDs extracted from the message text.

  • reach_ids (list[int]) – Reach IDs extracted from the message text.

  • layer_ids (list[int]) – Layer IDs extracted from the message text.

severity: MessageSeverity#
text: str#
procedure: str#
line_number: int#
node_ids: list[int]#
element_ids: list[int]#
reach_ids: list[int]#
layer_ids: list[int]#
__init__(severity, text, procedure, line_number, node_ids=<factory>, element_ids=<factory>, reach_ids=<factory>, layer_ids=<factory>)#
class pyiwfm.io.simulation_messages.ConvergenceRecord(timestep_index, date, iteration_count, max_residual, convergence_achieved, bottleneck_variable='', supply_adj_count=0)[source]#

Bases: object

Record of convergence iteration data for a single timestep.

Variables:
  • timestep_index (int) – 1-based timestep number.

  • date (str) – IWFM date string (MM/DD/YYYY_HH:MM) for the timestep.

  • iteration_count (int) – Number of solver iterations for this timestep.

  • max_residual (float | None) – Maximum residual at final iteration, if reported.

  • convergence_achieved (bool) – Whether the solver converged within the allowed iterations.

timestep_index: int#
date: str#
iteration_count: int#
max_residual: float | None#
convergence_achieved: bool#
bottleneck_variable: str = ''#
supply_adj_count: int = 0#
__init__(timestep_index, date, iteration_count, max_residual, convergence_achieved, bottleneck_variable='', supply_adj_count=0)#
class pyiwfm.io.simulation_messages.ConvergenceHotspot(variable, entity_type, entity_id, layer, occurrence_count, total_iterations, worst_timestep_index, worst_timestep_date)[source]#

Bases: object

Aggregated convergence bottleneck information for a single variable.

Variables:
  • variable (str) – Variable identifier (e.g. "GW_25393_(L1)").

  • entity_type (str) – Entity type: "groundwater", "stream", or "lake".

  • entity_id (int) – Numeric ID of the entity.

  • layer (int | None) – Layer number for groundwater nodes.

  • occurrence_count (int) – Number of timesteps where this was the bottleneck variable.

  • total_iterations (int) – Sum of iteration counts across all timesteps where this was bottleneck.

  • worst_timestep_index (int) – Timestep with the most iterations for this variable.

  • worst_timestep_date (str) – Date of the worst timestep.

variable: str#
entity_type: str#
entity_id: int#
layer: int | None#
occurrence_count: int#
total_iterations: int#
worst_timestep_index: int#
worst_timestep_date: str#
__init__(variable, entity_type, entity_id, layer, occurrence_count, total_iterations, worst_timestep_index, worst_timestep_date)#
class pyiwfm.io.simulation_messages.MassBalanceRecord(timestep_index, date, component, error_value, error_percent)[source]#

Bases: object

Mass balance error record for a single timestep and component.

Variables:
  • timestep_index (int) – 1-based timestep number.

  • date (str) – IWFM date string for the timestep.

  • component (str) – Hydrologic component (e.g. "groundwater", "stream", "rootzone").

  • error_value (float) – Absolute mass balance error value.

  • error_percent (float | None) – Mass balance error as a percentage, if reported.

timestep_index: int#
date: str#
component: str#
error_value: float#
error_percent: float | None#
__init__(timestep_index, date, component, error_value, error_percent)#
class pyiwfm.io.simulation_messages.TimestepCutRecord(timestep_index, date, reason, new_dt)[source]#

Bases: object

Record of a timestep cut or damping factor adjustment.

Variables:
  • timestep_index (int) – 1-based timestep number where the cut occurred.

  • date (str) – IWFM date string for the timestep.

  • reason (str) – Description of why the timestep was cut (e.g. "TIME STEP CUT", "DAMPING").

  • new_dt (float | None) – New timestep size after the cut, if reported.

timestep_index: int#
date: str#
reason: str#
new_dt: float | None#
__init__(timestep_index, date, reason, new_dt)#
class pyiwfm.io.simulation_messages.SimulationMessagesResult(messages, total_runtime, warning_count, error_count, convergence_records=<factory>, mass_balance_records=<factory>, timestep_cuts=<factory>, max_iterations=0, avg_iterations=0.0)[source]#

Bases: object

Result of parsing a SimulationMessages.out file.

Variables:
  • messages (list[SimulationMessage]) – All parsed messages.

  • total_runtime (timedelta | None) – Total simulation runtime if found in the summary.

  • warning_count (int) – Number of warning messages.

  • error_count (int) – Number of fatal/error messages.

  • convergence_records (list[ConvergenceRecord]) – Per-timestep convergence iteration data.

  • mass_balance_records (list[MassBalanceRecord]) – Per-timestep mass balance error data.

  • timestep_cuts (list[TimestepCutRecord]) – Timestep cut / damping adjustment events.

  • max_iterations (int) – Highest iteration count across all timesteps (0 if none parsed).

  • avg_iterations (float) – Average iteration count across all timesteps (0.0 if none parsed).

messages: list[SimulationMessage]#
total_runtime: timedelta | None#
warning_count: int#
error_count: int#
convergence_records: list[ConvergenceRecord]#
mass_balance_records: list[MassBalanceRecord]#
timestep_cuts: list[TimestepCutRecord]#
max_iterations: int = 0#
avg_iterations: float = 0.0#
get_convergence_summary()[source]#

Return summary statistics for convergence and mass balance.

Returns:

Dictionary with keys:

  • max_iterations — highest iteration count across all timesteps

  • avg_iterations — mean iteration count

  • total_timesteps — number of timesteps with convergence data

  • converged_count — number of timesteps that converged

  • failed_count — number of timesteps that did NOT converge

  • convergence_rate — fraction of timesteps that converged

  • timestep_cuts — total number of timestep cut events

  • mass_balance_records — total number of mass balance records

  • max_mass_balance_error — largest absolute mass balance error

  • components_with_errors — sorted list of unique component names

Return type:

dict[str, Any]

get_hotspots()[source]#

Return convergence hotspot variables sorted by occurrence count.

Groups convergence records by bottleneck_variable and aggregates occurrence counts, total iterations, and worst timestep.

Returns:

Hotspots sorted by occurrence count (descending).

Return type:

list[ConvergenceHotspot]

filter_by_severity(severity)[source]#

Return messages matching the given severity.

Parameters:

severity (MessageSeverity) – Severity level to filter by.

Returns:

Filtered messages.

Return type:

list[SimulationMessage]

get_spatial_summary()[source]#

Summarize message counts by spatial entity.

Returns:

Mapping from entity type ("nodes", "elements", "reaches", "layers") to a dict of ID → message count.

Return type:

dict[str, dict[int, int]]

to_geodataframe(grid)[source]#

Convert spatial message summary to a GeoDataFrame.

Parameters:

grid (AppGrid) – The model grid for looking up node/element coordinates.

Returns:

Point geometries at node locations or element centroids with message count attributes.

Return type:

GeoDataFrame

__init__(messages, total_runtime, warning_count, error_count, convergence_records=<factory>, mass_balance_records=<factory>, timestep_cuts=<factory>, max_iterations=0, avg_iterations=0.0)#
class pyiwfm.io.simulation_messages.SimulationMessagesReader(filepath)[source]#

Bases: BaseReader

Reader for IWFM SimulationMessages.out files.

Parameters:

filepath (Path | str) – Path to the SimulationMessages.out file.

property format: str#

Return the file format identifier.

read()[source]#

Parse the SimulationMessages.out file.

Returns:

Parsed messages with spatial context, convergence records, mass balance errors, and timestep cut events.

Return type:

SimulationMessagesResult

Drawdown#

Compute drawdown (head change) relative to a reference timestep, with support for per-node, per-element, robust range, and maximum drawdown map computations.

Drawdown computation from IWFM head data.

This module provides the DrawdownComputer class for computing drawdown (head change) relative to a reference timestep. Drawdown is defined as head(t_ref) - head(t) so that positive values indicate water level decline.

Sentinel values (< -9000) used by IWFM to represent dry cells are treated as NaN.

class pyiwfm.io.drawdown.DrawdownComputer(head_loader)[source]#

Bases: object

Compute drawdown (head change) relative to a reference timestep.

Drawdown = head(t_ref) - head(t) (positive = water level decline)

Parameters:

head_loader (LazyHeadDataLoader) – Loader providing lazy access to per-timestep head arrays.

__init__(head_loader)[source]#
compute_drawdown(timestep, reference_timestep=0, layer=1)[source]#

Compute per-node drawdown for a single timestep vs reference.

Parameters:
  • timestep (int) – 0-based target timestep index.

  • reference_timestep (int) – 0-based reference timestep index. Default is the first timestep (0).

  • layer (int) – 1-based layer number.

Returns:

Array of shape (n_nodes,). Positive values mean water level decline. NaN where either timestep has a dry-cell sentinel.

Return type:

NDArray[np.float64]

compute_drawdown_by_element(timestep, reference_timestep=0, layer=1, grid=None)[source]#

Compute per-element drawdown (average of element node values).

Each element’s drawdown is the arithmetic mean of its vertex node drawdown values.

Parameters:
  • timestep (int) – 0-based target timestep index.

  • reference_timestep (int) – 0-based reference timestep index.

  • layer (int) – 1-based layer number.

  • grid (AppGrid or None) – The model grid. Required to look up element vertices.

Returns:

Array of shape (n_elements,) ordered by element ID. NaN where any contributing node is NaN.

Return type:

NDArray[np.float64]

Raises:

ValueError – If grid is None.

compute_drawdown_range(reference_timestep=0, layer=1, max_frames=0)[source]#

Compute robust min/max drawdown across all timesteps.

Uses the 2nd and 98th percentile to exclude outliers.

Parameters:
  • reference_timestep (int) – 0-based reference timestep index.

  • layer (int) – 1-based layer number.

  • max_frames (int) – If > 0, sample at most this many evenly-spaced frames instead of scanning every timestep.

Returns:

(min_drawdown, max_drawdown) across all (sampled) timesteps. Falls back to (0.0, 1.0) if no valid data exists.

Return type:

tuple[float, float]

compute_max_drawdown_map(reference_timestep=0, layer=1)[source]#

Compute maximum drawdown at each node across all timesteps.

Parameters:
  • reference_timestep (int) – 0-based reference timestep index.

  • layer (int) – 1-based layer number.

Returns:

Array of shape (n_nodes,). Each entry is the maximum drawdown observed at that node over all timesteps. NaN where the node is always dry.

Return type:

NDArray[np.float64]

Stream Depletion#

Compare baseline and pumping-scenario model runs to quantify stream flow depletion at individual reaches.

Stream depletion analysis — compare streamflow between baseline and pumping scenarios.

class pyiwfm.io.stream_depletion.StreamDepletionResult(reach_id, reach_name, times, baseline_flow, scenario_flow, depletion, cumulative_depletion, max_depletion, max_depletion_timestep, total_depletion)[source]#

Bases: object

Result of stream depletion analysis at a single reach/node.

reach_id: int#
reach_name: str#
times: list[str]#
baseline_flow: NDArray[np.float64]#
scenario_flow: NDArray[np.float64]#
depletion: NDArray[np.float64]#
cumulative_depletion: NDArray[np.float64]#
max_depletion: float#
max_depletion_timestep: int#
total_depletion: float#
to_dict()[source]#

Serialize for API response.

__init__(reach_id, reach_name, times, baseline_flow, scenario_flow, depletion, cumulative_depletion, max_depletion, max_depletion_timestep, total_depletion)#
class pyiwfm.io.stream_depletion.StreamDepletionReport(results, n_reaches, n_timesteps, total_max_depletion, total_cumulative_depletion)[source]#

Bases: object

Aggregate depletion results across multiple reaches.

results: list[StreamDepletionResult]#
n_reaches: int#
n_timesteps: int#
total_max_depletion: float#
total_cumulative_depletion: float#
to_dict()[source]#

Serialize for API response.

__init__(results, n_reaches, n_timesteps, total_max_depletion, total_cumulative_depletion)#
pyiwfm.io.stream_depletion.compute_stream_depletion(baseline_dir, scenario_dir, reach_ids=None, budget_type='Stream Reach Budgets')[source]#

Compute stream depletion by comparing two model runs.

Parameters:
  • baseline_dir (str or Path) – Path to the baseline (no-pumping) model results directory.

  • scenario_dir (str or Path) – Path to the pumping scenario model results directory.

  • reach_ids (list[int] or None) – Specific reach IDs to analyze. If None, analyze all reaches.

  • budget_type (str) – Budget type name to look for in the HDF5 files.

Returns:

Depletion analysis results.

Return type:

StreamDepletionReport

Budget Checks#

Mass balance sanity checks for IWFM budget data — detect timesteps where inflows, outflows, and storage change are out of balance.

Water budget mass balance sanity checks.

This module provides utilities for verifying mass balance closure in IWFM budget output. Given a BudgetReader, it classifies columns as inflows, outflows, or storage changes and computes per-timestep discrepancies.

Example

>>> from pyiwfm.io.budget import BudgetReader
>>> from pyiwfm.io.budget_checks import check_budget_balance
>>> reader = BudgetReader("GW_Budget.hdf")
>>> report = check_budget_balance(reader, location_index=0)
>>> print(report.max_percent_error)
class pyiwfm.io.budget_checks.BalanceCheckResult(timestep_index, date, total_inflow, total_outflow, storage_change, discrepancy, percent_error, within_tolerance)[source]#

Bases: object

Result of a mass balance check for one timestep.

timestep_index: int#
date: str#
total_inflow: float#
total_outflow: float#
storage_change: float#
discrepancy: float#
percent_error: float#
within_tolerance: bool#
__init__(timestep_index, date, total_inflow, total_outflow, storage_change, discrepancy, percent_error, within_tolerance)#
class pyiwfm.io.budget_checks.BudgetSanityReport(budget_type, location, n_timesteps, n_violations, max_percent_error, mean_percent_error, records=<factory>)[source]#

Bases: object

Full sanity check report for a budget.

budget_type: str#
location: str#
n_timesteps: int#
n_violations: int#
max_percent_error: float#
mean_percent_error: float#
records: list[BalanceCheckResult]#
to_summary_dict()[source]#

Serialize summary (without per-timestep records) for API.

__init__(budget_type, location, n_timesteps, n_violations, max_percent_error, mean_percent_error, records=<factory>)#
pyiwfm.io.budget_checks.check_budget_balance(reader, location_index=0, tolerance_percent=1.0, inflow_columns=None, outflow_columns=None, storage_column=None)[source]#

Check mass balance for a budget location.

If column names are not specified, auto-detect by looking for columns containing 'inflow'/'recharge'/'gain' for inflows, 'outflow'/'discharge'/'loss' for outflows, and 'storage'/'change' for storage.

Parameters:
  • reader (BudgetReader) – An already-opened budget reader.

  • location_index (int) – Zero-based location index.

  • tolerance_percent (float) – Maximum acceptable percent error.

  • inflow_columns (list[str] or None) – Explicit column names for inflows/outflows.

  • outflow_columns (list[str] or None) – Explicit column names for inflows/outflows.

  • storage_column (str or None) – Explicit storage column name.

Return type:

BudgetSanityReport

pyiwfm.io.budget_checks.check_all_budgets(budget_readers, tolerance_percent=1.0)[source]#

Check mass balance across all available budgets.

Parameters:
  • budget_readers (dict[str, BudgetReader]) – Mapping of budget type name to reader instance.

  • tolerance_percent (float) – Maximum acceptable percent error per timestep.

Returns:

Mapping of budget type name to its sanity report (first location only).

Return type:

dict[str, BudgetSanityReport]

Zone File I/O#

The zones module provides readers and writers for IWFM zone definition files (used by ZBudget) and GeoJSON zone files.

Zone file I/O for IWFM and GeoJSON formats.

This module handles reading and writing zone definition files:

  • IWFM ZBudget format (text-based element-zone assignments)

  • GeoJSON format (geospatial zone boundaries)

IWFM Zone File Format#

The IWFM zone file format is used by ZBudget for water budget analysis:

` C Comment lines start with C 1                           # ZExtent: 1=horizontal, 0=vertical 1  Sacramento Valley        # Zone ID and name 2  San Joaquin Valley /                           # Separator 1    1                      # Element 1 -> Zone 1 2    1                      # Element 2 -> Zone 1 3    2                      # Element 3 -> Zone 2 `

Example

Read zone file:

>>> from pyiwfm.io.zones import read_iwfm_zone_file, write_iwfm_zone_file
>>> zone_def = read_iwfm_zone_file("ZBudget_Zones.dat")
>>> print(f"Loaded {zone_def.n_zones} zones")

Write zone file:

>>> write_iwfm_zone_file(zone_def, "output_zones.dat")
pyiwfm.io.zones.read_iwfm_zone_file(filepath, element_areas=None)[source]#

Read an IWFM-format zone definition file.

Parameters:
  • filepath (Path or str) – Path to the zone file.

  • element_areas (dict[int, float], optional) – Element areas for zone area calculation.

Returns:

The loaded zone definition.

Return type:

ZoneDefinition

Notes

The IWFM zone file format: - Lines starting with ‘C’ or ‘*’ are comments - First data line: ZExtent (1=horizontal, 0=vertical) - Zone definitions: ID and optional name - Separator: ‘/’ - Element assignments: element_id zone_id

Examples

>>> zone_def = read_iwfm_zone_file("zones.dat")
>>> print(f"Loaded {zone_def.n_zones} zones with {zone_def.n_elements} elements")
pyiwfm.io.zones.write_iwfm_zone_file(zone_def, filepath, header_comment='')[source]#

Write a zone definition to IWFM format file.

Parameters:
  • zone_def (ZoneDefinition) – The zone definition to write.

  • filepath (Path or str) – Output file path.

  • header_comment (str, optional) – Comment text for file header.

Examples

>>> write_iwfm_zone_file(zone_def, "output_zones.dat", header_comment="Custom zones")
pyiwfm.io.zones.read_geojson_zones(filepath, zone_id_field='id', zone_name_field='name', element_id_field='element_id')[source]#

Read zone definitions from a GeoJSON file.

Parameters:
  • filepath (Path or str) – Path to the GeoJSON file.

  • zone_id_field (str, optional) – Property field for zone ID. Default is “id”.

  • zone_name_field (str, optional) – Property field for zone name. Default is “name”.

  • element_id_field (str, optional) – Property field for element IDs (array). Default is “element_id”.

Returns:

The loaded zone definition.

Return type:

ZoneDefinition

Notes

Expected GeoJSON structure:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "id": 1,
        "name": "Zone A",
        "element_id": [1, 2, 3, 4]
      },
      "geometry": { ... }
    }
  ]
}
pyiwfm.io.zones.write_geojson_zones(zone_def, filepath, grid=None, include_geometry=True)[source]#

Write zone definitions to a GeoJSON file.

Parameters:
  • zone_def (ZoneDefinition) – The zone definition to write.

  • filepath (Path or str) – Output file path.

  • grid (AppGrid, optional) – Model grid for computing zone geometries.

  • include_geometry (bool, optional) – Include zone boundary geometries. Default is True.

Notes

If grid is provided and include_geometry is True, zone boundaries are computed as the convex hull of element centroids.

pyiwfm.io.zones.auto_detect_zone_file(filepath)[source]#

Auto-detect the format of a zone file.

Parameters:

filepath (Path or str) – Path to the zone file.

Returns:

Detected format: “iwfm”, “geojson”, or “unknown”.

Return type:

str

pyiwfm.io.zones.read_zone_file(filepath, element_areas=None)[source]#

Read a zone file, auto-detecting the format.

Parameters:
  • filepath (Path or str) – Path to the zone file.

  • element_areas (dict[int, float], optional) – Element areas for zone area calculation (IWFM format only).

Returns:

The loaded zone definition.

Return type:

ZoneDefinition

Raises:

ValueError – If file format cannot be determined.

Examples

>>> zone_def = read_zone_file("zones.dat")  # IWFM format
>>> zone_def = read_zone_file("zones.geojson")  # GeoJSON format
pyiwfm.io.zones.write_zone_file(zone_def, filepath, grid=None)[source]#

Write a zone file, choosing format based on extension.

Parameters:
  • zone_def (ZoneDefinition) – The zone definition to write.

  • filepath (Path or str) – Output file path.

  • grid (AppGrid, optional) – Model grid for GeoJSON geometry computation.

Examples

>>> write_zone_file(zone_def, "zones.dat")  # IWFM format
>>> write_zone_file(zone_def, "zones.geojson", grid)  # GeoJSON format

Supported formats:

  • IWFM ZBudget format: Text-based element-to-zone assignments with zone names and extents (horizontal/vertical)

  • GeoJSON: Standard geospatial format with geometry and properties for interoperability with GIS tools