60 std::array<int, 3> resolution,
61 std::array<float, 3> spacing,
62 Kokkos::View<int*, Mem>* rootsOut =
nullptr) {
64 const std::size_t n = sdf.extent(0);
65 const float min_sp = std::min(spacing[0], std::min(spacing[1], spacing[2]));
66 const float thr = -1.5f * min_sp;
68 Kokkos::View<int*, Mem> labels(
"labels", n), roots(
"roots", n);
69 Kokkos::View<int, Mem> changed(
"changed");
84 Kokkos::View<int*, Mem> labelsN(
"labelsN", n);
91 space, geo, sdf,
false, KOKKOS_LAMBDA(
Index i,
Index walker) { roots(i) = int(walker); });
99 Kokkos::View<int*, Mem> seg(
"seg", n);
100 Kokkos::View<int*, Mem> minPoreIdx(
"minPoreIdx", n), minSolidIdx(
"minSolidIdx", n);
101 Kokkos::View<int*, Mem> poreFirst(
"poreFirst", n), solidFirst(
"solidFirst", n);
102 Kokkos::View<int*, Mem> poreRank(
"poreRank", n), solidRank(
"solidRank", n);
103 Kokkos::View<int*, Mem> poreId(
"poreId", n), solidId(
"solidId", n);
104 constexpr int kBig = 0x7fffffff;
105 Kokkos::deep_copy(minPoreIdx, kBig);
106 Kokkos::deep_copy(minSolidIdx, kBig);
107 const std::size_t nn = n;
111 Kokkos::parallel_for(
112 "pnm::relabel_min", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
114 Kokkos::atomic_min(&minPoreIdx(roots(i)),
static_cast<int>(i));
115 else if (labels(i) != -1)
116 Kokkos::atomic_min(&minSolidIdx(labels(i)),
static_cast<int>(i));
120 Kokkos::parallel_for(
121 "pnm::relabel_first", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
122 poreFirst(i) = (sdf(i) > 0.0f && minPoreIdx(roots(i)) ==
static_cast<int>(i)) ? 1 : 0;
124 (sdf(i) <= 0.0f && labels(i) != -1 && minSolidIdx(labels(i)) ==
static_cast<int>(i))
130 Kokkos::parallel_scan(
131 "pnm::relabel_porescan", R1(space, 0, nn),
132 KOKKOS_LAMBDA(std::size_t i,
int& upd,
const bool fin) {
133 const int v = poreFirst(i);
138 Kokkos::parallel_scan(
139 "pnm::relabel_solidscan", R1(space, 0, nn),
140 KOKKOS_LAMBDA(std::size_t i,
int& upd,
const bool fin) {
141 const int v = solidFirst(i);
148 Kokkos::parallel_for(
149 "pnm::relabel_assign", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
151 poreId(roots(i)) = poreRank(i) + 1;
153 solidId(labels(i)) = -(solidRank(i) + 1);
158 Kokkos::parallel_for(
159 "pnm::relabel_seg", R1(space, 0, nn), KOKKOS_LAMBDA(std::size_t i) {
160 seg(i) = (sdf(i) > 0.0f) ? poreId(roots(i)) : (labels(i) == -1 ? 0 : solidId(labels(i)));
245 const std::vector<float>& sdf_h, std::array<int, 3> resolution, std::array<float, 3> origin,
246 std::array<float, 3> spacing,
const std::vector<double>& u_h,
const std::vector<double>& v_h,
247 const std::vector<double>& w_h,
const std::vector<double>& p_h,
const std::vector<double>& ox_h,
248 const std::vector<double>& oy_h,
249 const std::vector<double>& oz_h,
250 std::array<double, 3> grad_p) {
251 namespace kn = kernels;
255 const I3 res{resolution[0], resolution[1], resolution[2]};
257 const std::size_t n = sdf_h.size();
261 Kokkos::View<float*, Mem> sdf(
"nf::sdf", n);
263 Kokkos::View<int*, Mem> roots;
264 Kokkos::View<int*, Mem> seg =
segmentVolumeView(sdf, resolution, spacing, &roots);
267 const int np = kn::maxLabel(space, geo, seg);
272 Kokkos::View<int*, Mem> peak(
"nf::peak", np);
273 Kokkos::parallel_for(
274 "nf::peaks", R1(space, 0, n), KOKKOS_LAMBDA(std::size_t i) {
275 if (sdf(i) > 0.0f && roots(i) ==
static_cast<int>(i))
276 peak(seg(i) - 1) =
static_cast<int>(i);
279 Kokkos::View<Pore*, Mem> poresD(
"nf::pores", np);
281 const float oxo = origin[0], oyo = origin[1], ozo = origin[2];
282 const float sx = spacing[0], sy = spacing[1], sz = spacing[2];
284 Kokkos::parallel_for(
285 "nf::centers", R1(space, 0, np), KOKKOS_LAMBDA(
int k) {
287 g.ownedCoords(peak(k), ix, iy, iz);
288 poresD(k) = kn::poreAt(g, sdf, ix, iy, iz, oxo, oyo, ozo, sx, sy, sz);
295 auto upl = [&](
const std::vector<double>& h,
const char* nm,
bool required) {
296 Kokkos::View<double*, Mem> d(nm, required || !h.empty() ? n : 0);
301 Kokkos::View<double*, Mem> u = upl(u_h,
"nf::u",
true), v = upl(v_h,
"nf::v",
true),
302 w = upl(w_h,
"nf::w",
true), p = upl(p_h,
"nf::p",
true);
303 const bool hasOpen = !ox_h.empty();
304 Kokkos::View<double*, Mem> ox = upl(ox_h,
"nf::ox",
false), oy = upl(oy_h,
"nf::oy",
false),
305 oz = upl(oz_h,
"nf::oz",
false);
308 Kokkos::View<double*, Mem> ppres(
"nf::ppres", np);
310 const float oxo = origin[0], oyo = origin[1], ozo = origin[2];
311 const float sx = spacing[0], sy = spacing[1], sz = spacing[2];
313 Kokkos::parallel_for(
314 "nf::pore_pressure", R1(space, 0, np), KOKKOS_LAMBDA(
int k) {
315 const Pore po = poresD(k);
316 const double gp[3] = {(po.
x - oxo) / sx, (po.
y - oyo) / sy, (po.
z - ozo) / sz};
317 ppres(k) = kn::trilinear(g, p, gp);
322 auto hv =
downloadN(ppres,
static_cast<std::size_t
>(np));
332 Kokkos::View<int*, Mem> flowLab(
"nf::flowLab", n);
334 space, geo, sdf,
true, KOKKOS_LAMBDA(
Index i,
Index walker) {
335 flowLab(i) = seg(walker);
352 const std::size_t nf = 3 * n;
353 Kokkos::View<Index*, Mem> fpar(
"nf::fpar", nf);
354 Kokkos::View<Index*, Mem> fpair(
"nf::fpair", nf);
356 Kokkos::View<Index*, Mem> par(
"nf::fccl", nf);
357 Kokkos::View<char*, Mem> core(
"nf::fcore", nf);
358 Kokkos::View<int, Mem> changed(
"nf::fchanged");
359 kn::faceInit(space, geo, flowLab, sdf, ox, oy, oz, hasOpen, core, par, fpair);
361 auto cclPass = [&]() {
362 kn::cclFixpoint(space, par, nf, changed,
363 [&]() { kn::faceMerge(space, geo, par, fpair, changed); });
366 auto rootOf = KOKKOS_LAMBDA(
Index parent) {
370 kn::faceLabelCore(space, nf, par, fpair, fpar, rootOf);
372 const kn::FlatFaces<Kokkos::View<Index*, Mem>> nbLab{fpar}, nbPair{fpair};
375 Kokkos::deep_copy(changed, 0);
376 kn::filmAttachSweep(space, geo, fpar, fpair, core, nbLab, nbPair, changed);
380 kn::leftoverInit(space, nf, fpar, par);
382 kn::faceLabelLeftover(space, nf, par, fpar, rootOf);
385 kn::ThroatSlots slots;
387 Kokkos::View<Index*, Mem> rbuf(
"nf::rbuf", nf ? nf : 1), pbuf(
"nf::pbuf", nf ? nf : 1);
388 Kokkos::View<int, Mem> rcnt(
"nf::rcnt");
389 Kokkos::deep_copy(rcnt, 0);
390 Kokkos::parallel_for(
391 "nf::face_roots", R1(space, 0, nf), KOKKOS_LAMBDA(std::size_t f) {
392 if (fpar(f) ==
Index(f)) {
393 const int s0 = Kokkos::atomic_fetch_add(&rcnt(), 1);
399 const std::size_t nr = std::size_t(
readScalar(rcnt));
402 std::vector<std::pair<Index, Index>> rootPair(nr);
403 for (std::size_t i = 0; i < nr; ++i)
404 rootPair[i] = {hr[i], hp[i]};
405 slots = kn::throatSlots(std::move(rootPair));
408 const std::size_t nt = out.
throats.size();
409 Kokkos::View<Index*, Mem> keyD(
"nf::keys", nt);
410 Kokkos::View<int*, Mem> slotD(
"nf::slots", nt);
418 const auto peakH =
downloadN(peak,
static_cast<std::size_t
>(np));
419 std::vector<double> ancX(np), ancY(np), ancZ(np);
420 for (
int k = 0; k < np; ++k) {
421 const int pk = peakH[k];
422 ancX[k] = origin[0] + (pk % res.x) *
double(spacing[0]);
423 ancY[k] = origin[1] + ((pk / res.x) % res.y) * double(spacing[1]);
424 ancZ[k] = origin[2] + (pk / (res.x * res.y)) * double(spacing[2]);
426 Kokkos::View<double*, Mem> ancXD(
"nf::ancX", np), ancYD(
"nf::ancY", np), ancZD(
"nf::ancZ", np);
433 Kokkos::View<double*, Mem> Q(
"nf::Q", nt), A(
"nf::A", nt), resid(
"nf::resid", np);
434 Kokkos::View<double*, Mem> Cx(
"nf::Cx", nt), Cy(
"nf::Cy", nt), Cz(
"nf::Cz", nt);
435 kn::throatFlux(space, geo, flowLab, u, v, w, ox, oy, oz, hasOpen, fpar, keyD, slotD, nt, ancXD,
436 ancYD, ancZD, origin, spacing, resolution, Q, A, Cx, Cy, Cz, resid);
441 auto hr =
downloadN(resid,
static_cast<std::size_t
>(np));
450 hcz, ancX, ancY, ancZ, spacing, resolution, grad_p);