344 std::array<int, 3> gdims,
345 std::array<float, 3> origin,
346 std::array<float, 3> spacing, MPI_Comm comm) {
347 namespace dm = detail_mpi;
348 namespace kn = kernels;
349 using peclet::core::View;
350 using peclet::core::halo::GridHalo;
351 using peclet::core::halo::GridHaloTopology;
353 int rank = 0, nranks = 1;
354 MPI_Comm_rank(comm, &rank);
355 MPI_Comm_size(comm, &nranks);
358 peclet::core::decomp::BlockDecomposer<3> dec(
static_cast<std::size_t
>(nranks),
359 IVec<3>{gdims[0], gdims[1], gdims[2]});
361 GridHaloTopology<3> topo;
362 topo.buildTopology(dec, rank, G, {
true,
true,
true}, comm);
363 GridHalo<float> haloF;
365 GridHalo<Index> haloI;
370 const BlockGeo geo = dm::blockGeoOf(topo, gdims, G);
375 const std::size_t nInner = geo.
numOwned();
376 const std::size_t nExt = geo.
numExt();
380 int bad = sdf_local.size() != nInner ? 1 : 0, anyBad = 0;
381 MPI_Allreduce(&bad, &anyBad, 1, MPI_INT, MPI_MAX, comm);
383 throw std::runtime_error(bad ?
"[pnm::mpi] rank " + std::to_string(rank) +
384 ": sdf_local size " + std::to_string(sdf_local.size()) +
385 " != block " + std::to_string(geo.
nx) +
"x" +
386 std::to_string(geo.
ny) +
"x" + std::to_string(geo.
nz)
387 :
"[pnm::mpi] rank " + std::to_string(rank) +
388 ": sdf_local size mismatch on another rank");
392 const auto inner = kn::ownedRange(space, geo);
393 const auto ext = kn::MD3(space, {0, 0, 0}, {geo.
ex, geo.
ey, geo.
ez});
396 View<float> sdfE = dm::stageInner(space, geo, sdf_local,
"pnm::mpi::sdfE");
397 haloF.exchange(sdfE);
401 const int max_pores = 1000000;
402 View<Pore> pores(
"pnm::mpi::pores", max_pores);
403 Kokkos::View<int, Mem> counter(
"pnm::mpi::pore_count");
404 Kokkos::deep_copy(counter, 0);
405 kn::detectPores(space, geo, sdfE, origin, spacing, pores, counter, max_pores);
410 const float min_sp = std::min(spacing[0], std::min(spacing[1], spacing[2]));
411 const float thr = -1.5f * min_sp;
413 View<Index> labelE(
"pnm::mpi::labelE", nExt);
415 Kokkos::View<int*, Mem> parent(
"pnm::mpi::parent", nInner);
416 Kokkos::View<int, Mem> changed(
"pnm::mpi::changed");
417 kn::initMarkers(space, geo, sdfE, thr, parent);
418 kn::cclFixpoint(space, parent, nInner, changed,
419 [&]() { kn::cclMergeMarkers(space, geo, parent, changed); });
422 View<Index> mg(
"pnm::mpi::mg", nInner);
423 Kokkos::deep_copy(mg,
Index(0x7fffffffffffffffLL));
424 Kokkos::parallel_for(
425 "pnm::mpi::ccl_mingid", inner, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
426 const Index o = g.owned(ix, iy, iz);
428 Kokkos::atomic_min(&mg(parent(o)), g.gid(ix + 1, iy + 1, iz + 1));
431 Kokkos::parallel_for(
432 "pnm::mpi::ccl_scatter", ext, KOKKOS_LAMBDA(
int lx,
int ly,
int lz) {
433 const Index e = g.cell(lx, ly, lz);
434 const int ix = lx - 1, iy = ly - 1, iz = lz - 1;
435 if (g.inOwned(ix, iy, iz)) {
436 const int p = parent(g.owned(ix, iy, iz));
437 labelE(e) = (p == -1) ? Index(-1) : mg(p);
444 haloI.exchange(labelE);
448 const Index maxPairs =
Index(nInner) * 4 + 1024;
449 View<Index> bpairs(
"pnm::mpi::bpairs", std::size_t(2 * maxPairs));
450 Kokkos::View<int, Mem> bcnt(
"pnm::mpi::bcnt");
451 Kokkos::deep_copy(bcnt, 0);
452 Kokkos::parallel_for(
453 "pnm::mpi::boundary_pairs", inner, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
455 if (!g.onSurface(ix, iy, iz))
457 const int lx = ix + 1, ly = iy + 1, lz = iz + 1;
458 const Index my = labelE(g.cell(lx, ly, lz));
461 for (
int dz = -1; dz <= 1; ++dz)
462 for (
int dy = -1; dy <= 1; ++dy)
463 for (
int dx = -1; dx <= 1; ++dx) {
464 if (dx == 0 && dy == 0 && dz == 0)
466 if (g.inOwned(ix + dx, iy + dy, iz + dz))
468 const Index nl = labelE(g.cell(lx + dx, ly + dy, lz + dz));
469 if (nl == -1 || nl == my)
471 const int s = Kokkos::atomic_fetch_add(&bcnt(), 1);
473 bpairs(2 * s) = my < nl ? my : nl;
474 bpairs(2 * s + 1) = my < nl ? nl : my;
480 dm::localPairs(bpairs, std::size_t(std::min<Index>(
readScalar(bcnt), maxPairs)));
481 const dm::MergeRemap merge = dm::globalMerge(loc, comm);
483 space, merge, labelE, nExt, KOKKOS_LAMBDA(std::size_t e) {
return labelE(e) != -1; });
485 haloI.exchange(labelE);
489 View<Index> labelN(
"pnm::mpi::labelN", nExt);
490 Kokkos::View<int, Mem> changed(
"pnm::mpi::fchanged");
491 kn::floodFixpoint(space, geo, sdfE, labelE, labelN, changed, [&](
int c) {
492 const int gc = dm::allreduceMaxInt(c, comm);
494 haloI.exchange(labelE);
507 View<Index> targetE(
"pnm::mpi::targetE", nExt);
509 Kokkos::deep_copy(targetE,
Index(-1));
511 space, geo, sdfE, targetE, KOKKOS_LAMBDA(
Index ci) {
return sdfE(ci) > 0.0f; },
512 KOKKOS_LAMBDA(
Index,
Index cgid) {
return ~cgid; },
513 KOKKOS_LAMBDA(
Index,
Index cgid) {
return ~cgid; });
514 dm::resolveToFixpoint(space, geo, haloI, targetE, comm,
"gradient-root resolution");
518 View<int> segE(
"pnm::mpi::segE", nExt);
521 auto locPores = dm::minByKey(
522 nInner, KOKKOS_LAMBDA(std::size_t o,
Index & k,
Index & v) {
524 g.ownedCoords(
Index(o), ix, iy, iz);
525 const Index e = g.cell(ix + 1, iy + 1, iz + 1);
529 v = g.gid(ix + 1, iy + 1, iz + 1);
535 auto locSolids = dm::minByKey(
536 nInner, KOKKOS_LAMBDA(std::size_t o,
Index & k,
Index & v) {
538 g.ownedCoords(
Index(o), ix, iy, iz);
539 const Index e = g.cell(ix + 1, iy + 1, iz + 1);
540 if (sdfE(e) > 0.0f || labelE(e) == -1)
543 v = g.gid(ix + 1, iy + 1, iz + 1);
546 auto allPores = dm::allgatherv(locPores, comm);
547 auto allSolids = dm::allgatherv(locSolids, comm);
549 auto reduceSort = [](std::vector<std::pair<Index, Index>>& v) {
550 std::map<Index, Index> m;
551 for (
const auto& kv : v) {
552 auto it = m.find(kv.first);
554 m.emplace(kv.first, kv.second);
555 else if (kv.second < it->second)
556 it->second = kv.second;
558 std::vector<std::pair<Index, Index>> order;
559 order.reserve(m.size());
560 for (
const auto& kv : m)
561 order.push_back({kv.second, kv.first});
562 std::sort(order.begin(), order.end());
565 auto poreOrder = reduceSort(allPores);
566 auto solidOrder = reduceSort(allSolids);
568 std::vector<std::pair<Index, int>> pmap(poreOrder.size()), smap(solidOrder.size());
569 for (std::size_t i = 0; i < poreOrder.size(); ++i)
570 pmap[i] = {poreOrder[i].second, int(i) + 1};
571 for (std::size_t i = 0; i < solidOrder.size(); ++i)
572 smap[i] = {solidOrder[i].second, -(int(i) + 1)};
573 std::sort(pmap.begin(), pmap.end());
574 std::sort(smap.begin(), smap.end());
575 std::vector<Index> pk(pmap.size()), sk(smap.size());
576 std::vector<int> pv(pmap.size()), sv(smap.size());
577 for (std::size_t i = 0; i < pmap.size(); ++i) {
578 pk[i] = pmap[i].first;
579 pv[i] = pmap[i].second;
581 for (std::size_t i = 0; i < smap.size(); ++i) {
582 sk[i] = smap[i].first;
583 sv[i] = smap[i].second;
585 View<Index> dpk(
"pnm::mpi::dpk", pk.size()), dsk(
"pnm::mpi::dsk", sk.size());
586 Kokkos::View<int*, Mem> dpv(
"pnm::mpi::dpv", pv.size()), dsv(
"pnm::mpi::dsv", sv.size());
592 Kokkos::deep_copy(segE, 0);
593 Kokkos::parallel_for(
594 "pnm::mpi::seg_assign", inner, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
595 const Index e = g.cell(ix + 1, iy + 1, iz + 1);
597 if (sdfE(e) > 0.0f) {
598 const Index k = kn::bsearchKey(dpk, npk, ~targetE(e));
599 id = (k >= 0) ? dpv(k) : 0;
600 }
else if (labelE(e) != -1) {
601 const Index k = kn::bsearchKey(dsk, nsk, labelE(e));
602 id = (k >= 0) ? dsv(k) : 0;
608 haloS.exchange(segE);
613 Kokkos::View<int*, Mem> pairs(
"pnm::mpi::pairs", std::size_t(2 * maxPairs));
614 Kokkos::View<int, Mem> cnt(
"pnm::mpi::tcnt");
615 Kokkos::deep_copy(cnt, 0);
616 kn::boundaryPairs(space, geo, segE, pairs, cnt, maxPairs);
617 auto loc = kn::uniquePairs(pairs, std::size_t(std::min<Index>(
readScalar(cnt), maxPairs)));
618 auto all = dm::allgatherv(loc, comm);
620 out.connections = std::move(all);
624 out.seg = dm::packInner(space, geo, segE);
640 const std::vector<float>& sdf_local, std::array<int, 3> gdims, std::array<float, 3> origin,
641 std::array<float, 3> spacing,
const std::vector<double>& u_h,
const std::vector<double>& v_h,
642 const std::vector<double>& w_h,
const std::vector<double>& p_h,
const std::vector<double>& ox_h,
643 const std::vector<double>& oy_h,
const std::vector<double>& oz_h, std::array<double, 3> grad_p,
645 namespace dm = detail_mpi;
646 namespace kn = kernels;
647 using peclet::core::View;
648 using peclet::core::halo::GridHalo;
649 using peclet::core::halo::GridHaloTopology;
655 int rank = 0, nranks = 1;
656 MPI_Comm_rank(comm, &rank);
657 MPI_Comm_size(comm, &nranks);
658 peclet::core::decomp::BlockDecomposer<3> dec(
static_cast<std::size_t
>(nranks),
659 IVec<3>{gdims[0], gdims[1], gdims[2]});
661 GridHaloTopology<3> topo;
662 topo.buildTopology(dec, rank, G, {
true,
true,
true}, comm);
663 GridHalo<float> haloF;
665 GridHalo<Index> haloI;
669 GridHalo<double> haloD;
672 const BlockGeo geo = dm::blockGeoOf(topo, gdims, G);
674 const std::size_t nInner = geo.
numOwned();
675 const std::size_t nExt = geo.
numExt();
678 const auto inner = kn::ownedRange(space, geo);
681 View<float> sdfE = dm::stageInner(space, geo, sdf_local,
"nf::mpi::f");
682 haloF.exchange(sdfE);
683 View<int> segE = dm::stageInner(space, geo, base.
seg,
"nf::mpi::s");
684 haloS.exchange(segE);
685 auto stageD = [&](
const std::vector<double>& h,
bool required) {
687 return View<double>(
"nf::mpi::d", required ? nExt : 0);
688 View<double> e = dm::stageInner(space, geo, h,
"nf::mpi::d");
692 View<double> uE = stageD(u_h,
true), vE = stageD(v_h,
true), wE = stageD(w_h,
true);
693 const bool hasOpen = !ox_h.empty();
694 View<double> oxE = stageD(ox_h,
false), oyE = stageD(oy_h,
false), ozE = stageD(oz_h,
false);
701 View<Index> labWork(
"pnm::nfmpi::labWork", nExt);
703 Kokkos::deep_copy(labWork,
Index(-1));
705 space, geo, sdfE, labWork, KOKKOS_LAMBDA(
Index ci) {
return sdfE(ci) <= 0.0f; },
708 dm::resolveToFixpoint(space, geo, haloI, labWork, comm,
"flow-basin resolution");
710 View<int> flowLab(
"pnm::nfmpi::flowLab", nExt);
711 Kokkos::parallel_for(
712 "pnm::nfmpi::basin_label", R1(space, 0, nExt),
713 KOKKOS_LAMBDA(std::size_t e) { flowLab(e) = int(-labWork(e) - 1); });
715 haloS.exchange(flowLab);
721 const int npLoc = kn::maxLabel(space, geo, segE);
722 MPI_Allreduce(&npLoc, &np, 1, MPI_INT, MPI_MAX, comm);
727 GridHaloTopology<3> topo2;
728 topo2.buildTopology(dec, rank, 2, {
true,
true,
true}, comm);
729 GridHalo<double> haloP2;
731 const BlockGeo g2 = dm::blockGeoOf(topo2, gdims, 2);
732 View<double> pE2 = dm::stageInner(space, g2, p_h,
"pnm::nfmpi::pE2");
733 haloP2.exchange(pE2);
735 std::vector<PoreRec> recs;
737 Kokkos::View<PoreRec*, Mem> buf(
"pnm::nfmpi::precs", nInner ? nInner : 1);
738 Kokkos::View<int, Mem> cnt(
"pnm::nfmpi::pcnt");
739 Kokkos::deep_copy(cnt, 0);
740 const float oxo = origin[0], oyo = origin[1], ozo = origin[2];
741 const float sx = spacing[0], sy = spacing[1], sz = spacing[2];
743 Kokkos::parallel_for(
744 "pnm::nfmpi::pores", inner, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
745 const int lx = ix + 1, ly = iy + 1, lz = iz + 1;
746 const Index ci = g.cell(lx, ly, lz);
747 const float cv = sdfE(ci);
750 if (!kn::isPeak(g, sdfE, lx, ly, lz, cv, g.gid(lx, ly, lz)))
754 g.gcoord(lx, ly, lz, rec.
pgx, rec.
pgy, rec.
pgz);
755 rec.
po = kn::poreAt(g, sdfE, lx, ly, lz, oxo, oyo, ozo, sx, sy, sz);
757 const double gp3[3] = {(rec.
po.
x - oxo) / sx, (rec.
po.
y - oyo) / sy,
758 (rec.
po.
z - ozo) / sz};
759 rec.
press = kn::trilinear(gg, pE2, gp3);
760 const int s0 = Kokkos::atomic_fetch_add(&cnt(), 1);
766 auto allRecs = dm::allgatherv(recs, comm);
767 out.
pores.resize(np);
769 std::vector<double> ancX(np), ancY(np), ancZ(np);
770 for (
const auto& r0 : allRecs) {
771 out.
pores[r0.id - 1] = r0.po;
773 ancX[r0.id - 1] = origin[0] + r0.pgx * double(spacing[0]);
774 ancY[r0.id - 1] = origin[1] + r0.pgy * double(spacing[1]);
775 ancZ[r0.id - 1] = origin[2] + r0.pgz * double(spacing[2]);
777 View<double> ancXD(
"pnm::nfmpi::ancX", np), ancYD(
"pnm::nfmpi::ancY", np),
778 ancZD(
"pnm::nfmpi::ancZ", np);
788 const std::size_t nfL = 3 * nInner;
789 Kokkos::View<Index*, Mem> faceLab(
"pnm::nfmpi::faceLab", nfL);
790 kn::ThroatSlots slots;
791 constexpr Index kSent = kn::kSent;
793 Kokkos::View<int*, Mem> parent(
"pnm::nfmpi::fparent", nfL);
794 Kokkos::View<Index*, Mem> fpairL(
"pnm::nfmpi::fpairL", nfL);
795 Kokkos::View<char*, Mem> fcoreL(
"pnm::nfmpi::fcoreL", nfL);
796 Kokkos::View<int, Mem> changed(
"pnm::nfmpi::fch");
797 Kokkos::View<Index*, Mem> mg(
"pnm::nfmpi::fmg", nfL);
798 auto rootOf = KOKKOS_LAMBDA(
int p) {
804 View<Index> flabE[3], fpairE[3];
805 for (
int d = 0; d < 3; ++d) {
806 flabE[d] = View<Index>(
"pnm::nfmpi::flabE", nExt);
807 fpairE[d] = View<Index>(
"pnm::nfmpi::fpairE", nExt);
808 Kokkos::deep_copy(flabE[d],
Index(-1));
809 Kokkos::deep_copy(fpairE[d],
Index(-1));
811 const kn::SplitFaces<View<Index>> nbLab{flabE[0], flabE[1], flabE[2]};
812 const kn::SplitFaces<View<Index>> nbPair{fpairE[0], fpairE[1], fpairE[2]};
815 auto scatterLabels = [&](
bool byParent) {
816 for (
int d = 0; d < 3; ++d) {
819 Kokkos::parallel_for(
820 "pnm::nfmpi::face_scatter", inner, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
821 const Index f = 3 * g.owned(ix, iy, iz) + dd0;
822 const Index lbl = faceLab(f);
823 const bool take = byParent ? (parent(f) >= 0) : (lbl >= 0 && lbl < kSent);
824 fl(g.cell(ix + 1, iy + 1, iz + 1)) = take ? lbl :
Index(-1);
827 haloI.exchange(flabE[d]);
832 const std::int64_t maxbp = std::int64_t(nInner) * 8 + 1024;
833 Kokkos::View<Index*, Mem> bpairs(
"pnm::nfmpi::fbp", std::size_t(2 * maxbp));
834 Kokkos::View<int, Mem> bcnt(
"pnm::nfmpi::fbpcnt");
835 auto boundaryMerge = [&]() {
836 Kokkos::deep_copy(bcnt, 0);
837 Kokkos::parallel_for(
838 "pnm::nfmpi::face_bpairs", inner, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
839 if (!g.onSurface(ix, iy, iz))
841 const Index o = g.owned(ix, iy, iz);
842 for (
int d = 0; d < 3; ++d) {
843 if (parent(3 * o + d) < 0)
845 const Index myl = faceLab(3 * o + d);
846 const Index pk = fpairL(3 * o + d);
847 for (
int dz = -1; dz <= 1; ++dz)
848 for (
int dy = -1; dy <= 1; ++dy)
849 for (
int dx = -1; dx <= 1; ++dx) {
850 if (g.inOwned(ix + dx, iy + dy, iz + dz))
852 const Index e2 = g.cell(ix + 1 + dx, iy + 1 + dy, iz + 1 + dz);
853 for (
int d2 = 0; d2 < 3; ++d2) {
854 const Index ol = nbLab(e2, d2);
855 if (ol < 0 || ol == myl)
857 if (nbPair(e2, d2) != pk)
859 if (!kn::facesAdjacent(dx, dy, dz, d, d2))
861 const int s0 = Kokkos::atomic_fetch_add(&bcnt(), 1);
863 bpairs(2 * s0) = myl < ol ? myl : ol;
864 bpairs(2 * s0 + 1) = myl < ol ? ol : myl;
872 dm::localPairs(bpairs, std::size_t(std::min<std::int64_t>(
readScalar(bcnt), maxbp)));
873 const dm::MergeRemap merge = dm::globalMerge(loc, comm);
875 space, merge, faceLab, nfL, KOKKOS_LAMBDA(std::size_t f) {
return parent(f) >= 0; });
878 auto cclTier = [&]() {
879 kn::cclFixpoint(space, parent, nfL, changed,
880 [&]() { kn::faceMerge(space, geo, parent, fpairL, changed); });
881 Kokkos::deep_copy(mg,
Index(0x7fffffffffffffffLL));
882 kn::faceMinGid(space, geo, parent, mg);
886 kn::faceInit(space, geo, flowLab, sdfE, oxE, oyE, ozE, hasOpen, fcoreL, parent, fpairL);
888 kn::faceLabelCore(space, nfL, parent, fpairL, faceLab, rootOf);
890 for (
int d = 0; d < 3; ++d) {
893 Kokkos::parallel_for(
894 "pnm::nfmpi::pair_scatter", inner, KOKKOS_LAMBDA(
int ix,
int iy,
int iz) {
895 fq(g.cell(ix + 1, iy + 1, iz + 1)) = fpairL(3 * g.owned(ix, iy, iz) + dd0);
898 haloI.exchange(fpairE[d]);
900 scatterLabels(
false);
907 scatterLabels(
false);
908 Kokkos::deep_copy(changed, 0);
909 kn::filmAttachSweep(space, geo, faceLab, fpairL, fcoreL, nbLab, nbPair, changed);
910 if (!dm::allreduceMaxInt(
readScalar(changed), comm))
912 if (++rounds2 > 100000) {
914 throw std::runtime_error(
"[pnm::mpi] film attachment did not converge");
920 kn::leftoverInit(space, nfL, faceLab, parent);
922 kn::faceLabelLeftover(space, nfL, parent, faceLab, rootOf);
929 std::vector<std::pair<Index, Index>> locR;
930 for (std::size_t f = 0; f < nfL; ++f)
932 locR.push_back({flabH[f], fpairH[f]});
933 dm::sortUnique(locR);
934 auto allR = dm::allgatherv(locR, comm);
935 dm::sortUnique(allR);
936 slots = kn::throatSlots(std::move(allR));
940 const std::size_t nt = out.
throats.size();
941 Kokkos::View<Index*, Mem> keyD(
"pnm::nfmpi::keys", nt);
942 Kokkos::View<int*, Mem> slotD(
"pnm::nfmpi::slots", nt);
947 std::vector<double> Qh(nt, 0.0), Ah(nt, 0.0), Cxh(nt, 0.0), Cyh(nt, 0.0), Czh(nt, 0.0),
950 View<double> Q(
"pnm::nfmpi::Q", nt), A(
"pnm::nfmpi::A", nt), Cx(
"pnm::nfmpi::Cx", nt),
951 Cy(
"pnm::nfmpi::Cy", nt), Cz(
"pnm::nfmpi::Cz", nt), resid(
"pnm::nfmpi::res", np);
952 kn::throatFlux(space, geo, flowLab, uE, vE, wE, oxE, oyE, ozE, hasOpen, faceLab, keyD, slotD,
953 nt, ancXD, ancYD, ancZD, origin, spacing, gdims, Q, A, Cx, Cy, Cz, resid);
954 auto red = [&](
const View<double>& d, std::vector<double>& h) {
956 MPI_Allreduce(loc.data(), h.data(),
int(h.size()), MPI_DOUBLE, MPI_SUM, comm);
972 Czh, ancX, ancY, ancZ, spacing, gdims, grad_p);