Visualization#

The visualization modules provide tools for exporting model data to various formats for visualization in external tools.

GIS Export#

The GIS export module provides functionality for exporting model data to GIS formats including GeoPackage, Shapefile, and GeoJSON.

GIS export functionality for IWFM models.

This module provides the GISExporter class for exporting IWFM model data to various GIS formats including GeoPackage, Shapefile, and GeoJSON.

Supported Formats#

  • GeoPackage (.gpkg): Recommended format, single file with multiple layers

  • Shapefile (.shp): Widely compatible but limited to 10-char field names

  • GeoJSON (.geojson): Text-based, good for web applications

Example

Export a mesh to GeoPackage:

>>> from pyiwfm.core.mesh import AppGrid, Node, Element
>>> from pyiwfm.visualization.gis_export import GISExporter
>>>
>>> # Create simple mesh
>>> nodes = {1: Node(id=1, x=0.0, y=0.0), 2: Node(id=2, x=100.0, y=0.0),
...          3: Node(id=3, x=50.0, y=100.0)}
>>> elements = {1: Element(id=1, vertices=(1, 2, 3))}
>>> grid = AppGrid(nodes=nodes, elements=elements)
>>> grid.compute_connectivity()
>>>
>>> # Export to GeoPackage
>>> exporter = GISExporter(grid=grid, crs="EPSG:26910")
>>> exporter.export_geopackage("model.gpkg")
class pyiwfm.visualization.gis_export.GISExporter(grid, stratigraphy=None, streams=None, crs=None)[source]#

Bases: object

Export IWFM model data to GIS formats.

This class converts model meshes, stratigraphy, and stream networks to GeoDataFrames that can be exported to various GIS formats.

Parameters:
  • grid (AppGrid) – Model mesh to export.

  • stratigraphy (Stratigraphy, optional) – Model stratigraphy. If provided, layer elevations are added as attributes to the nodes GeoDataFrame.

  • streams (AppStream, optional) – Stream network. If provided, enables stream layer export.

  • crs (str, optional) – Coordinate reference system (e.g., ‘EPSG:26910’, ‘EPSG:2227’). If None, output files will have no CRS defined.

Raises:

ImportError – If geopandas or shapely are not installed.

Examples

Basic export to GeoPackage:

>>> exporter = GISExporter(grid=grid, crs="EPSG:26910")
>>> exporter.export_geopackage("model.gpkg")

Export with stratigraphy data:

>>> exporter = GISExporter(grid=grid, stratigraphy=strat, crs="EPSG:26910")
>>> gdf = exporter.nodes_to_geodataframe()
>>> # GeoDataFrame includes gs_elev, layer_1_top, layer_1_bottom, etc.

Export with custom attributes:

>>> head_data = {1: 50.0, 2: 52.0, 3: 48.0}  # node_id -> value
>>> gdf = exporter.nodes_to_geodataframe(attributes={"head_ft": head_data})
>>> gdf.to_file("nodes_with_heads.gpkg", driver="GPKG")

Export to multiple formats:

>>> exporter.export_geopackage("model.gpkg")  # GeoPackage
>>> exporter.export_shapefiles("shapefiles/")  # Shapefiles
>>> exporter.export_geojson("elements.geojson", layer="elements")
__init__(grid, stratigraphy=None, streams=None, crs=None)[source]#

Initialize the GIS exporter.

Parameters:
  • grid (AppGrid) – Model mesh

  • stratigraphy (Stratigraphy | None) – Model stratigraphy (optional)

  • streams (AppStream | None) – Stream network (optional)

  • crs (str | None) – Coordinate reference system (e.g., ‘EPSG:26910’)

nodes_to_geodataframe(attributes=None)[source]#

Convert mesh nodes to a GeoDataFrame.

Parameters:

attributes (dict[str, dict[int, Any]] | None) – Optional dict of attribute_name -> {node_id: value}

Returns:

GeoDataFrame with node points

Return type:

GeoDataFrame

elements_to_geodataframe(attributes=None)[source]#

Convert mesh elements to a GeoDataFrame.

Parameters:

attributes (dict[str, dict[int, Any]] | None) – Optional dict of attribute_name -> {element_id: value}

Returns:

GeoDataFrame with element polygons

Return type:

GeoDataFrame

streams_to_geodataframe()[source]#

Convert stream network to a GeoDataFrame.

Returns:

GeoDataFrame with stream reach linestrings

Return type:

GeoDataFrame

subregions_to_geodataframe()[source]#

Convert subregions to a GeoDataFrame (dissolved elements).

Returns:

GeoDataFrame with subregion polygons

Return type:

GeoDataFrame

boundary_to_geodataframe()[source]#

Extract model boundary as a GeoDataFrame.

Returns:

GeoDataFrame with model boundary polygon

Return type:

GeoDataFrame

export_geopackage(output_path, include_streams=True, include_subregions=True, include_boundary=True)[source]#

Export model to GeoPackage format.

Parameters:
  • output_path (Path | str) – Output file path (.gpkg)

  • include_streams (bool) – Include stream network layer

  • include_subregions (bool) – Include subregions layer

  • include_boundary (bool) – Include boundary layer

export_shapefiles(output_dir, include_streams=True, include_subregions=True, include_boundary=True)[source]#

Export model to Shapefile format.

Creates separate shapefiles for nodes, elements, etc.

Parameters:
  • output_dir (Path | str) – Output directory

  • include_streams (bool) – Include stream network shapefile

  • include_subregions (bool) – Include subregions shapefile

  • include_boundary (bool) – Include boundary shapefile

export_geojson(output_path, layer='elements')[source]#

Export a single layer to GeoJSON format.

Parameters:
  • output_path (Path | str) – Output file path (.geojson)

  • layer (str) – Layer to export (‘nodes’, ‘elements’, ‘streams’, ‘subregions’, ‘boundary’)

VTK Export#

The VTK export module provides functionality for exporting 3D model data to VTK formats for visualization in ParaView.

VTK export functionality for IWFM models.

This module provides classes for exporting IWFM model data to VTK formats for 3D visualization in tools like ParaView.

class pyiwfm.visualization.vtk_export.VTKExporter(grid, stratigraphy=None)[source]#

Bases: object

Export IWFM model data to VTK formats.

This class converts model meshes and stratigraphy to VTK UnstructuredGrid objects that can be exported to VTU or legacy VTK formats for visualization in ParaView.

Variables:
  • grid – Model mesh

  • stratigraphy – Model stratigraphy (optional, required for 3D)

__init__(grid, stratigraphy=None)[source]#

Initialize the VTK exporter.

Parameters:
  • grid (AppGrid) – Model mesh

  • stratigraphy (Stratigraphy | None) – Model stratigraphy (optional, required for 3D)

create_2d_mesh()[source]#

Create a 2D VTK UnstructuredGrid from the mesh.

Returns:

VTK UnstructuredGrid with 2D mesh

Return type:

vtk.vtkUnstructuredGrid

create_3d_mesh()[source]#

Create a 3D VTK UnstructuredGrid from mesh and stratigraphy.

Quad elements become hexahedra, triangles become wedges.

Returns:

VTK UnstructuredGrid with 3D mesh

Raises:

ValueError – If stratigraphy is not set

Return type:

vtk.vtkUnstructuredGrid

add_node_scalar(vtk_grid, name, values)[source]#

Add scalar data to mesh nodes.

Parameters:
  • vtk_grid (vtk.vtkUnstructuredGrid) – VTK grid to add data to

  • name (str) – Scalar array name

  • values (NDArray[np.float64]) – Scalar values (one per node)

add_cell_scalar(vtk_grid, name, values)[source]#

Add scalar data to mesh cells.

Parameters:
  • vtk_grid (vtk.vtkUnstructuredGrid) – VTK grid to add data to

  • name (str) – Scalar array name

  • values (NDArray[np.float64]) – Scalar values (one per cell)

export_vtu(output_path, mode='2d', node_scalars=None, cell_scalars=None)[source]#

Export mesh to VTU format (XML-based VTK).

Parameters:
  • output_path (Path | str) – Output file path (.vtu)

  • mode (Literal['2d', '3d']) – ‘2d’ for surface mesh, ‘3d’ for volumetric mesh

  • node_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for node data

  • cell_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for cell data

export_vtk(output_path, mode='2d', node_scalars=None, cell_scalars=None)[source]#

Export mesh to legacy VTK format.

Parameters:
  • output_path (Path | str) – Output file path (.vtk)

  • mode (Literal['2d', '3d']) – ‘2d’ for surface mesh, ‘3d’ for volumetric mesh

  • node_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for node data

  • cell_scalars (dict[str, ndarray[tuple[Any, ...], dtype[float64]]] | None) – Dict of name -> values for cell data

to_pyvista_3d(node_scalars=None, cell_scalars=None)[source]#

Create a PyVista UnstructuredGrid from mesh and stratigraphy.

This method converts the IWFM mesh and stratigraphy to a PyVista UnstructuredGrid for use in interactive 3D visualization. The resulting mesh can be used with PyVista plotting functions or the Trame web visualization framework.

Parameters:
  • node_scalars (dict[str, NDArray], optional) – Dictionary of scalar arrays to add to mesh nodes. Keys are array names, values are 1D arrays with one value per node (for 2D) or per node-surface point (for 3D).

  • cell_scalars (dict[str, NDArray], optional) – Dictionary of scalar arrays to add to mesh cells. Keys are array names, values are 1D arrays with one value per cell.

Returns:

PyVista UnstructuredGrid with 3D volumetric mesh if stratigraphy is available, otherwise 2D surface mesh.

Return type:

pv.UnstructuredGrid

Raises:

ImportError – If PyVista is not installed.

Examples

Create a 3D mesh for visualization:

>>> exporter = VTKExporter(grid=grid, stratigraphy=strat)
>>> pv_mesh = exporter.to_pyvista_3d()
>>> pv_mesh.plot()

Add scalar data:

>>> kh_values = np.random.rand(n_cells)
>>> pv_mesh = exporter.to_pyvista_3d(cell_scalars={"Kh": kh_values})
>>> pv_mesh.plot(scalars="Kh", cmap="viridis")

Plotting#

The plotting module provides matplotlib-based visualization tools for creating 2D plots of model meshes and data.

Plotting functionality for IWFM models.

This module re-exports all public plotting functions and classes from the sub-modules plot_mesh, plot_timeseries, plot_budget, and plot_calibration.

Importing from pyiwfm.visualization.plotting continues to work exactly as before – every name listed in __all__ is available at this level.

class pyiwfm.visualization.plotting.MeshPlotter(grid, streams=None, figsize=(10, 8))[source]#

Bases: object

High-level class for creating mesh visualizations.

This class provides a convenient interface for creating multi-layer visualizations of IWFM model meshes.

Variables:
  • grid – Model mesh

  • streams – Stream network (optional)

__init__(grid, streams=None, figsize=(10, 8))[source]#

Initialize the mesh plotter.

Parameters:
plot_mesh(show_edges=True, show_node_ids=False, show_element_ids=False, show_streams=False, **kwargs)[source]#

Plot the mesh with optional overlays.

Parameters:
  • show_edges (bool) – Show element edges

  • show_node_ids (bool) – Label nodes with their IDs

  • show_element_ids (bool) – Label elements with their IDs

  • show_streams (bool) – Overlay stream network

  • **kwargs (Any) – Additional arguments passed to plot_mesh

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

plot_composite(show_mesh=True, show_streams=False, node_values=None, cell_values=None, title=None, cmap='viridis', **kwargs)[source]#

Create a composite plot with multiple layers.

Parameters:
  • show_mesh (bool) – Show mesh edges

  • show_streams (bool) – Overlay stream network

  • node_values (ndarray[tuple[Any, ...], dtype[float64]] | None) – Scalar values at nodes (optional)

  • cell_values (ndarray[tuple[Any, ...], dtype[float64]] | None) – Scalar values at cells (optional)

  • title (str | None) – Plot title

  • cmap (str) – Colormap for scalar values

  • **kwargs (Any) – Additional arguments

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

save(output_path, dpi=150, **kwargs)[source]#

Save the current figure to file.

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

  • dpi (int) – Resolution in dots per inch

  • **kwargs (Any) – Additional arguments passed to savefig

pyiwfm.visualization.plotting.plot_mesh(grid, ax=None, show_edges=True, show_node_ids=False, show_element_ids=False, edge_color='black', edge_width=0.5, fill_color='lightblue', alpha=0.3, figsize=(10, 8))[source]#

Plot the mesh with elements and optional annotations.

Parameters:
  • grid (AppGrid) – Model mesh

  • ax (Axes | None) – Existing axes to plot on (creates new if None)

  • show_edges (bool) – Show element edges

  • show_node_ids (bool) – Label nodes with their IDs

  • show_element_ids (bool) – Label elements with their IDs

  • edge_color (str) – Color for element edges

  • edge_width (float) – Width of edge lines

  • fill_color (str) – Fill color for elements

  • alpha (float) – Transparency of element fill

  • figsize (tuple[float, float]) – Figure size in inches

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_nodes(grid, ax=None, highlight_boundary=False, marker_size=20, color='blue', boundary_color='red', figsize=(10, 8))[source]#

Plot mesh nodes as points.

Parameters:
  • grid (AppGrid) – Model mesh

  • ax (Axes | None) – Existing axes to plot on

  • highlight_boundary (bool) – Use different color for boundary nodes

  • marker_size (float) – Size of node markers

  • color (str) – Color for interior nodes

  • boundary_color (str) – Color for boundary nodes

  • figsize (tuple[float, float]) – Figure size in inches

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_elements(grid, ax=None, color_by='none', cmap='viridis', show_colorbar=True, edge_color='black', edge_width=0.5, alpha=0.7, figsize=(10, 8))[source]#

Plot mesh elements with optional coloring by attribute.

Parameters:
  • grid (AppGrid) – Model mesh

  • ax (Axes | None) – Existing axes to plot on

  • color_by (Literal['subregion', 'area', 'none']) – Attribute to color elements by

  • cmap (str) – Colormap name

  • show_colorbar (bool) – Show colorbar for colored plots

  • edge_color (str) – Color for element edges

  • edge_width (float) – Width of edge lines

  • alpha (float) – Transparency of element fill

  • figsize (tuple[float, float]) – Figure size in inches

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_scalar_field(grid, values, field_type='node', ax=None, cmap='viridis', show_colorbar=True, vmin=None, vmax=None, show_mesh=True, edge_color='gray', edge_width=0.3, n_subdiv=4, figsize=(10, 8))[source]#

Plot scalar field values on the mesh.

Parameters:
  • grid (AppGrid) – Model mesh

  • values (NDArray[np.float64]) – Scalar values (one per node or cell)

  • field_type (Literal['node', 'cell']) – ‘node’ for node values, ‘cell’ for cell values

  • ax (Axes | None) – Existing axes to plot on

  • cmap (str) – Colormap name

  • show_colorbar (bool) – Show colorbar

  • vmin (float | None) – Minimum value for colormap

  • vmax (float | None) – Maximum value for colormap

  • show_mesh (bool) – Show mesh edges

  • edge_color (str) – Color for mesh edges

  • edge_width (float) – Width of mesh edges

  • n_subdiv (int) – Subdivision level for bilinear quad interpolation (>=2 enables FE subdivision; 1 uses legacy diagonal-split triangulation)

  • figsize (tuple[float, float]) – Figure size in inches

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_streams(streams, ax=None, show_nodes=False, line_color='blue', line_width=2.0, node_color='blue', node_size=30, figsize=(10, 8))[source]#

Plot stream network.

Parameters:
  • streams (AppStream) – Stream network

  • ax (Axes | None) – Existing axes to plot on

  • show_nodes (bool) – Show stream node markers

  • line_color (str) – Color for stream lines

  • line_width (float) – Width of stream lines

  • node_color (str) – Color for stream nodes

  • node_size (float) – Size of stream node markers

  • figsize (tuple[float, float]) – Figure size in inches

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_lakes(lakes, grid, ax=None, fill_color='cyan', edge_color='blue', edge_width=1.5, alpha=0.5, show_labels=True, label_fontsize=9, cmap=None, figsize=(10, 8))[source]#

Plot lake elements on the mesh.

Parameters:
  • lakes (AppLake) – Lake component containing lake definitions and element assignments.

  • grid (AppGrid) – Model mesh used to look up element vertex coordinates.

  • ax (Axes, optional) – Existing axes to plot on. Creates new figure if None.

  • fill_color (str, default "cyan") – Fill color for lake elements (used when cmap is None).

  • edge_color (str, default "blue") – Edge color for lake element polygons.

  • edge_width (float, default 1.5) – Width of lake element edges.

  • alpha (float, default 0.5) – Transparency of lake element fill.

  • show_labels (bool, default True) – Show lake name labels at the centroid of each lake.

  • label_fontsize (float, default 9) – Font size for lake labels.

  • cmap (str, optional) – If provided, color each lake with a different color from this colormap instead of using fill_color.

  • figsize (tuple, default (10, 8)) – Figure size in inches.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_boundary(grid, ax=None, line_color='black', line_width=2.0, fill=False, fill_color='lightgray', alpha=0.3, figsize=(10, 8))[source]#

Plot model boundary.

Parameters:
  • grid (AppGrid) – Model mesh

  • ax (Axes | None) – Existing axes to plot on

  • line_color (str) – Color for boundary line

  • line_width (float) – Width of boundary line

  • fill (bool) – Fill the boundary polygon

  • fill_color (str) – Fill color

  • alpha (float) – Fill transparency

  • figsize (tuple[float, float]) – Figure size in inches

Returns:

Tuple of (Figure, Axes)

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_timeseries(timeseries, ax=None, title=None, xlabel='Date', ylabel=None, legend=True, colors=None, linestyles=None, markers=None, figsize=(12, 6), grid=True, date_format=None)[source]#

Plot one or more time series as line charts.

Parameters:
  • timeseries (TimeSeries or sequence of TimeSeries) – Time series data to plot. Can be a single TimeSeries or a list.

  • ax (Axes, optional) – Existing axes to plot on. Creates new figure if None.

  • title (str, optional) – Plot title.

  • xlabel (str, default "Date") – X-axis label.

  • ylabel (str, optional) – Y-axis label. Uses units from first time series if not specified.

  • legend (bool, default True) – Show legend.

  • colors (sequence of str, optional) – Line colors for each series.

  • linestyles (sequence of str, optional) – Line styles for each series (e.g., ‘-’, ‘–’, ‘:’).

  • markers (sequence of str, optional) – Markers for each series (e.g., ‘o’, ‘s’, ‘^’).

  • figsize (tuple, default (12, 6)) – Figure size in inches.

  • grid (bool, default True) – Show grid lines.

  • date_format (str, optional) – Date format for x-axis (e.g., ‘%Y-%m’).

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

Examples

Plot a single time series:

>>> ts = TimeSeries(times=times, values=values, name='Head', units='ft')
>>> fig, ax = plot_timeseries(ts, title='Groundwater Head')

Plot multiple time series:

>>> fig, ax = plot_timeseries([ts1, ts2, ts3], legend=True)
pyiwfm.visualization.plotting.plot_timeseries_comparison(observed, simulated, ax=None, title=None, show_residuals=False, show_metrics=True, obs_color='blue', sim_color='red', obs_marker='o', figsize=(12, 8))[source]#

Plot observed vs simulated time series comparison.

Parameters:
  • observed (TimeSeries) – Observed data time series.

  • simulated (TimeSeries) – Simulated/modeled data time series.

  • ax (Axes, optional) – Existing axes. Creates new figure if None.

  • title (str, optional) – Plot title.

  • show_residuals (bool, default False) – Show residual subplot below main plot.

  • show_metrics (bool, default True) – Display comparison metrics (RMSE, NSE, etc.) on plot.

  • obs_color (str, default "blue") – Color for observed data.

  • sim_color (str, default "red") – Color for simulated data.

  • obs_marker (str, default "o") – Marker for observed data points.

  • figsize (tuple, default (12, 8)) – Figure size.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

Examples

>>> fig, ax = plot_timeseries_comparison(
...     observed=obs_ts,
...     simulated=sim_ts,
...     title='Head Calibration - Well 1',
...     show_metrics=True
... )
pyiwfm.visualization.plotting.plot_timeseries_collection(collection, locations=None, ax=None, title=None, max_series=10, figsize=(12, 6), **kwargs)[source]#

Plot multiple time series from a collection.

Parameters:
  • collection (TimeSeriesCollection) – Collection of time series data.

  • locations (sequence of str, optional) – Specific locations to plot. Plots all if None.

  • ax (Axes, optional) – Existing axes to plot on.

  • title (str, optional) – Plot title. Uses collection name if not specified.

  • max_series (int, default 10) – Maximum number of series to plot (for readability).

  • figsize (tuple, default (12, 6)) – Figure size.

  • **kwargs (Any) – Additional arguments passed to plot_timeseries.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_timeseries_statistics(collection, ax=None, band='minmax', mean_color='steelblue', band_alpha=0.25, show_individual=False, individual_alpha=0.15, title=None, ylabel=None, figsize=(12, 6))[source]#

Plot ensemble mean with min/max or standard-deviation bands.

Parameters:
  • collection (TimeSeriesCollection) – Collection of time series data.

  • ax (Axes, optional) – Existing axes to plot on.

  • band ({"minmax", "std"}, default "minmax") – Band type: min/max envelope or +/- 1 standard deviation.

  • mean_color (str, default "steelblue") – Color for the mean line.

  • band_alpha (float, default 0.25) – Transparency for the shaded band.

  • show_individual (bool, default False) – If True, draw each individual series behind the statistics.

  • individual_alpha (float, default 0.15) – Alpha for individual series lines.

  • title (str, optional) – Plot title.

  • ylabel (str, optional) – Y-axis label.

  • figsize (tuple, default (12, 6)) – Figure size in inches.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_dual_axis(ts1, ts2, ax=None, color1='steelblue', color2='coral', style1='-', style2='-', label1=None, label2=None, ylabel1=None, ylabel2=None, title=None, figsize=(12, 6))[source]#

Dual y-axis comparison of two time series.

Parameters:
  • ts1 (TimeSeries) – The two time series to plot.

  • ts2 (TimeSeries) – The two time series to plot.

  • ax (Axes, optional) – Primary axes. If None, a new figure is created.

  • color1 (str) – Colors for the two series.

  • color2 (str) – Colors for the two series.

  • style1 (str) – Line styles (e.g., “-”, “–”, “o-“).

  • style2 (str) – Line styles (e.g., “-”, “–”, “o-“).

  • label1 (str, optional) – Legend labels. Falls back to ts.name.

  • label2 (str, optional) – Legend labels. Falls back to ts.name.

  • ylabel1 (str, optional) – Y-axis labels. Falls back to ts.units.

  • ylabel2 (str, optional) – Y-axis labels. Falls back to ts.units.

  • title (str, optional) – Plot title.

  • figsize (tuple, default (12, 6)) – Figure size in inches.

Returns:

(Figure, (Axes_left, Axes_right)).

Return type:

tuple

pyiwfm.visualization.plotting.plot_streamflow_hydrograph(times, flows, baseflow=None, ax=None, flow_color='steelblue', baseflow_color='darkorange', fill_alpha=0.3, log_scale=False, title='Streamflow Hydrograph', ylabel='Flow', units='cfs', figsize=(14, 6))[source]#

Plot streamflow hydrograph with optional baseflow separation.

Parameters:
  • times (ndarray) – Datetime array for x-axis.

  • flows (ndarray) – Total streamflow values.

  • baseflow (ndarray, optional) – Baseflow component. If provided, the area between total flow and baseflow is shaded to highlight the quickflow component.

  • ax (Axes, optional) – Existing axes to plot on.

  • flow_color (str, default "steelblue") – Color for the total flow line.

  • baseflow_color (str, default "darkorange") – Color for the baseflow line.

  • fill_alpha (float, default 0.3) – Alpha for the shaded quickflow area.

  • log_scale (bool, default False) – If True, use log scale for the y-axis.

  • title (str, default "Streamflow Hydrograph") – Plot title.

  • ylabel (str, default "Flow") – Y-axis label prefix.

  • units (str, default "cfs") – Flow units appended to ylabel.

  • figsize (tuple, default (14, 6)) – Figure size in inches.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

class pyiwfm.visualization.plotting.BudgetPlotter(budgets, times=None, units='AF')[source]#

Bases: object

High-level class for creating budget visualizations.

This class provides convenience methods for creating various budget-related plots from IWFM model output data.

Parameters:
  • budgets (dict) – Budget data organized by time step or as totals.

  • times (array, optional) – Time array for time-series plots.

  • units (str, default "AF") – Volume units.

Examples

>>> plotter = BudgetPlotter(budgets={'Precip': 1000, 'ET': -600})
>>> fig, ax = plotter.bar_chart()
>>> plotter.save('budget.png')
__init__(budgets, times=None, units='AF')[source]#
bar_chart(**kwargs)[source]#

Create bar chart of budget components.

stacked_area(**kwargs)[source]#

Create stacked area chart over time.

pie_chart(**kwargs)[source]#

Create pie chart of budget distribution.

line_chart(**kwargs)[source]#

Create line chart of budget components over time.

save(output_path, dpi=150, **kwargs)[source]#

Save current figure to file.

pyiwfm.visualization.plotting.plot_budget_bar(components, ax=None, title='Water Budget', orientation='vertical', inflow_color='steelblue', outflow_color='coral', show_values=True, units='AF', figsize=(10, 6))[source]#

Plot water budget components as a bar chart.

Parameters:
  • components (dict) – Dictionary of component names to values. Positive values are inflows, negative values are outflows.

  • ax (Axes, optional) – Existing axes to plot on.

  • title (str, default "Water Budget") – Plot title.

  • orientation ({'vertical', 'horizontal'}, default 'vertical') – Bar orientation.

  • inflow_color (str, default "steelblue") – Color for inflow (positive) bars.

  • outflow_color (str, default "coral") – Color for outflow (negative) bars.

  • show_values (bool, default True) – Show values on bars.

  • units (str, default "AF") – Units for y-axis label and values.

  • figsize (tuple, default (10, 6)) – Figure size.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

Examples

>>> budget = {
...     'Precipitation': 1500,
...     'Stream Inflow': 800,
...     'Pumping': -1200,
...     'Evapotranspiration': -600,
...     'Stream Outflow': -400,
... }
>>> fig, ax = plot_budget_bar(budget, title='Annual Water Budget')
pyiwfm.visualization.plotting.plot_budget_horizontal_bars(inflows, outflows, storage_change=0.0, discrepancy=0.0, ax=None, table_ax=None, title='Water Budget', inflow_label='Inflows', outflow_label='Outflows', units='AF/yr', inflow_colors=None, outflow_colors=None, color_map=None, show_table=True, table_fontsize=7.0, number_fontsize=6.5, figsize=(6.5, 4.0))[source]#

Horizontal stacked bar chart for water budget data.

Two horizontal bars (inflows, outflows) with stacked colored segments, plus a smaller bar for storage change. Segments are numbered with white-on-outline text. An optional legend table maps numbers to component names and values.

Parameters:
  • inflows (list of (name, value) tuples) – Inflow components sorted largest-first.

  • outflows (list of (name, value) tuples) – Outflow components sorted largest-first.

  • storage_change (float) – Net storage change (positive = increase).

  • discrepancy (float) – Balance error / discrepancy.

  • ax (Axes, optional) – Axes for the bar chart. Creates new figure if None.

  • table_ax (Axes, optional) – Axes for the legend table. If None and show_table is True, a table is placed below the chart.

  • title (str) – Chart title.

  • inflow_label (str) – Category labels for the y-axis.

  • outflow_label (str) – Category labels for the y-axis.

  • units (str) – Units string for x-axis label and table header.

  • inflow_colors (sequence of str, optional) – Override palettes for inflow/outflow segments.

  • outflow_colors (sequence of str, optional) – Override palettes for inflow/outflow segments.

  • color_map (dict, optional) – Map of component name -> hex color. Falls back to palette.

  • show_table (bool) – Whether to draw the legend table.

  • table_fontsize (float) – Font size for the legend table.

  • number_fontsize (float) – Font size for segment numbers on bars.

  • figsize (tuple) – Figure size when creating a new figure.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_budget_stacked(times, components, ax=None, title='Water Budget Over Time', inflows_above=True, cmap='tab10', alpha=0.8, units='AF', show_legend=True, legend_loc='outside lower center', figsize=(14, 7))[source]#

Plot water budget components as stacked area chart over time.

Parameters:
  • times (array) – Time array (datetime64).

  • components (dict) – Dictionary of component names to time series arrays. Positive values are inflows, negative values are outflows.

  • ax (Axes, optional) – Existing axes to plot on.

  • title (str, default "Water Budget Over Time") – Plot title.

  • inflows_above (bool, default True) – Plot inflows above x-axis and outflows below.

  • cmap (str, default "tab10") – Colormap for components.

  • alpha (float, default 0.8) – Fill transparency.

  • units (str, default "AF") – Units for y-axis label.

  • show_legend (bool, default True) – Show legend.

  • legend_loc (str, default "outside lower center") – Legend location string passed to fig.legend(loc=...).

  • figsize (tuple, default (14, 7)) – Figure size.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

Examples

>>> times = np.array(['2020-01', '2020-02', '2020-03'], dtype='datetime64')
>>> components = {
...     'Precipitation': np.array([100, 150, 80]),
...     'Pumping': np.array([-50, -60, -55]),
... }
>>> fig, ax = plot_budget_stacked(times, components)
pyiwfm.visualization.plotting.plot_budget_pie(components, ax=None, title='Water Budget Distribution', budget_type='both', cmap='tab10', show_values=True, units='AF', figsize=(10, 8))[source]#

Plot water budget components as pie chart(s).

Parameters:
  • components (dict) – Dictionary of component names to values.

  • ax (Axes, optional) – Existing axes (ignored if budget_type=’both’).

  • title (str, default "Water Budget Distribution") – Plot title.

  • budget_type ({'inflow', 'outflow', 'both'}, default 'both') – Which components to show.

  • cmap (str, default "tab10") – Colormap for slices.

  • show_values (bool, default True) – Show values in labels.

  • units (str, default "AF") – Units for value labels.

  • figsize (tuple, default (10, 8)) – Figure size.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_water_balance(inflows, outflows, storage_change=0.0, ax=None, title='Water Balance Summary', units='AF', figsize=(12, 6))[source]#

Plot comprehensive water balance summary with inflows, outflows, and storage.

Parameters:
  • inflows (dict) – Dictionary of inflow component names to values.

  • outflows (dict) – Dictionary of outflow component names to values (positive values).

  • storage_change (float, default 0.0) – Change in storage (positive = increase).

  • ax (Axes, optional) – Existing axes to plot on.

  • title (str, default "Water Balance Summary") – Plot title.

  • units (str, default "AF") – Volume units.

  • figsize (tuple, default (12, 6)) – Figure size.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

Examples

>>> inflows = {'Precip': 1000, 'Stream In': 500, 'Recharge': 200}
>>> outflows = {'ET': 600, 'Pumping': 800, 'Stream Out': 300}
>>> fig, ax = plot_water_balance(inflows, outflows, storage_change=-100)
pyiwfm.visualization.plotting.plot_zbudget(zone_budgets, ax=None, title='Zone Budget Summary', plot_type='bar', units='AF', cmap='RdYlBu', figsize=(12, 8))[source]#

Plot zone budget data for multiple zones.

Parameters:
  • zone_budgets (dict) – Dictionary mapping zone ID to budget component dictionaries. Example: {1: {‘Inflow’: 100, ‘Outflow’: -80}, 2: {…}}

  • ax (Axes, optional) – Existing axes to plot on.

  • title (str, default "Zone Budget Summary") – Plot title.

  • plot_type ({'bar', 'heatmap'}, default 'bar') – Type of plot to create.

  • units (str, default "AF") – Volume units.

  • cmap (str, default "RdYlBu") – Colormap for heatmap.

  • figsize (tuple, default (12, 8)) – Figure size.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

Examples

>>> zone_budgets = {
...     1: {'Recharge': 500, 'Pumping': -300, 'Flow to Zone 2': -100},
...     2: {'Recharge': 300, 'Pumping': -200, 'Flow from Zone 1': 100},
... }
>>> fig, ax = plot_zbudget(zone_budgets, title='Subregion Budgets')
pyiwfm.visualization.plotting.plot_budget_timeseries(times, budgets, cumulative=False, ax=None, title='Budget Components Over Time', units='AF', show_net=True, figsize=(14, 6))[source]#

Plot budget component time series as line charts.

Parameters:
  • times (array) – Time array (datetime64).

  • budgets (dict) – Dictionary of component names to value arrays.

  • cumulative (bool, default False) – Plot cumulative values.

  • ax (Axes, optional) – Existing axes to plot on.

  • title (str, default "Budget Components Over Time") – Plot title.

  • units (str, default "AF") – Volume units.

  • show_net (bool, default True) – Show net budget line.

  • figsize (tuple, default (14, 6)) – Figure size.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_streams_colored(grid, streams, values, ax=None, cmap='Blues', vmin=None, vmax=None, line_width=2.0, show_colorbar=True, colorbar_label='', show_mesh=True, mesh_alpha=0.15, figsize=(10, 8))[source]#

Color stream reaches by a scalar value (e.g., flow rate, gaining/losing).

Parameters:
  • grid (AppGrid) – Model mesh (plotted as background when show_mesh is True).

  • streams (AppStream) – Stream network.

  • values (ndarray) – One value per reach, used for coloring.

  • ax (Axes, optional) – Existing axes to plot on.

  • cmap (str, default "Blues") – Matplotlib colormap name.

  • vmin (float, optional) – Limits for the color scale.

  • vmax (float, optional) – Limits for the color scale.

  • line_width (float, default 2.0) – Width of stream lines.

  • show_colorbar (bool, default True) – Whether to add a colorbar.

  • colorbar_label (str, default "") – Label for the colorbar.

  • show_mesh (bool, default True) – Whether to draw the mesh as background.

  • mesh_alpha (float, default 0.15) – Alpha for mesh background.

  • figsize (tuple, default (10, 8)) – Figure size in inches.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_cross_section(cross_section, ax=None, layer_colors=None, layer_labels=None, scalar_name=None, layer_property_name=None, layer_property_cmap='viridis', show_ground_surface=True, alpha=0.7, title=None, figsize=(14, 6))[source]#

Plot a cross-section through an IWFM model.

Supports three rendering modes that can be combined:

  1. Default (no property): Layers filled with flat colors via fill_between.

  2. Layer property (layer_property_name): Each layer band is color-mapped by a per-layer property (e.g. hydraulic conductivity).

  3. Scalar overlay (scalar_name): A dashed line showing a per-sample scalar value (e.g. water table elevation).

Parameters:
  • cross_section (CrossSection) – Cross-section data from CrossSectionExtractor.

  • ax (Axes, optional) – Existing axes to plot on. Creates a new figure if None.

  • layer_colors (sequence of str, optional) – Fill colors for each layer (used when layer_property_name is None). Defaults to brown tones.

  • layer_labels (sequence of str, optional) – Legend labels for each layer. Defaults to “Layer 1”, “Layer 2”, etc.

  • scalar_name (str, optional) – Key into cross_section.scalar_values to overlay as a line.

  • layer_property_name (str, optional) – Key into cross_section.layer_properties to color-map layers.

  • layer_property_cmap (str, default "viridis") – Colormap used for layer property rendering.

  • show_ground_surface (bool, default True) – Draw the ground surface as a green line.

  • alpha (float, default 0.7) – Fill transparency.

  • title (str, optional) – Plot title.

  • figsize (tuple, default (14, 6)) – Figure size in inches.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_cross_section_location(grid, cross_section, ax=None, line_color='red', line_width=2.5, mesh_alpha=0.3, show_labels=True, figsize=(10, 8))[source]#

Plot a plan-view map showing the cross-section line on the mesh.

Parameters:
  • grid (AppGrid) – Model mesh.

  • cross_section (CrossSection) – Cross-section whose path will be drawn.

  • ax (Axes, optional) – Existing axes. Creates a new figure if None.

  • line_color (str, default "red") – Color of the cross-section line.

  • line_width (float, default 2.5) – Width of the cross-section line.

  • mesh_alpha (float, default 0.3) – Transparency of the mesh underlay.

  • show_labels (bool, default True) – Show A / A’ labels at line endpoints.

  • figsize (tuple, default (10, 8)) – Figure size in inches.

Returns:

(Figure, Axes) matplotlib objects.

Return type:

tuple

pyiwfm.visualization.plotting.plot_one_to_one(observed, simulated, ax=None, color_by=None, show_metrics=True, show_identity=True, show_regression=True, title=None, units='', figsize=(8, 8))[source]#

Plot a 1:1 comparison of observed vs simulated values.

Parameters:
  • observed (NDArray[np.float64]) – Observed values.

  • simulated (NDArray[np.float64]) – Simulated values.

  • ax (Axes | None) – Existing axes to plot on.

  • color_by (NDArray[np.float64] | None) – Optional array to color scatter points by.

  • show_metrics (bool) – Show a text box with RMSE, NSE, etc.

  • show_identity (bool) – Show the 1:1 identity line.

  • show_regression (bool) – Show a linear regression line.

  • title (str | None) – Plot title.

  • units (str) – Unit label for axes.

  • figsize (tuple[float, float]) – Figure size.

Returns:

The figure and axes.

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_residual_cdf(residuals, ax=None, show_percentile_lines=True, title='Cumulative Frequency of Residuals', figsize=(8, 6))[source]#

Plot an empirical CDF (cumulative frequency) of residuals.

Parameters:
  • residuals (NDArray[np.float64]) – Array of residual values (simulated - observed).

  • ax (Axes | None) – Existing axes to plot on.

  • show_percentile_lines (bool, default True) – Draw reference lines at the 5th and 95th percentiles and a vertical line at zero.

  • title (str, default "Cumulative Frequency of Residuals") – Plot title.

  • figsize (tuple[float, float], default (8, 6)) – Figure size in inches.

Returns:

The figure and axes.

Return type:

tuple[Figure, Axes]

pyiwfm.visualization.plotting.plot_spatial_bias(grid, x, y, bias, ax=None, show_mesh=True, cmap='RdBu_r', symmetric_colorbar=True, title='Spatial Bias', units='', figsize=(10, 8))[source]#

Plot spatial bias (simulated - observed) at observation locations.

Parameters:
  • grid (AppGrid) – Model grid for background mesh.

  • x (NDArray[np.float64]) – X coordinates of observation points.

  • y (NDArray[np.float64]) – Y coordinates of observation points.

  • bias (NDArray[np.float64]) – Bias values (simulated - observed).

  • ax (Axes | None) – Existing axes to plot on.

  • show_mesh (bool) – Show the mesh as background.

  • cmap (str) – Colormap name (should be diverging).

  • symmetric_colorbar (bool) – Center the colorbar at 0.

  • title (str) – Plot title.

  • units (str) – Unit label for colorbar.

  • figsize (tuple[float, float]) – Figure size.

Returns:

The figure and axes.

Return type:

tuple[Figure, Axes]

Interactive Web Viewer#

The web viewer is a FastAPI backend + React SPA frontend with four tabs: Overview, 3D Mesh (vtk.js), Results Map (deck.gl + MapLibre), and Budgets (Plotly).

Configuration#

The ModelState singleton that holds the loaded IWFMModel and provides lazy getters for head data, budget data, stream reach boundaries, and coordinate reprojection.

Configuration settings for the FastAPI web viewer.

class pyiwfm.visualization.webapi.config.ViewerSettings(*args, **kwargs)[source]#

Bases: BaseModel

Settings for the web viewer.

model_config = {'extra': 'forbid'}#
class pyiwfm.visualization.webapi.config.ModelState[source]#

Bases: MeshStateMixin, ResultsStateMixin, BudgetStateMixin, CacheStateMixin

Holds the loaded model and derived data for the API.

This is a singleton-like container that holds the model and precomputed meshes for efficient API responses.

MAX_OBSERVATIONS = 100#
__init__()[source]#
property model: IWFMModel | None#

Get the loaded model.

property is_loaded: bool#

Check if a model is loaded.

set_model(model, crs='+proj=utm +zone=10 +datum=NAD83 +units=us-ft +no_defs', no_cache=False, rebuild_cache=False)[source]#

Set the model and reset caches.

Thread-safe: acquires lock to prevent concurrent requests from seeing partially reset state.

add_observation(obs_id, data)[source]#

Store an uploaded observation dataset.

Raises:

ValueError – If the maximum number of observations has been reached.

get_observation(obs_id)[source]#

Get observation data by ID.

list_observations()[source]#

List all uploaded observations.

delete_observation(obs_id)[source]#

Delete an observation by ID.

pyiwfm.visualization.webapi.config.require_model()[source]#

Return the loaded model or raise HTTPException(404).

Use this in route handlers to narrow model_state.model from IWFMModel | None to IWFMModel, satisfying mypy while also giving a clean API error when no model is loaded.

Server#

FastAPI application creation, CRS configuration, and static file serving.

FastAPI server for the IWFM web viewer.

pyiwfm.visualization.webapi.server.create_app(model=None, settings=None)[source]#

Create the FastAPI application.

Parameters:
  • model (IWFMModel, optional) – Pre-loaded IWFM model

  • settings (ViewerSettings, optional) – Viewer settings

Returns:

Configured FastAPI application

Return type:

FastAPI

pyiwfm.visualization.webapi.server.launch_viewer(model, host='127.0.0.1', port=8080, title='IWFM Viewer', open_browser=True, debug=False, crs='+proj=utm +zone=10 +datum=NAD83 +units=us-ft +no_defs', no_cache=False, rebuild_cache=False, observation_paths=None)[source]#

Launch the web viewer server.

Parameters:
  • model (IWFMModel) – The model to visualize

  • host (str) – Server host address

  • port (int) – Server port

  • title (str) – Application title

  • open_browser (bool) – Whether to open browser on start

  • debug (bool) – Enable debug mode

  • crs (str) – Source coordinate reference system (default: UTM Zone 10N, NAD83, US survey feet)

  • no_cache (bool) – Disable SQLite cache layer

  • rebuild_cache (bool) – Force rebuild of SQLite cache

  • observation_paths (list[Path], optional) – Observation files (.smp, .csv) or directories to load at startup

Head Data Loader#

Moved to pyiwfm.io.head_loader. See I/O Modules for full docs.

Hydrograph Reader#

Moved to pyiwfm.io.hydrograph_reader. See I/O Modules for full docs.

Properties#

Property extraction and caching for the web viewer.

Property visualization component for IWFM web visualization.

This module provides the PropertyVisualizer class for managing the display of aquifer properties on the 3D mesh.

class pyiwfm.visualization.webapi.properties.PropertyVisualizer(mesh, stratigraphy=None, aquifer_params=None)[source]#

Bases: object

Manages visualization of aquifer properties on the mesh.

This class handles the selection and display of different aquifer properties (Kh, Kv, Ss, Sy, head) on the 3D mesh visualization. It manages colormaps, value ranges, and layer filtering.

Parameters:
  • mesh (pv.UnstructuredGrid) – The PyVista mesh to add properties to.

  • stratigraphy (Stratigraphy, optional) – Model stratigraphy for computing layer-based properties.

  • aquifer_params (object, optional) – Aquifer parameters object containing Kh, Kv, Ss, Sy values.

Variables:
  • mesh (pv.UnstructuredGrid) – The mesh being visualized.

  • active_property (str) – Currently displayed property name.

  • active_layer (int) – Currently displayed layer (1-indexed, 0 for all layers).

  • colormap (str) – Current colormap name.

DEFAULT_COLORMAPS = {'bottom_elev': 'terrain', 'head': 'coolwarm', 'kh': 'turbo', 'kv': 'turbo', 'layer': 'viridis', 'ss': 'plasma', 'sy': 'plasma', 'thickness': 'viridis', 'top_elev': 'terrain'}#
__init__(mesh, stratigraphy=None, aquifer_params=None)[source]#

Initialize the property visualizer.

property available_properties: list[str]#

Get list of available property names.

property active_property: str#

Get the currently active property.

property active_layer: int#

Get the currently active layer (0 = all layers).

property colormap: str#

Get the current colormap.

property active_scalars: ndarray[tuple[Any, ...], dtype[float64]] | None#

Get the scalar array for the currently active property.

property value_range: tuple[float, float]#

Get the current value range (min, max).

set_active_property(name)[source]#

Set the displayed property.

Parameters:

name (str) – Property name: ‘layer’, ‘kh’, ‘kv’, ‘ss’, ‘sy’, ‘head’, ‘thickness’, ‘top_elev’, ‘bottom_elev’.

Raises:

ValueError – If the property name is not recognized or not available.

set_layer(layer)[source]#

Set the active layer for display.

Parameters:

layer (int) – Layer number (1-indexed). Use 0 to show all layers.

set_colormap(cmap)[source]#

Set the colormap for property display.

Parameters:

cmap (str) – Matplotlib colormap name (e.g., ‘viridis’, ‘coolwarm’, ‘jet’).

set_range(vmin, vmax)[source]#

Set the value range for the colormap.

Parameters:
  • vmin (float) – Minimum value.

  • vmax (float) – Maximum value.

set_auto_range(auto=True)[source]#

Enable or disable automatic range calculation.

Parameters:

auto (bool) – If True, automatically calculate range from data.

get_property_array(name, layer=0)[source]#

Get the scalar array for a property.

Parameters:
  • name (str) – Property name.

  • layer (int) – Layer number (0 for all layers).

Returns:

Scalar array for the property, or None if not available.

Return type:

NDArray or None

add_head_data(head, layer=None)[source]#

Add head data to the mesh.

Parameters:
  • head (NDArray) – Head values. Shape should be (n_nodes,) for single layer or (n_nodes, n_layers) for all layers.

  • layer (int, optional) – If provided, the layer for 1D head data.

get_property_info(name=None)[source]#

Get metadata about a property.

Parameters:

name (str, optional) – Property name. If None, returns info for active property.

Returns:

Property metadata including name, units, description, recommended colormap, and log scale flag.

Return type:

dict

get_colorbar_settings()[source]#

Get settings for the colorbar display.

Returns:

Colorbar settings including title, units, range, colormap.

Return type:

dict

clear_cache()[source]#

Clear the property cache.

Slicing#

Cross-section slice plane computation.

Slicing controller for IWFM web visualization.

This module provides the SlicingController class for interactive slicing of 3D meshes along various planes.

class pyiwfm.visualization.webapi.slicing.SlicingController(mesh)[source]#

Bases: object

Controls for slicing the 3D model along planes.

This class provides methods for creating slices through the model mesh along axis-aligned planes, arbitrary angles, and custom cross-sections defined by two points on the map.

Parameters:

mesh (pv.UnstructuredGrid) – The PyVista mesh to slice.

Variables:
  • mesh (pv.UnstructuredGrid) – The mesh being sliced.

  • bounds (tuple) – Mesh bounding box (xmin, xmax, ymin, ymax, zmin, zmax).

Examples

>>> slicer = SlicingController(mesh)
>>> slice_x = slicer.slice_x(position=1000.0)
>>> slice_z = slicer.slice_z(position=-50.0)
__init__(mesh)[source]#

Initialize the slicing controller.

property bounds: tuple[float, float, float, float, float, float]#

Get mesh bounds (xmin, xmax, ymin, ymax, zmin, zmax).

property x_range: tuple[float, float]#

Get X coordinate range.

property y_range: tuple[float, float]#

Get Y coordinate range.

property z_range: tuple[float, float]#

Get Z coordinate range.

property center: tuple[float, float, float]#

Get mesh center point.

slice_x(position)[source]#

Create a slice perpendicular to the X axis (YZ plane).

Parameters:

position (float) – X coordinate for the slice plane.

Returns:

The slice result as a surface mesh.

Return type:

pv.PolyData

Examples

>>> slice_mesh = slicer.slice_x(position=1500.0)
>>> slice_mesh.plot()
slice_y(position)[source]#

Create a slice perpendicular to the Y axis (XZ plane).

Parameters:

position (float) – Y coordinate for the slice plane.

Returns:

The slice result as a surface mesh.

Return type:

pv.PolyData

Examples

>>> slice_mesh = slicer.slice_y(position=2000.0)
>>> slice_mesh.plot()
slice_z(position)[source]#

Create a horizontal slice at a specific elevation (XY plane).

Parameters:

position (float) – Z (elevation) coordinate for the slice plane.

Returns:

The slice result as a surface mesh.

Return type:

pv.PolyData

Examples

>>> # Slice at -100 ft elevation
>>> slice_mesh = slicer.slice_z(position=-100.0)
>>> slice_mesh.plot()
slice_arbitrary(normal, origin=None)[source]#

Create a slice along an arbitrary plane.

Parameters:
  • normal (tuple[float, float, float]) – Normal vector of the slice plane.

  • origin (tuple[float, float, float], optional) – Origin point of the plane. Default is mesh center.

Returns:

The slice result as a surface mesh.

Return type:

pv.PolyData

Examples

>>> # Slice at 45 degrees in XZ plane
>>> slice_mesh = slicer.slice_arbitrary(
...     normal=(0.707, 0, 0.707),
...     origin=(1000, 2000, 0),
... )
create_cross_section(start, end, n_samples=100)[source]#

Create a vertical cross-section between two map points.

This method creates a vertical slice through the model along a line drawn on the map (2D view). The resulting cross-section shows the full depth of the model along that transect.

Parameters:
  • start (tuple[float, float]) – Starting point (x, y) on the map.

  • end (tuple[float, float]) – Ending point (x, y) on the map.

  • n_samples (int, optional) – Number of sample points along the transect line. Default is 100.

Returns:

The cross-section as a surface mesh.

Return type:

pv.PolyData

Examples

>>> # Create cross-section from point A to point B
>>> cross_section = slicer.create_cross_section(
...     start=(1000, 2000),
...     end=(5000, 3000),
... )
>>> cross_section.plot()

Notes

The cross-section is computed by creating a vertical slice plane that passes through both start and end points. The plane extends from the top to the bottom of the mesh.

slice_along_polyline(points, resolution=100)[source]#

Create a vertical cross-section along a polyline path.

Parameters:
  • points (list[tuple[float, float]]) – List of (x, y) points defining the polyline path.

  • resolution (int, optional) – Number of sample points per segment. Default is 100.

Returns:

Combined cross-section mesh.

Return type:

pv.PolyData

Examples

>>> # Create cross-section along a stream path
>>> path = [(1000, 2000), (2000, 2500), (3000, 2000)]
>>> cross_section = slicer.slice_along_polyline(path)
slice_box(bounds=None, invert=False)[source]#

Extract a box region from the mesh.

Parameters:
  • bounds (tuple[float, float, float, float, float, float], optional) – Box bounds (xmin, xmax, ymin, ymax, zmin, zmax). Default is center third of the mesh.

  • invert (bool, optional) – If True, extract everything outside the box instead.

Returns:

Extracted mesh region.

Return type:

pv.UnstructuredGrid

slice_multiple_z(positions=None, n_slices=5)[source]#

Create multiple horizontal slices at specified elevations.

Parameters:
  • positions (list[float], optional) – Z (elevation) coordinates for slice planes. If None, evenly space n_slices through the mesh.

  • n_slices (int, optional) – Number of slices if positions not specified. Default is 5.

Returns:

List of slice meshes.

Return type:

list[pv.PolyData]

Examples

>>> # Get 5 evenly spaced horizontal slices
>>> slices = slicer.slice_multiple_z(n_slices=5)
>>> for i, s in enumerate(slices):
...     s.save(f"slice_{i}.vtk")
get_slice_properties(slice_mesh)[source]#

Get properties of a slice result.

Parameters:

slice_mesh (pv.PolyData) – A slice mesh.

Returns:

Dictionary with slice properties: - n_cells: Number of cells in the slice - n_points: Number of points in the slice - area: Total area of the slice - bounds: Bounding box of the slice - arrays: Available data arrays

Return type:

dict

normalized_to_position_along(normal, normalized)[source]#

Convert a 0-1 normalized position to world coordinates along an arbitrary normal.

Projects the mesh bounding box onto the given normal direction and interpolates between the minimum and maximum projections.

Parameters:
  • normal (tuple[float, float, float]) – The normal direction to project along.

  • normalized (float) – Normalized position (0-1) along the normal direction.

Returns:

World-space origin point for a slice at the given position.

Return type:

tuple[float, float, float]

clear_cache()[source]#

Clear the slice cache.

position_to_normalized(axis, position)[source]#

Convert absolute position to normalized (0-1) range.

Parameters:
  • axis (str) – Axis name (‘x’, ‘y’, or ‘z’).

  • position (float) – Absolute position value.

Returns:

Normalized position (0-1).

Return type:

float

normalized_to_position(axis, normalized)[source]#

Convert normalized (0-1) position to absolute value.

Parameters:
  • axis (str) – Axis name (‘x’, ‘y’, or ‘z’).

  • normalized (float) – Normalized position (0-1).

Returns:

Absolute position value.

Return type:

float

API Routes#

The web viewer backend exposes a REST API under /api/. Key route groups:

  • Model (/api/model): Load model info, compare with a second model

  • Mesh (/api/mesh): GeoJSON mesh, element details, node lookups

  • Results (/api/results): Head values, drawdown with pagination (offset/limit/skip), head statistics (min/max/mean/std across timesteps), subsidence surface

  • Diagnostics (/api/diagnostics): Simulation messages, convergence tracking, mass balance errors, spatial summary

  • Budgets (/api/budgets): Water budget time series by type and location

  • Export (/api/export): CSV downloads (heads, budgets, hydrographs), GeoJSON mesh, GeoPackage (multi-layer), matplotlib plot generation (PNG/SVG), and HTML/JSON diagnostic reports

  • Observations (/api/observations): Upload observed data for hydrograph comparison

  • Groundwater, Streams, Lakes, Root Zone, Small Watersheds, Unsaturated Zone (/api/groundwater, /api/streams, /api/small-watersheds, /api/unsaturated-zone, etc.): Component-specific endpoints

Diagnostics Routes#

Endpoints for simulation diagnostic analysis including convergence tracking, mass balance error timeseries, and spatial message summaries.

Diagnostics API routes for simulation messages, convergence, and mass balance.

pyiwfm.visualization.webapi.routes.diagnostics.get_messages(severity=fastapi.Query, limit=fastapi.Query, offset=fastapi.Query)#

Parse and return simulation messages with optional severity filtering.

pyiwfm.visualization.webapi.routes.diagnostics.get_convergence()#

Return convergence iteration data from simulation messages.

pyiwfm.visualization.webapi.routes.diagnostics.get_convergence_hotspots()#

Return convergence hotspot variables with spatial coordinates.

pyiwfm.visualization.webapi.routes.diagnostics.get_hotspot_context(variable=fastapi.Query, timestep_index=fastapi.Query)#

Return spatial context and iteration history for a convergence hotspot.

pyiwfm.visualization.webapi.routes.diagnostics.get_element_budget(element_id=fastapi.Query, timestep_index=fastapi.Query, layer=fastapi.Query)#

Fetch GW ZBudget data for a single element.

pyiwfm.visualization.webapi.routes.diagnostics.get_stream_node_budget(stream_node_id=fastapi.Query, timestep_index=fastapi.Query)#

Fetch stream node budget data for a specific stream node.

pyiwfm.visualization.webapi.routes.diagnostics.get_mass_balance(component=fastapi.Query)#

Return mass balance error timeseries from simulation messages.

pyiwfm.visualization.webapi.routes.diagnostics.get_summary()#

Combined diagnostics summary.

pyiwfm.visualization.webapi.routes.diagnostics.get_spatial_summary()#

Spatial summary of message counts for map overlay.

Unsaturated Zone Routes#

Endpoints for unsaturated zone component information and parameter summaries.

Unsaturated zone API routes.

pyiwfm.visualization.webapi.routes.unsaturated_zone.get_unsaturated_zone()#

Get unsaturated zone component information.

pyiwfm.visualization.webapi.routes.unsaturated_zone.get_unsaturated_zone_summary()#

Get summary of unsaturated zone parameters.