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
.pbmfiles as ParquetBlockModel containers:The extension must be
.pbm.The content is a Parquet table with embedded geometry metadata under the
"parq-blockmodel"key, describing a regularparq_blockmodel.geometry.RegularGeometrygrid.
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:
ivaries fastest, thenj, thenk. This aligns withnumpy.ravel(order="C")and howRegularGeometrycomputes 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
.pbmfile 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:
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
ParquetBlockModelfrom a pandas DataFrame.from_geometry(geometry, path[, name])Create a
ParquetBlockModelfrom a geometry only.from_parquet(parquet_path[, columns, ...])Create a
ParquetBlockModelfrom 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_COLUMNSGet the centroid index of the block model as
(x, y, z).column_categorical_orderedBackward-compatible access to the local grid corner.
Zero-based C-order indices for the dense ijk grid.
Zero-based Fortran-order indices for the dense ijk grid.
is_sparseBackward-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,zstored in the underlying.pbmParquet 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
.parquetfile to hold the raw demo data. The canonical blockmodel file will be written alongside it with a.pbmsuffix viafrom_parquet().**demo_kwargs – Additional keyword arguments forwarded to
parq_blockmodel.utils.demo_block_model.create_demo_blockmodel(), for exampleshapeorblock_size. This allows tests and examples to control the logical grid used for the demo model.
- Return type:
- 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
shapeandblock_size, anchored atcornerin world coordinates. A simple ellipsoidal grade distribution is created in xyz space and stored undergrade_namealongside any supporting attributes.- Parameters:
filename (Path) – Path to the source
.parquetfile to hold the raw toy data. A canonical.pbmfile 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 byRegularGeometry.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 byRegularGeometry.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 byRegularGeometry.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 withnoise_std.noise_seed (int, optional) – Seed used for reproducible Gaussian noise.
- Returns:
A
ParquetBlockModelbacked by a canonical.pbmfile with the generated toy data and geometry metadata.- Return type:
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:
- 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
ParquetBlockModelfrom 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
.pbmfile (replacing the.parquetsuffix offilename) with embeddedRegularGeometrymetadata. Geometry is either supplied explicitly or inferred from the xyz centroids viaRegularGeometry.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
.parquetfile which will be used as the basis for the sibling.pbmcontainer.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 tofilename.stem.overwrite (bool, default False) – If True, allows overwriting an existing
.pbmfile 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
.pbmfile.- Return type:
Note
New ijk-first workflows are encouraged to construct a
RegularGeometryexplicitly and useParquetBlockModel.from_geometry()orParquetBlockModel.from_parquet(). This helper remains for backwards compatibility with xyz-indexed DataFrames.
- classmethod from_geometry(geometry, path, name=None)[source]#
Create a
ParquetBlockModelfrom a geometry only.This constructor materialises a dense ijk grid defined by a
RegularGeometryinto 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
.pbmfile. 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:
- 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
ParquetBlockModelfrom a source Parquet file.This helper promotes a source
.parquetfile (typically xyz-centric) into a canonical.pbmcontainer with embeddedRegularGeometrymetadata.The input Parquet is expected to contain centroid columns
x,y,zdescribing block centroids in world coordinates.RegularGeometryis reconstructed from those centroids and/or any existing"parq-blockmodel"metadata viaRegularGeometry.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
.pbmandoverwriteis False, aValueErroris 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
.pbmfile. IfNone, all columns are copied.overwrite (bool, default False) – If True, allows overwriting an existing
.pbmfile 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
.pbmfile located alongside
parquet_path.
- A block model backed by a newly written
- Return type:
- property index_c: ndarray#
Zero-based C-order indices for the dense ijk grid.
The returned array has length
ni * nj * nkand 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()withindex="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 forgrid_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 fromRegularGeometryviato_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 asas_xyz()or via pandas accessors that read geometry fromdf.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, orNonefor 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 ofparq_blockmodelthat treated centroid coordinates as canonical. New code is encouraged to passindex="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
.pbmmay be sparse with respect to the dense ijk grid encoded bygeometry. This helper iterates over the on‑disk data in chunks, reindexes each chunk to the full dense centroid MultiIndexgeometry.to_multi_index_xyz()and writes the result tofilepath.- 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
.pbmfile.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
.pbmfile with embeddedRegularGeometrymetadata.
- 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:
Notes
The mesh uses right-handed coordinates in world space, with rotation applied via
geometryaxis 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: