Reblocking#

import tempfile

import pandas as pd
from pathlib import Path
import pyvista as pv

from parq_blockmodel import ParquetBlockModel

Create a Parquet Block Model#

We leverage the create_demo_block_model class method to create a Parquet Block Model.

temp_dir = Path(tempfile.gettempdir()) / "block_model_example"
temp_dir.mkdir(parents=True, exist_ok=True)

pbm: ParquetBlockModel = ParquetBlockModel.create_demo_block_model(
    filename=temp_dir / "demo_block_model.parquet")
pbm
ParquetBlockModel(name=demo_block_model, path=/tmp/block_model_example/demo_block_model.pbm)

Visualise the Model#

A continuous attribute.

pbm.plot(scalar='depth', threshold=False, enable_picking=True).show()
06 reblocking

A categorical attribute.

pbm.plot(scalar='depth_category', threshold=False, enable_picking=True).show()
06 reblocking

Upsampling#

Create a new grid with smaller blocks, and visualise.

upsampled_pbm: ParquetBlockModel = pbm.upsample(new_block_size=(0.5, 0.5, 0.5),
                                                interpolation_config={
                                                    'depth': 'linear',
                                                    'depth_category': 'nearest'
                                                })

upsampled_pbm.plot(scalar='depth', threshold=False, enable_picking=True).show()
06 reblocking

Visualise a reblocked categorical attribute.

upsampled_pbm.plot(scalar='depth_category', threshold=False, enable_picking=True).show()
06 reblocking

Downsample#

Downsample the upsampled model back to the original block size.

downsampled_pbm: ParquetBlockModel = upsampled_pbm.downsample(new_block_size=(1.0, 1.0, 1.0),
                                                              aggregation_config={
                                                                  'depth': {'method': 'mean'},
                                                                  'depth_category': {'method': 'mode'}
                                                              })

downsampled_pbm.plot(scalar='depth', threshold=False, enable_picking=True).show()
downsampled_pbm.plot(scalar='depth_category', threshold=False, enable_picking=True).show()
  • 06 reblocking
  • 06 reblocking

Validate#

"""Validate that reblocking preserves both data and centroids.

1. Confirm the round trip (upsample then downsample) returns the
   original *data* when viewed in a consistent ijk ordering.
2. Demonstrate that centroids derived from geometry are as expected.
"""

# Read attribute data on the canonical ijk grid for both models. We work
# only with the attributes of interest here (depth and depth_category)
# rather than asserting equality for every auxiliary column.
blocks_original: pd.DataFrame = pbm.read(index="ijk", dense=True)[["depth", "depth_category"]]
blocks_up_down: pd.DataFrame = downsampled_pbm.read(index="ijk", dense=True)[["depth", "depth_category"]]

# 1a. Ensure the reblocked attributes are fully populated (no NaNs) on
# the dense ijk grid for this dense demo model.
for col in ["depth", "depth_category"]:
    assert blocks_up_down[col].notna().all(), f"{col} contains NaNs after reblocking"

# 1b. Numeric attribute: values should be similar after an
# upsample→downsample round trip, but not necessarily identical. Check
# that the maximum absolute difference is within a small tolerance.
depth_diff = (blocks_up_down["depth"].sort_index() -
              blocks_original["depth"].sort_index()).abs()
assert depth_diff.max() < 0.5, (
    "Depth changed too much during reblocking; max |Δdepth| = "
    f"{float(depth_diff.max()):.3f}"
)

# 1c. Categorical attribute: in this demo we expect depth_category
# labels to be preserved exactly by the round trip. We compare the
# string representations to ignore differences in categorical metadata
# such as the "ordered" flag.
orig_cat = blocks_original["depth_category"].astype(str).sort_index()
down_cat = blocks_up_down["depth_category"].astype(str).sort_index()
pd.testing.assert_series_equal(orig_cat, down_cat, check_names=True)

# Demonstrate that the logical ijk grid and XYZ centroids of the
# downsampled model match those of the original geometry.
orig_ijk = pbm.geometry.to_multi_index_ijk()
down_ijk = downsampled_pbm.geometry.to_multi_index_ijk()
pd.testing.assert_index_equal(orig_ijk, down_ijk)

orig_xyz = pbm.geometry.to_multi_index_xyz()
down_xyz = downsampled_pbm.geometry.to_multi_index_xyz()

pd.testing.assert_index_equal(orig_xyz, down_xyz)

Total running time of the script: (0 minutes 2.451 seconds)

Gallery generated by Sphinx-Gallery