parq_blockmodel.blockmodel.ParquetBlockModel#

class parq_blockmodel.blockmodel.ParquetBlockModel(blockmodel_path, name=None, geometry=None)[source]#

A class to represent a regular Parquet block model.

Canonical on-disk representation:

We treat .pbm files as ParquetBlockModel containers:

The geometry is ijk-first: logical indices (i, j, k) enumerate the dense grid, and centroid coordinates (x, y, z) are derived from those indices plus geometry (corner, block size, axis orientation). In non-rotated models the grid axes align with the global X, Y, Z directions; in rotated models they do not.

Parquet storage order follows NumPy C-order with respect to ijk: i varies fastest, then j, then k. This aligns with numpy.ravel(order="C") and how RegularGeometry computes row indices. For xyz-defined, non-rotated models this corresponds to an increasing lexicographic ordering in (x, y, z), but callers should treat ijk + geometry as the canonical view.

blockmodel_path#

Path to the canonical .pbm file backing this block model.

Type:

Path

name#

The name of the block model, derived from the file name.

Type:

str

geometry#

Geometry of the block model, loaded from Parquet metadata or inferred from centroid columns.

Type:

RegularGeometry

__init__(blockmodel_path, name=None, geometry=None)[source]#

Methods

__init__(blockmodel_path[, name, geometry])

create_demo_block_model(filename, **demo_kwargs)

Convenience helper used in tests to write a demo Parquet file and wrap it as a ParquetBlockModel.

create_heatmap_from_threshold(attribute, ...)

Create a 2D heatmap from a 3D block model at a specified attribute threshold.

create_report([columns, column_batch_size, ...])

Create a ydata-profiling report for the block model.

create_toy_blockmodel(filename[, shape, ...])

Create a synthetic toy block model.

downsample(new_block_size, aggregation_config)

Downsample the block model to a coarser grid with specified aggregation methods for each attribute.

from_dataframe(dataframe, filename[, ...])

Create a ParquetBlockModel from a pandas DataFrame.

from_geometry(geometry, path[, name])

Create a ParquetBlockModel from a geometry only.

from_parquet(parquet_path[, columns, ...])

Create a ParquetBlockModel from a source Parquet file.

plot(scalar[, grid_type, frame, threshold, ...])

Plot the block model using PyVista.

plot_heatmap(attribute, threshold[, axis, title])

Create a 2D heatmap plotly figure from a 3D block model at a specified attribute threshold.

read([columns, index, dense])

Read the Parquet file and return a DataFrame.

to_dense_parquet(filepath[, chunk_size, ...])

Export the block model as a dense xyz-indexed Parquet file.

to_glb(output_path[, attributes, ...])

Export the block model as a GLB (glTF 2.0 binary) mesh.

to_ply(output_path[, attributes, ...])

Export the block model as a PLY (Polygon File Format) mesh.

to_pyvista([grid_type, attributes, frame])

triangulate([attributes, surface_only, sparse])

Generate a triangulated mesh from the block model.

upsample(new_block_size, interpolation_config)

Upsample the block model to a finer grid with specified interpolation methods for each attribute.

validate_sparse()

validate_xyz_parquet(parquet_path[, ...])

Validate xyz-defined Parquet input and return inferred geometry.

Attributes

POSITION_COLUMNS

centroid_index

Get the centroid index of the block model as (x, y, z).

column_categorical_ordered

corner

Backward-compatible access to the local grid corner.

index_c

Zero-based C-order indices for the dense ijk grid.

index_f

Zero-based Fortran-order indices for the dense ijk grid.

is_sparse

origin

Backward-compatible access to the world-frame origin.

sparsity

property centroid_index: MultiIndex#

Get the centroid index of the block model as (x, y, z).

This index is built from the centroid columns x, y, z stored in the underlying .pbm Parquet file. It is primarily a backwards-compatibility view for xyz-first workflows which treat world-space centroids as the primary index.

Canonical ordering is defined by the C-order ijk layout in RegularGeometry, not by lexicographic sorting of (x, y, z). We therefore require this index to be unique but do not enforce global monotonicity in xyz.

Returns:

The MultiIndex representing centroid coordinates (x, y, z).

Return type:

pd.MultiIndex

property corner: tuple[float, float, float] | list[float]#

Backward-compatible access to the local grid corner.

classmethod create_demo_block_model(filename, **demo_kwargs)[source]#

Convenience helper used in tests to write a demo Parquet file and wrap it as a ParquetBlockModel.

Parameters:
  • filename (Path) – Target .parquet file to hold the raw demo data. The canonical blockmodel file will be written alongside it with a .pbm suffix via from_parquet().

  • **demo_kwargs – Additional keyword arguments forwarded to parq_blockmodel.utils.demo_block_model.create_demo_blockmodel(), for example shape or block_size. This allows tests and examples to control the logical grid used for the demo model.

Return type:

ParquetBlockModel

create_heatmap_from_threshold(attribute, threshold, axis='z', return_array=False)[source]#

Create a 2D heatmap from a 3D block model at a specified attribute threshold.

Parameters:
  • attribute (str) – The name of the attribute to threshold.

  • threshold (float) – The threshold value for the attribute.

  • axis (str) – The axis to view from (‘x’, ‘y’, or ‘z’). Defaults to ‘z’.

  • return_array (bool) – If True, returns the heatmap as a NumPy array. Defaults to False.

Returns:

A 2D heatmap as a PyVista ImageData object or a NumPy array.

Return type:

Union[pv.ImageData, np.ndarray]

create_report(columns=None, column_batch_size=10, show_progress=True, open_in_browser=False)[source]#

Create a ydata-profiling report for the block model. The report will be of the same name as the block model, with a ‘.html’ extension.

Parameters:
  • columns (Optional[list[str]]) – List of column names to include in the profile. If None, all columns are used.

  • column_batch_size (int) – The number of columns to process in each batch. If None, processes all columns at once.

  • show_progress (bool) – bool: If True, displays a progress bar during profiling.

  • open_in_browser (bool) – bool: If True, opens the report in a web browser after generation.

Return type:

Path

Returns

Path: The path to the generated profile report.

classmethod create_toy_blockmodel(filename, shape=(3, 3, 3), block_size=(1, 1, 1), corner=(-0.5, -0.5, -0.5), axis_azimuth=0.0, axis_dip=0.0, axis_plunge=0.0, deposit_bearing=20.0, deposit_dip=30.0, deposit_plunge=10.0, grade_name='grade', grade_min=50.0, grade_max=65.0, deposit_center=(10.0, 7.5, 5.0), deposit_radii=(8.0, 5.0, 3.0), noise_std=0.0, noise_rel=None, noise_seed=None)[source]#

Create a synthetic toy block model.

The toy model is generated on a regular ijk grid with the specified shape and block_size, anchored at corner in world coordinates. A simple ellipsoidal grade distribution is created in xyz space and stored under grade_name alongside any supporting attributes.

Parameters:
  • filename (Path) – Path to the source .parquet file to hold the raw toy data. A canonical .pbm file will be written alongside it with the same stem.

  • shape (tuple of int, default (3, 3, 3)) – Number of blocks along the logical ijk axes.

  • block_size (tuple of float, default (1, 1, 1)) – Block dimensions along each logical axis.

  • corner (tuple of float, default (-0.5, -0.5, -0.5)) – World‑space coordinates of the block with indices (i=0, j=0, k=0) lower corner.

  • axis_azimuth (float, default 0.0) – Rotation angles defining orientation of the logical ijk axes relative to the world xyz frame. These are converted to an orthonormal basis (axis_u, axis_v, axis_w) used by RegularGeometry.

  • axis_dip (float, default 0.0) – Rotation angles defining orientation of the logical ijk axes relative to the world xyz frame. These are converted to an orthonormal basis (axis_u, axis_v, axis_w) used by RegularGeometry.

  • axis_plunge (float, default 0.0) – Rotation angles defining orientation of the logical ijk axes relative to the world xyz frame. These are converted to an orthonormal basis (axis_u, axis_v, axis_w) used by RegularGeometry.

  • deposit_bearing (float) – Orientation of the synthetic deposit in degrees.

  • deposit_dip (float) – Orientation of the synthetic deposit in degrees.

  • deposit_plunge (float) – Orientation of the synthetic deposit in degrees.

  • grade_name (str, default "grade") – Name of the column storing the simulated grade values.

  • grade_min (float) – Minimum and maximum grade values for the toy distribution.

  • grade_max (float) – Minimum and maximum grade values for the toy distribution.

  • deposit_center (tuple of float) – Center of the ellipsoidal deposit in world xyz coordinates.

  • deposit_radii (tuple of float) – Semi‑axes of the deposit ellipsoid in world units.

  • noise_std (float, default 0.0) – Absolute standard deviation of Gaussian noise added to grades.

  • noise_rel (float, optional) – Relative standard deviation expressed as a fraction of (grade_max - grade_min). Mutually exclusive with noise_std.

  • noise_seed (int, optional) – Seed used for reproducible Gaussian noise.

Returns:

A ParquetBlockModel backed by a canonical .pbm file with the generated toy data and geometry metadata.

Return type:

ParquetBlockModel

Notes

For rotated geometries (non‑zero rotation angles), xyz centroids will appear rotated in world coordinates relative to the ijk axes of the grid. Geometry validation is skipped in this case to avoid imposing axis‑aligned assumptions on the rotated layout.

downsample(new_block_size, aggregation_config)[source]#

Downsample the block model to a coarser grid with specified aggregation methods for each attribute. This function supports downsampling of both categorical and numeric attributes. :type new_block_size: :param new_block_size: tuple of floats (dx, dy, dz) for the new block size. :type aggregation_config: :param aggregation_config: dict mapping attribute names to aggregation methods.

Example

aggregation_config = {

‘grade’: {‘method’: ‘weighted_mean’, ‘weight’: ‘dry_mass’}, ‘density’: {‘method’: ‘weighted_mean’, ‘weight’: ‘volume’}, ‘dry_mass’: {‘method’: ‘sum’}, ‘volume’: {‘method’: ‘sum’}, ‘rock_type’: {‘method’: ‘mode’}

}

Returns:

A new ParquetBlockModel instance with the downsampled grid.

Return type:

ParquetBlockModel

classmethod from_dataframe(dataframe, filename, geometry=None, name=None, overwrite=False, axis_azimuth=0.0, axis_dip=0.0, axis_plunge=0.0, chunk_size=1000000)[source]#

Create a ParquetBlockModel from a pandas DataFrame.

This constructor is oriented towards xyz-indexed DataFrames, where the index is a centroid MultiIndex with levels ("x", "y", "z") in world coordinates.

The DataFrame is written to a sibling .pbm file (replacing the .parquet suffix of filename) with embedded RegularGeometry metadata. Geometry is either supplied explicitly or inferred from the xyz centroids via RegularGeometry.from_multi_index().

Parameters:
  • dataframe (pandas.DataFrame) – Block model data indexed by centroid coordinates with dataframe.index.names == ["x", "y", "z"].

  • filename (Path) – Path to the source .parquet file which will be used as the basis for the sibling .pbm container.

  • geometry (RegularGeometry, optional) – Geometry of the logical ijk grid. If omitted, it is inferred from the centroid MultiIndex, using the optional rotation angles to define the ijk axis orientation.

  • name (str, optional) – Optional name for the resulting ParquetBlockModel. Defaults to filename.stem.

  • overwrite (bool, default False) – If True, allows overwriting an existing .pbm file at the derived path.

  • axis_azimuth (float, default 0.0) – Optional rotation angle used when inferring geometry from the xyz centroids. Defines the orientation of the logical ijk axes relative to the world coordinate frame.

  • axis_dip (float, default 0.0) – Optional rotation angle. See axis_azimuth.

  • axis_plunge (float, default 0.0) – Optional rotation angle. See axis_azimuth.

  • chunk_size (int, default 1_000_000) – Batch size for writing Parquet data.

Returns:

A block model backed by the newly written .pbm file.

Return type:

ParquetBlockModel

Note

New ijk-first workflows are encouraged to construct a RegularGeometry explicitly and use ParquetBlockModel.from_geometry() or ParquetBlockModel.from_parquet(). This helper remains for backwards compatibility with xyz-indexed DataFrames.

classmethod from_geometry(geometry, path, name=None)[source]#

Create a ParquetBlockModel from a geometry only.

This constructor materialises a dense ijk grid defined by a RegularGeometry into a Parquet file with xyz centroid columns but no additional attributes. It is useful for creating an empty block model skeleton that can be joined with external attribute data.

Parameters:
  • geometry (RegularGeometry) – Geometry of the logical ijk grid.

  • path (Path) – Path to the target .pbm file. The file will contain a row for every ijk block with derived xyz centroid columns and embedded geometry metadata.

  • name (str, optional) – Optional name for the resulting ParquetBlockModel.

Returns:

A block model with geometry defined but no attribute columns.

Return type:

ParquetBlockModel

classmethod from_parquet(parquet_path, columns=None, overwrite=False, axis_azimuth=0.0, axis_dip=0.0, axis_plunge=0.0, chunk_size=1000000)[source]#

Create a ParquetBlockModel from a source Parquet file.

This helper promotes a source .parquet file (typically xyz-centric) into a canonical .pbm container with embedded RegularGeometry metadata.

The input Parquet is expected to contain centroid columns x, y, z describing block centroids in world coordinates. RegularGeometry is reconstructed from those centroids and/or any existing "parq-blockmodel" metadata via RegularGeometry.from_parquet(). Geometry becomes the authoritative description of the dense ijk grid; xyz centroids are treated as a derived view.

Parameters:
  • parquet_path (Path) – Path to the source Parquet file. If the suffix is .pbm and overwrite is False, a ValueError is raised to avoid mutating an existing canonical container.

  • columns (list[str], optional) – Optional subset of columns to copy from the source file into the resulting .pbm file. If None, all columns are copied.

  • overwrite (bool, default False) – If True, allows overwriting an existing .pbm file at the target location.

  • axis_azimuth (float, default 0.0) – Optional rotation angle used by RegularGeometry.from_parquet() to define the orientation of the logical ijk axes in world coordinates when inferring geometry. In the common case geometry is read directly from existing metadata and this parameter is 0.

  • axis_dip (float, default 0.0) – Optional rotation angle. See axis_azimuth.

  • axis_plunge (float, default 0.0) – Optional rotation angle. See axis_azimuth.

  • chunk_size (int, default 1_000_000) – Batch size for reading Parquet data.

Returns:

A block model backed by a newly written .pbm file

located alongside parquet_path.

Return type:

ParquetBlockModel

property index_c: ndarray#

Zero-based C-order indices for the dense ijk grid.

The returned array has length ni * nj * nk and corresponds to linearised logical indices (i, j, k) using NumPy C‑order:

r = i + ni * (j + nj * k).

This is mainly a low‑level helper for downstream APIs that need a stable mapping between a flat row index and ijk; most callers should use read() with index="ijk" instead.

property index_f: ndarray#

Zero-based Fortran-order indices for the dense ijk grid.

Uses the same ijk logical grid as :pyattr:`index_c`, but flattened with order="F". This is useful when interacting with tools (such as some VTK/PyVista paths) that expect F‑order layouts.

property origin: tuple[float, float, float] | list[float]#

Backward-compatible access to the world-frame origin.

plot(scalar, grid_type='image', frame='world', threshold=True, show_edges=True, show_axes=True, enable_picking=False, picked_attributes=None)[source]#

Plot the block model using PyVista.

Parameters:
  • scalar (str) – The name of the scalar attribute to visualize.

  • grid_type (Literal['image', 'structured', 'unstructured']) – The type of grid to use for plotting. Options are “image”, “structured”, or “unstructured”.

  • frame (Literal['world', 'local']) – Coordinate frame for image grids. "world" applies geometry axis vectors; "local" uses axis-aligned local ijk orientation. Only supported for grid_type="image".

  • threshold (bool) – The thresholding option for the mesh. If True, applies a threshold to the scalar values.

  • show_edges (bool) – Show edges of the mesh.

  • show_axes (bool) – Show the axes in the plot.

  • enable_picking (bool) – If True, enables picking mode to interactively select cells in the plot.

  • picked_attributes (Optional[list[str]]) – A list of attributes that will be returned in picking mode. If None, all attributes are returned.

Return type:

Plotter

Returns:

plot_heatmap(attribute, threshold, axis='z', title=None)[source]#

Create a 2D heatmap plotly figure from a 3D block model at a specified attribute threshold.

Parameters:
  • attribute (str) – The name of the attribute to threshold.

  • threshold (float) – The threshold value for the attribute.

  • axis (str) – The axis to view from (‘x’, ‘y’, or ‘z’). Defaults to ‘z’.

  • title (Optional[str]) – The title of the heatmap. If None, a default title is used.

Returns:

A Plotly figure containing the heatmap.

Return type:

go.Figure

read(columns=None, index='xyz', dense=False)[source]#

Read the Parquet file and return a DataFrame.

Notes

Current behaviour (for backwards compatibility):

  • index="xyz" (the default) returns a DataFrame indexed by centroid coordinates (x, y, z). This matches the original design where xyz are treated as canonical.

  • index="ijk" returns a DataFrame whose index is derived from RegularGeometry via to_ijk_multi_index.

Option B design (future change, not yet enforced):

  • The canonical in-memory representation will become an (i, j, k) MultiIndex with attributes-only columns.

  • xyz (centroid) coordinates will be treated as a derived view computed from (i, j, k) + geometry, exposed via a helper such as as_xyz() or via pandas accessors that read geometry from df.attrs["parq-blockmodel"].

  • Plotting helpers (PyVista, Plotly) will consume ijk + geometry and compute xyz internally when needed, so they do not rely on xyz being persisted as data columns.

Until that refactor is complete, this method preserves the existing xyz-first semantics so that external callers continue to work.

Parameters:
  • columns (Optional[list[str]]) – List of column names to read. If None, all columns are read.

  • index (Literal['xyz', 'ijk', None]) – The index type to use for the DataFrame. Options are "xyz" for centroid coordinates, "ijk" for block indices, or None for no index.

  • dense (bool) – If True, reads/reindexes to the full dense grid. If False, returns the sparse layout in the underlying file.

Returns:

The DataFrame containing the block model data.

Return type:

pd.DataFrame

Notes

index="xyz" is preserved as the default for backwards compatibility with earlier versions of parq_blockmodel that treated centroid coordinates as canonical. New code is encouraged to pass index="ijk" explicitly and work in terms of logical grid indices plus geometry.

to_dense_parquet(filepath, chunk_size=100000, show_progress=False)[source]#

Export the block model as a dense xyz-indexed Parquet file.

The underlying .pbm may be sparse with respect to the dense ijk grid encoded by geometry. This helper iterates over the on‑disk data in chunks, reindexes each chunk to the full dense centroid MultiIndex geometry.to_multi_index_xyz() and writes the result to filepath.

Parameters:
  • filepath (Path) – Target path for the exported Parquet file.

  • chunk_size (int, default 100_000) – Number of rows to process per chunk when reading from the backing .pbm file.

  • show_progress (bool, default False) – If True, display a progress bar while exporting.

Return type:

None

Notes

This export is primarily intended for interoperability with tools that expect a fully populated xyz grid. The canonical representation of the block model remains the .pbm file with embedded RegularGeometry metadata.

to_glb(output_path, attributes=None, texture_attribute=None, colormap='viridis', surface_only=True, sparse=None)[source]#

Export the block model as a GLB (glTF 2.0 binary) mesh.

GLB is a derived format for external visualization in 3D viewers. Optionally applies vertex colors based on a scalar attribute (e.g., grade, density). Geometry metadata is embedded in the glTF extras field for reference.

Parameters:
  • output_path (str or Path) – Target GLB file path.

  • attributes (list[str], optional) – Block attributes to include (stored in glTF extensions).

  • texture_attribute (str, optional) – Attribute name to use for vertex coloring (e.g., “grade”). Must be numeric. If None, uses default gray material.

  • colormap (str, default "viridis") – Matplotlib colormap name for mapping texture_attribute to colors. Examples: “viridis”, “plasma”, “coolwarm”, “Greys”.

  • surface_only (bool, default True) – If True, export only exterior surface faces.

  • sparse (bool, optional) – If True, export only existing blocks. If None, infer from model sparsity.

Returns:

The output file path (pathlib.Path).

Return type:

Path

Notes

GLB format is optimized for visualization and may lose some precision compared to PLY. For scientific workflows requiring lossless data preservation, prefer to_ply().

The texture_attribute is normalized to [0, 1] and mapped to the specified colormap. NaN values are rendered as transparent.

Examples

>>> pbm = ParquetBlockModel(Path("model.pbm"))
>>> pbm.to_glb("model.glb", texture_attribute="grade", colormap="viridis")
to_ply(output_path, attributes=None, surface_only=True, sparse=None, binary=False)[source]#

Export the block model as a PLY (Polygon File Format) mesh.

PLY is the canonical format for mesh storage, supporting lossless round-tripping of all geometry and attribute data. The output file includes: - Vertex coordinates in world space - Face connectivity (triangles) - Per-vertex and per-face attributes (grades, rock type, etc.) - Block logical indices (i, j, k) for traceability - Geometry metadata (corner, block_size, shape, axes, CRS)

Parameters:
  • output_path (str or Path) – Target PLY file path.

  • attributes (list[str], optional) – Block attributes to include. If None, only geometry is exported.

  • surface_only (bool, default True) – If True, export only exterior surface faces.

  • sparse (bool, optional) – If True, export only existing blocks. If None, infer from model sparsity.

  • binary (bool, default False) – If True, write binary PLY (smaller file, less readable). If False, write ASCII PLY (larger, human-readable).

Returns:

The output file path (pathlib.Path).

Return type:

Path

Notes

Units and coordinate system are inherited from geometry. For rotated geometries, world-space coordinates reflect the rotation.

Examples

>>> pbm = ParquetBlockModel(Path("model.pbm"))
>>> pbm.to_ply("model.ply", attributes=["grade", "density"])
triangulate(attributes=None, surface_only=True, sparse=None)[source]#

Generate a triangulated mesh from the block model.

Creates a triangle mesh representation of the block model geometry, optionally including block attributes (grades, rock types, etc.) as vertex or face attributes.

Parameters:
  • attributes (list[str], optional) – List of attribute columns to include in the mesh. If None, only geometry is included (no attributes). Attributes must be in self.attributes.

  • surface_only (bool, default True) – If True, include only exterior surface faces. If False, include all interior faces as well. Useful for sparse models.

  • sparse (bool, optional) – If True (or None and sparse model detected), include only blocks that exist in the data. If False, generate mesh for full dense grid.

Returns:

Triangle mesh with vertices, faces, and optional attributes.

Return type:

parq_blockmodel.mesh.TriangleMesh

Notes

The mesh uses right-handed coordinates in world space, with rotation applied via geometry axis vectors. Attributes are preserved as per-vertex or per-face properties. For sparse models, only surface faces are typically needed (surface_only=True).

Examples

>>> pbm = ParquetBlockModel(Path("model.pbm"))
>>> mesh = pbm.triangulate(attributes=["grade", "density"], surface_only=True)
>>> print(f"Mesh: {mesh.n_vertices} vertices, {mesh.n_faces} faces")
upsample(new_block_size, interpolation_config)[source]#

Upsample the block model to a finer grid with specified interpolation methods for each attribute. This function supports upsampling of both categorical and numeric attributes. :type new_block_size: :param new_block_size: tuple of floats (dx, dy, dz) for the new block size. :type interpolation_config: :param interpolation_config: dict mapping attribute names to interpolation methods.

Example

interpolation_config = {

‘grade’: {‘method’: ‘linear’}, ‘density’: {‘method’: ‘nearest’}, ‘dry_mass’: {‘method’: ‘linear’}, ‘volume’: {‘method’: ‘linear’}, ‘rock_type’: {‘method’: ‘nearest’}

}

Returns:

A new ParquetBlockModel instance with the upsampled grid.

Return type:

ParquetBlockModel

classmethod validate_xyz_parquet(parquet_path, axis_azimuth=0.0, axis_dip=0.0, axis_plunge=0.0, chunk_size=1000000, tol=1e-06)[source]#

Validate xyz-defined Parquet input and return inferred geometry.

Return type:

RegularGeometry