pnm 1.0.3
peclet.pnm — pore-network extraction from SDF geometry (Kokkos)
Loading...
Searching...
No Matches
test_smoke.py
Go to the documentation of this file.
1"""peclet.pnm binding smoke: the synthetic-SDF contracts through the Python API.
2
3Run: PYTHONPATH=<pnm build dir> python tests/python/test_smoke.py
4(registered as the `pnm_python_smoke` ctest under PECLET_PNM_BUILD_TESTS).
5
6The same three geometries as tests/kokkos/test_pnm.cpp, with the same hand-derived counts, so a
7(Nz, Ny, Nx) array / z-y-x origin+spacing convention slip in the binding shows up as a count or
8position mismatch here while the C++ contract stays green.
9"""
10
11import sys
12
13import numpy as np
14
15import peclet.pnm as pnm
16
17
18def grid(shape_xyz):
19 nx, ny, nz = shape_xyz
20 z, y, x = np.meshgrid(
21 np.arange(nz, dtype=np.float32),
22 np.arange(ny, dtype=np.float32),
23 np.arange(nx, dtype=np.float32),
24 indexing="ij",
25 )
26 return x, y, z # each (Nz, Ny, Nx), x fastest
27
28
29def throats(conns):
30 return conns[(conns[:, 0] > 0) & (conns[:, 1] > 0)].tolist()
31
32
34 n, R = 16, 5.5
35 x, y, z = grid((n, n, n))
36 sdf = (R - np.sqrt((x - 8) ** 2 + (y - 8) ** 2 + (z - 8) ** 2)).astype(np.float32)
37 pores, seg, conns = pnm.extract_pore_network(sdf, [0.0] * 3, [1.0] * 3)
38 assert isinstance(seg, np.ndarray) and seg.dtype == np.int32 and seg.shape == sdf.shape
39 assert isinstance(conns, np.ndarray) and conns.dtype == np.int32 and conns.shape == (1, 2)
40 assert len(pores) == 1, pores
41 p = pores[0]
42 assert p.radius == np.float32(R), p
43 assert (p.x, p.y, p.z) == (8.0, 8.0, 8.0), p
44 assert seg.max() == 1 and seg.min() == -1 and not (seg == 0).any()
45 assert conns.tolist() == [[-1, 1]], conns
46 assert len(pnm.extract_pores(sdf, [0.0] * 3, [1.0] * 3)) == 1
47 # staged == fused, and extract_topology reads the 3-D int32 array (or the flat vector + shape)
48 seg2 = pnm.segment_volume(sdf, [1.0] * 3)
49 assert seg2.shape == sdf.shape and np.array_equal(seg2, seg)
50 assert np.array_equal(pnm.extract_topology(seg2), conns)
51 assert np.array_equal(pnm.extract_topology(seg2.ravel(), shape_zyx=sdf.shape), conns)
52 # z-y-x origin/spacing: the pore lands at origin + voxel*spacing per axis.
53 (p2,), _, _ = pnm.extract_pore_network(sdf, [0.5, 2.0, -1.0], [2.0, 1.0, 0.5])
54 assert abs(p2.x - (-1.0 + 8 * 0.5)) < 1e-5 and abs(p2.y - (2.0 + 8)) < 1e-5, p2
55 assert abs(p2.z - (0.5 + 8 * 2.0)) < 1e-5, p2
56 print("one_sphere: 1 pore, 0 throats, 1 connection ok")
57
58
60 R = 5.5
61 x, y, z = grid((24, 16, 16))
62 d1 = R - np.sqrt((x - 8) ** 2 + (y - 8) ** 2 + (z - 8) ** 2)
63 d2 = R - np.sqrt((x - 16) ** 2 + (y - 8) ** 2 + (z - 8) ** 2)
64 sdf = np.maximum(d1, d2).astype(np.float32)
65 pores, seg, conns = pnm.extract_pore_network(sdf, [0.0] * 3, [1.0] * 3)
66 assert len(pores) == 2, pores
67 assert sorted((p.x, p.y, p.z) for p in pores) == [(8.0, 8.0, 8.0), (16.0, 8.0, 8.0)], pores
68 assert conns.tolist() == [[-1, 1], [-1, 2], [1, 2]], conns
69 assert throats(conns) == [[1, 2]]
70 assert seg.max() == 2 and seg.min() == -1 and not (seg == 0).any()
71 assert seg.shape == sdf.shape and seg[8, 8, 8] == 1 and seg[8, 8, 16] == 2 # (Nz,Ny,Nx)
72 print("two_spheres: 2 pores, 1 throat, 3 connections ok")
73
74
76 # The tests/kokkos_mpi generator: 2x2x2 periodic lattice of solid spheres (min-image).
77 gd = (36, 30, 24)
78 R = np.float32(0.22 * min(gd))
79 x, y, z = grid(gd)
80 sdf = np.full(x.shape, 1e30, np.float32)
81 for cz in (0, 1):
82 for cy in (0, 1):
83 for cx in (0, 1):
84 cc = [gd[i] * (2 * c + 1) / 4.0 for i, c in enumerate((cx, cy, cz))]
85 dx, dy, dz = np.abs(x - cc[0]), np.abs(y - cc[1]), np.abs(z - cc[2])
86 dx = np.minimum(dx, gd[0] - dx)
87 dy = np.minimum(dy, gd[1] - dy)
88 dz = np.minimum(dz, gd[2] - dz)
89 sdf = np.minimum(sdf, (np.sqrt(dx * dx + dy * dy + dz * dz) - R).astype(np.float32))
90 pores, seg, conns = pnm.extract_pore_network(sdf, [0.0] * 3, [1.0] * 3)
91 assert len(pores) == 8, len(pores)
92 centres = sorted((round(p.x), round(p.y), round(p.z)) for p in pores)
93 assert centres == [(px, py, pz) for px in (0, 18) for py in (0, 15) for pz in (0, 12)], centres
94 assert all(abs(p.radius - sdf[0, 0, 0]) == 0 for p in pores), pores
95 assert len(throats(conns)) == 12 and len(conns) == 76, (len(throats(conns)), len(conns))
96 assert seg.max() == 8 and seg.min() == -8 and not (seg == 0).any()
97 assert len(pnm.extract_pores(sdf, [0.0] * 3, [1.0] * 3)) == 8
98 print("sphere_lattice: 8 pores, 12 throats, 76 connections ok")
99
100
101if __name__ == "__main__":
102 print("peclet.pnm on", pnm.execution_space)
106 print("peclet.pnm smoke ok")
107 sys.exit(0)
check_two_spheres()
Definition test_smoke.py:59
check_one_sphere()
Definition test_smoke.py:33
throats(conns)
Definition test_smoke.py:29
grid(shape_xyz)
Definition test_smoke.py:18
check_sphere_lattice()
Definition test_smoke.py:75