Skip to main content

peclet-pnm

peclet.pnm — GPU pore-network extraction from SDF geometry.

Given a signed-distance-field (SDF) description of a porous solid (negative inside the solid, positive in the pore space), peclet.pnm extracts the pore network:

  • SDFReader — pure-C++ VTI (VTK ImageData) reader for SDF volumes.
  • extract_pores — pore detection: local maxima of the SDF + weighted centroids and radii.
  • segment_volume — marker-controlled watershed segmentation of the pore space (marker init → union-find connected-component labelling → flood fill).
  • extract_topology_gpu — pore-to-pore connectivity (throats) from boundary pairs between basins.
  • extract_pore_network — the fused pipeline (SDF uploaded once, segmentation device-resident across all three stages): returns (pores, segmentation, connections) in one call.

The compute is Kokkos — the same source runs on CUDA, HIP, and OpenMP backends, selected at build time by the install prefix. Part of the peclet suite; split out of peclet-flow (its former peclet.flow.pnm module — the repo's original "pnm_from_sdf" feature).

Install / build

# From the peclet suite checkout (Kokkos prefix bootstrapped once by ../tools/bootstrap_deps.sh):
CMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda" pip install .

# Or a dev cmake build (nanobind found via the active interpreter):
cmake -S . -B build -DCMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda"
cmake --build build -j            # -> build/peclet/pnm/_pnm.*.so ; PYTHONPATH=$PWD/build to import

Without a Kokkos prefix on CMAKE_PREFIX_PATH, the build vendors Kokkos (OpenMP+Serial) via FetchContent, so pip install . works standalone on any Linux with a C++20 toolchain.

Usage

import peclet.pnm as pnm

sdf_3d, origin_zyx, spacing_zyx = pnm.SDFReader.read_vti("packing.vti")  # (Nz,Ny,Nx) C-order
pores = pnm.extract_pores(sdf_3d, origin_zyx, spacing_zyx)               # Pore(x,y,z,radius) list
seg = pnm.segment_volume(sdf_3d, spacing_zyx)                            # flat per-voxel pore label
conns = pnm.extract_topology_gpu(seg, list(sdf_3d.shape))                # [(label_a, label_b), ...]

# or fused (SDF uploaded once, segmentation stays device-resident across stages):
pores, seg, conns = pnm.extract_pore_network(sdf_3d, origin_zyx, spacing_zyx)

Conventions: the SDF array is (Nz, Ny, Nx) C-order (x fastest), origin/spacing are z-y-x; SDF sign is negative inside the solid — see the suite's docs/CONVENTIONS.md.

Smoke tests: python scripts/test_extraction.py <sdf.vti> and python scripts/verify_segmentation.py <sdf.vti> (writes a labelled .vti + a pore-pair edge list).

Network flow: throat flow rates + pore pressures from a DNS

extract_network_flow turns a converged peclet-flow velocity/pressure field on the same grid into pore-network flow data — the method carried over from the Voronoi-tessellation PNM of sphere packings (pnm_voronoi), where the throat flow was ∫u·n over the Voronoi facet and the pore pressure a trilinear sample at the pore center:

s = peclet.flow.Solver(nx, ny, nz)
...; s.set_body_force(fx, 0, 0); s.set_solid(sdf_xyz, cutcell_pressure=True); ...steps...
net = pnm.extract_network_flow(
    sdf_zyx, origin_zyx, spacing_zyx,
    s.get_uf().T, s.get_vf().T, s.get_wf().T, s.get_p().T,   # zero-copy transposes to zyx
    s.get_ox().T, s.get_oy().T, s.get_oz().T,                # cut-cell face openness
    grad_p_zyx=[0, 0, -fx])                                  # body force f == -grad p_macro
net["throat_flow"]     # Q through each pore-pore interface (o·u·A summed over MAC faces)
net["pore_pressure"]   # periodic p interpolated at each pore center (basin SDF peak)
net["throat_dp"]       # total-pressure drop P_i - P_j (periodic parts + macro gradient
                       # along the throat-anchored min-image path)
net["pore_residual"]   # signed flux over each pore's whole boundary — ~ solver tolerance

On the voxel network the throat integral is exact: a throat is a set of grid-aligned MAC faces and the openness-weighted face velocity is the discrete flux carrier, so per-pore mass balance holds to the pressure-solve tolerance (pore_residual is the built-in check). Fluxes are accumulated on flow basins (gradient-ascent assignment of every cell, including cut cells whose center is inside the solid) — keyed on the segmentation labels alone, the near-wall staircase flux would bypass the interface (measured 6% on a tube).

Both IBM variants are supported. With the cut-cell IBM the bookkeeping is machine-exact (pass get_ox()...). With the ghost-cell IBM (set_ghost_projection(True)) pass flow's get_ox_proj()/get_oy_proj()/get_oz_proj() — the binary (COUPLED) openness the ghost projection conserves. Ghost-cell IBM is pointwise 2nd-order but not locally mass-conserving at the wall, so there the network data is truncation-accurate: pore_residual becomes the per-pore wall leak (measured 3.2e-2·F at a 4-cell tube radius, converging at order ~2.7 under refinement).

MPI: extract_network_flow_mpi(sdf_local, global_shape_zyx, ..., u_local, ...) runs the whole pipeline distributed on the core ORB blocks (fields from a distributed peclet.flow run on the same decomposition); every rank returns the identical global network. Matches the single-rank result to accumulation-order tolerance (tests/kokkos_mpi/test_pnm_flow_mpi, np = 1, 2, 4).

Validated in scripts/verify_network_flow.py (chamber-tube chain + asymmetric tube lattice + the ghost-IBM chain, DNS by peclet.flow): every throat carries the DNS flux to ~1e-11 relative (cut-cell), residuals ~1e-12·F, g = Q/dp > 0 on all throats, and the dp sum around each loop equals the macroscopic drop. scripts/demo_network_flow_packing.py runs the pipeline on a real sphere packing.

Throats are per-patch: a throat is a connected patch of interface faces (CCL over the interface, core faces = both cells fluid-centered, wall-film faces attached by propagation), so two disjoint interfaces between the same two pores — e.g. two parallel tubes, or a direct contact plus one through the periodic wrap — are separate parallel throats and the throat list can repeat a pore pair (validated: two capsules of different radii report two (1,2) throats whose fluxes sum to the DNS flux exactly). Remaining caveat: on loose packings (porosity ≳ 0.6) intra-pore pressure variation is comparable to throat drops, so per-throat g = Q/dp scatters — a property of the point-pressure PNM abstraction, not of the extraction.

Distributed (MPI) extraction

Built with -DPECLET_PNM_MPI=ON, the module also runs the whole pipeline multi-rank: the SDF is decomposed over ranks by the shared peclet-core ORB (the same deterministic partition flow/dem use), every stage runs per-rank on a 1-cell ghost layer (core GridHalo exchange), and the result is bit-exact to the single-rank pipeline — labels are global voxel ids, so the CCL fixpoint, the watershed flood (Jacobi), the gradient-path pore basins, and the renumbering are all decomposition-independent.

# mpirun -np 4 python extract.py
import peclet.pnm as pnm
origin, shape = pnm.mpi_block(global_shape_zyx)      # this rank's ORB block of the global grid
local = sdf[origin[0]:origin[0]+shape[0], origin[1]:origin[1]+shape[1], origin[2]:origin[2]+shape[2]]
pores, seg, conns = pnm.extract_pore_network_mpi(local, global_shape_zyx, origin_zyx, spacing_zyx)
# pores: the pores whose peak this rank owns; seg: this rank's block; conns: global (identical everywhere)

Validated by tests/kokkos_mpi (ctest, np = 1, 2, 4, OpenMP + CUDA): per-voxel segmentation ids, the pore set, and the connection list all match the single-rank oracle exactly (pore centroid positions to 1e-5·spacing on GPU — FMA contraction noise; radii and everything integer bitwise).

License

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

peclet_pnm-0.1.0.tar.gz (65.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

peclet_pnm-0.1.0-cp313-cp313-manylinux_2_28_x86_64.whl (471.4 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.28+ x86-64

peclet_pnm-0.1.0-cp312-cp312-manylinux_2_28_x86_64.whl (471.4 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.28+ x86-64

peclet_pnm-0.1.0-cp311-cp311-manylinux_2_28_x86_64.whl (472.5 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.28+ x86-64

peclet_pnm-0.1.0-cp310-cp310-manylinux_2_28_x86_64.whl (473.1 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.28+ x86-64

File details

Details for the file peclet_pnm-0.1.0.tar.gz.

File metadata

  • Download URL: peclet_pnm-0.1.0.tar.gz
  • Upload date:
  • Size: 65.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for peclet_pnm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bf683079851a068740a3550cb7aaefebf9b669b43a8885a0bf31b6dfd1ec3801
MD5 f8ea8c7cd064c7d2eac87bf53c086169
BLAKE2b-256 677085cfe526acce60845d1df13f855db8da4ae90cf0df5f715a4479ee420bcc

See more details on using hashes here.

Provenance

The following attestation bundles were made for peclet_pnm-0.1.0.tar.gz:

Publisher: release.yml on computational-chemical-engineering/peclet-pnm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peclet_pnm-0.1.0-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peclet_pnm-0.1.0-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 fc5d6c6a8c50e47a416caac191a69d10432daa468ddcacb0e69a0cbb32a49891
MD5 f2e8632c3993df38aeff11b7398c06e8
BLAKE2b-256 d6e1d49147b1de0bc008d8d4a4e7950dc18431fe413962c9f42eee21f398326e

See more details on using hashes here.

Provenance

The following attestation bundles were made for peclet_pnm-0.1.0-cp313-cp313-manylinux_2_28_x86_64.whl:

Publisher: release.yml on computational-chemical-engineering/peclet-pnm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peclet_pnm-0.1.0-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peclet_pnm-0.1.0-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 28eb6a350e6e4634dae284ef971873f72d4cc287e0cdee2d06be087886502e4d
MD5 89500a7faa4d201c46e6db325fc85ddc
BLAKE2b-256 79dc6e05118c795ed8b3b9831a18c58dc06588368bc5fd87587895a3d4aa549b

See more details on using hashes here.

Provenance

The following attestation bundles were made for peclet_pnm-0.1.0-cp312-cp312-manylinux_2_28_x86_64.whl:

Publisher: release.yml on computational-chemical-engineering/peclet-pnm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peclet_pnm-0.1.0-cp311-cp311-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peclet_pnm-0.1.0-cp311-cp311-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 0e9b379908f5df4534401551abe94452f7b6496275807a4f7f2bd4003de97b1c
MD5 620c3c55596b1babc867899d1cec1b7b
BLAKE2b-256 33614c9f4ff0174d6750026a11581d7738156f4813a41a47ffe30c4dfc892620

See more details on using hashes here.

Provenance

The following attestation bundles were made for peclet_pnm-0.1.0-cp311-cp311-manylinux_2_28_x86_64.whl:

Publisher: release.yml on computational-chemical-engineering/peclet-pnm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peclet_pnm-0.1.0-cp310-cp310-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peclet_pnm-0.1.0-cp310-cp310-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 3f995f324011ef4391deccbd12a25e51afeeeba4683a1a70a418a415d5f2953b
MD5 87ca139f47bd64e1364c19450c00189a
BLAKE2b-256 09b11b891974ce63f5bf38d8ba18e077919008471804b066e7e6dfef80fbe254

See more details on using hashes here.

Provenance

The following attestation bundles were made for peclet_pnm-0.1.0-cp310-cp310-manylinux_2_28_x86_64.whl:

Publisher: release.yml on computational-chemical-engineering/peclet-pnm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

5 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page