24#include <Kokkos_Core.hpp>
29#include "../synthetic_sdf.hpp"
42 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
47using Pair = std::pair<int, int>;
49bool poreLess(
const Pore& a,
const Pore& b) {
52std::vector<Pore> sorted(std::vector<Pore> p) {
53 std::sort(
p.begin(),
p.end(), poreLess);
56bool poreBitwise(
const Pore& a,
const Pore& b) {
59bool poresBitwise(
const std::vector<Pore>& a,
const std::vector<Pore>& b) {
60 return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), poreBitwise);
64std::vector<Pair>
throats(
const std::vector<Pair>& conns) {
66 for (
const auto& c : conns)
67 if (c.first > 0 && c.second > 0)
71int minLabel(
const std::vector<int>& seg) {
72 return *std::min_element(seg.begin(), seg.end());
74int maxLabel(
const std::vector<int>& seg) {
75 return *std::max_element(seg.begin(), seg.end());
77std::size_t countLabel(
const std::vector<int>& seg,
int l) {
78 return std::size_t(std::count(seg.begin(), seg.end(), l));
83bool poreAt(
const Pore& p, std::array<float, 3> xyz, std::array<float, 3> spc) {
84 return std::fabs(
p.x - xyz[0]) <= 1e-5f * spc[0] && std::fabs(
p.y - xyz[1]) <= 1e-5f * spc[1] &&
85 std::fabs(
p.z - xyz[2]) <= 1e-5f * spc[2];
88void report(
const char* name,
const PoreNetwork& net) {
89 std::printf(
"[%s] %zu pores, %zu connections (%zu throats), labels %d..%d\n", name,
95void checkStagedAndDeterministic(
const char* name,
const std::vector<float>& sdf,
96 std::array<int, 3> gd, std::array<float, 3> org,
101 CHECK(poresBitwise(sorted(pores), sorted(
net.pores)));
106 CHECK(poresBitwise(sorted(again.pores), sorted(
net.pores)));
108 CHECK(again.connections ==
net.connections);
109 std::printf(
"[%s] staged == fused, two runs bitwise equal\n", name);
113void testOneSphere() {
114 const std::array<int, 3> gd{16, 16, 16}, c{8, 8, 8};
115 const float R = 5.5f;
116 const std::array<float, 3> org{0.f, 0.f, 0.f}, spc{1.f, 1.f, 1.f};
120 report(
"one_sphere", net);
123 if (
net.pores.size() == 1) {
130 CHECK((
net.connections == std::vector<Pair>{{-1, 1}}));
133 std::size_t nPore = 0;
134 for (std::size_t i = 0; i <
sdf.size(); ++i) {
137 CHECK((sdf[i] > 0.f) == (
net.seg[i] == 1));
139 CHECK(countLabel(
net.seg, 1) == nPore);
140 checkStagedAndDeterministic(
"one_sphere", sdf, gd, org, spc, net);
144 const std::array<float, 3> org2{-1.f, 2.f, 0.5f}, spc2{0.5f, 1.f, 2.f};
147 if (
net2.pores.size() == 1) {
150 {org2[0] + 8 * spc2[0], org2[1] + 8 * spc2[1], org2[2] + 8 * spc2[2]}, spc2));
152 CHECK((
net2.connections == std::vector<Pair>{{-1, 1}}));
156void testTwoSpheres() {
157 const std::array<int, 3> gd{24, 16, 16}, c1{8, 8, 8}, c2{16, 8, 8};
158 const float R = 5.5f;
159 const std::array<float, 3> org{0.f, 0.f, 0.f}, spc{1.f, 1.f, 1.f};
163 report(
"two_spheres", net);
166 const auto p = sorted(
net.pores);
168 CHECK(p[0].radius == R && p[1].radius == R);
175 CHECK(
net.connections == (std::vector<Pair>{{-1, 1}, {-1, 2}, {1, 2}}));
180 const auto idx = [&](
int x,
int y,
int z) {
return (std::size_t(z) * gd[1] +
y) * gd[0] + x; };
185 for (std::size_t i = 0; i <
sdf.size(); ++i)
186 CHECK((sdf[i] > 0.f) == (
net.seg[i] > 0));
187 checkStagedAndDeterministic(
"two_spheres", sdf, gd, org, spc, net);
191void testSphereLattice() {
192 const std::array<int, 3> gd{36, 30, 24};
193 const std::array<float, 3> org{0.f, 0.f, 0.f}, spc{1.f, 1.f, 1.f};
197 report(
"sphere_lattice", net);
202 CHECK(std::fabs(Rpeak - (std::sqrt(9.f * 9.f + 7.5f * 7.5f + 6.f * 6.f) - 0.22f * 24.f)) < 1e-5f);
203 const auto p = sorted(
net.pores);
205 for (
float x : {0.f, 18.f})
206 for (
float y : {0.f, 15.f})
207 for (
float z : {0.f, 12.f}) {
209 CHECK(p[k].radius == Rpeak);
219 for (std::size_t i = 0; i <
sdf.size(); ++i)
220 CHECK((sdf[i] > 0.f) == (
net.seg[i] > 0));
224 const std::vector<Pair> expectedThroats{{1, 2}, {1, 3}, {1, 5}, {2, 4}, {2, 6}, {3, 4},
225 {3, 7}, {4, 8}, {5, 6}, {5, 7}, {6, 8}, {7, 8}};
228 CHECK(
net.connections.size() == 76);
229 std::vector<Pair> expected;
230 for (
int s = -8;
s <= -1; ++
s)
231 for (
int q = 1; q <= 8; ++q)
232 expected.push_back({s, q});
233 expected.insert(expected.end(), expectedThroats.begin(), expectedThroats.end());
236 checkStagedAndDeterministic(
"sphere_lattice", sdf, gd, org, spc, net);
241 const std::array<int, 3> gd{0, 0, 0};
242 const std::array<float, 3>
z{0.f, 0.f, 0.f}, one{1.f, 1.f, 1.f};
243 const std::vector<float> none;
248 CHECK(
net.pores.empty() &&
net.seg.empty() &&
net.connections.empty());
253int main(
int argc,
char** argv) {
254 Kokkos::ScopeGuard kokkos(argc, argv);
255 std::printf(
"peclet-pnm single-rank tests on %s\n", Kokkos::DefaultExecutionSpace::name());
261 std::fprintf(stderr,
"%d CHECK(s) failed\n", failures);
263 std::printf(
"all checks passed\n");
264 return failures ? 1 : 0;
int maxLabel(const Exec &space, const Geo &geo, const SegView &seg)
The largest label over the owned box (= the number of pore labels).
Pore poreAt(const Geo &g, const SdfView &sdf, int lx, int ly, int lz, float ox, float oy, float oz, float sx, float sy, float sz)
The pore record at a peak: sub-voxel position by the SDF^2-weighted centroid of the 3^3 neighbourhood...
float sphereLatticeSdf(int gx, int gy, int gz, std::array< int, 3 > gd)
2x2x2 lattice of SOLID spheres in a periodic box (min-image distance); pores at the interstices.
std::vector< float > sampleGrid(std::array< int, 3 > gd, const SdfFn &fn)
Sample fn(gx, gy, gz) over the whole gd grid in pnm's flat x-fastest layout.
float twoSpherePoresSdf(int gx, int gy, int gz, std::array< int, 3 > c1, std::array< int, 3 > c2, float radius)
Two overlapping spherical pores (the union = max of the two SDFs); a throat at the mid-plane when the...
float spherePoreSdf(int gx, int gy, int gz, std::array< int, 3 > c, float radius)
One spherical PORE (SDF > 0 inside) of radius radius centred on the integer voxel c; solid everywhere...
std::vector< int > segment_volume_k(const std::vector< float > &sdf_h, std::array< int, 3 > resolution, std::array< float, 3 > spacing)
std::vector< Pore > extract_pores_k(const std::vector< float > &sdf_h, std::array< int, 3 > resolution, std::array< float, 3 > origin, std::array< float, 3 > spacing)
std::vector< std::pair< int, int > > extract_topology_k(const int *seg_h, std::size_t n, std::array< int, 3 > resolution)
PoreNetwork extract_pore_network_k(const std::vector< float > &sdf_h, std::array< int, 3 > resolution, std::array< float, 3 > origin, std::array< float, 3 > spacing)
The full pore network from one extraction: pores, the per-voxel segmentation (flat),...
int main(int argc, char **argv)